/*
 *  ZThreads, a platform-independant, multithreading and 
 *  synchroniation library
 *
 *  Copyright (C) 2001, 2002 Eric Crahen, See LGPL.TXT for details
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 *
 *  SUNY @ Buffalo, hereby disclaims all copyright interest in the
 *  ZThreads library written by Eric Crahen
 */


#include "POSIX_ThreadManager.h"
#include "POSIX_ThreadImpl.h"

#ifndef NDEBUG
#include "POSIX_ThreadLocalImpl.h"
#endif

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <assert.h>

#include <errno.h>
#include <stdio.h>
#include <signal.h>

#if !defined(DISABLE_IO_INTERRUPT)
#include <setjmp.h>
#endif

#include "zthread/Guard.h"

using namespace ZThread;

/**
 * Create the ThreadManager, install any support it requires.
 */
ThreadManager::ThreadManager() throw() {

#if !defined(DISABLE_IO_INTERRUPT)
  
  // Install a signal handler to handle SIGALRM for interrupts
  struct sigaction act;

  sigemptyset(&act.sa_mask);

  act.sa_handler = &interruptHandler;
  act.sa_flags = SA_RESTART;

  if(sigaction(SIGALRM, &act, NULL) < 0) 
    fatal("Could not install SIGALRM signal handler!\n");

#endif

#ifndef NDEBUG 
  // Install a cleanup handler for debugging ThreadLocals
  if(atexit(&ThreadLocalImpl::cleanup) != 0)
    fatal("Could not install thread local cleanup handler\n");
#endif

  // Install the cleanup handler for threads
  if(atexit(&cleanup) != 0)
    fatal("Could not install cleanup handler\n");

#ifdef USE_STRESS
  // Seed the random number generator if stress will be used.
  srand(time(0));
#endif

  // Loaded okay
  ZTDEBUG("ZThreads loaded.\n\n");


}

/**
 * Destroy the ThreadManager, remove support installed, join waiting
 * threads.
 *
 */
ThreadManager::~ThreadManager() throw() {

  ZTDEBUG("%d detached thread(s) being cleaned.\n", _detached.size() );

  // Destroy detached threads
  ThreadMap::iterator i = _detached.begin(); 
  for(;i != _detached.end(); ++i) {
    // Join detached and update thier state
    i->second->getMonitor()->transition(State::JOINED);
    i->second->release();
  }

  ZTDEBUG("%d reference thread(s) being cleaned.\n", _reference.size() );

  // Destroy reference threads
  i = _reference.begin(); 
  for(;i != _reference.end(); ++i)
    i->second->release();

  ZTDEBUG("ZThreads unloaded.\n");

}
/**
 * The cleanup() method will have been invoked before the destructor
 */
void ThreadManager::cleanup() {

  ThreadManager* mgr = instance();

  ZTDEBUG("%d detached thread(s) being joined.\n", mgr->_detached.size() );

  // Destroy detached (DEAMON) threads
  ThreadMap::iterator i = mgr->_detached.begin(); 
  for(;i != mgr->_detached.end(); ++i) {
    
    // If a detached thread is still running and it
    // does wantNotify(), send it one
    {

      ThreadImpl* impl = i->second;
      ThreadImpl::MONITOR& m = impl->getMonitor();
      Guard<ThreadImpl::MONITOR> g(m);
    
      // Check the state of the thread. If it is still running,
      // has not been interrupt()ed yet and wants to be 
      // interrupt()ed before the library unloads then 
      // adjust its state.
      if(!m->check(State::INTERRUPTED) && m->check(State::NOTIFY)) 
        if(!m->check(State::IDLE|State::RUNNING)) {
          m->set(State::INTERRUPTED);
          m.notify();
        }
      
      
    }

    int err = 0;
    do {    
      err = pthread_join(i->first, NULL);
    } while(err == EINTR);

    // These errors are acceptable, ESRCH can happen for ThreadImpl objects
    // that have been prepared but never used to run a task. 
    // EINVAL happens if the thread has been joined somehow having transitioned
    // state before the atexit handler was invoked

    assert(err == 0 || err == ESRCH || err == EINVAL);
    if(err == ESRCH || err == EINVAL)
      ZTDEBUG("Warning: error joining detached thread.\n");

  }

  ZTDEBUG("All daemon threads are joined\n");

}



void ThreadManager::fatal(const char* msg) { 

  fprintf(stderr, msg); 
  abort();

}


/**
 * This should be recieved when a thread is interrupt()ed. The POSIX 
 * Thread implementation will tag the thread as interrupted, and then
 * check the Thread interrupt key. If a value has been placed here 
 * then a siglongjmp() is issued.
 *
 * This allows other libraries to be written so that interruptable 
 * operations can be performed. 
 */
void ThreadManager::interruptHandler(int sig) {

#if !defined(DISABLE_IO_INTERRUPT)

  try {

    sigjmp_buf* jmp;
    
    // Check the thread key for a possible siglongjmp
    if( (jmp = (sigjmp_buf*)Thread::interruptKey().get()) != NULL) {
 
      if(ThreadImpl::current()->isInterrupted()) {
        
        ThreadImpl::current()->interrupt();
        Thread::interruptKey().set(NULL);
 
        siglongjmp(*jmp, 1);
        
      }
      
    }
    
    ZTDEBUG("SIGALRM caught\n");
    
  } catch(...) { /* No exceptions from a signal handler */ }

#endif

}


/***
 * A detached thread will be joined by the ThreadManager when the 
 * program exits and the cleanup handler is invoked.
 *
 * Called from ThreadImpl::_dispatch() if the thread has its DAEMON
 * flag set. 
 */
void ThreadManager::detachThread(ThreadImpl* impl) {
  
  ZTDEBUG("Detaching %dth DAEMON thread\n", _detached.size());
  assert(impl != 0);

  // Acquire the detached thread map lock witha Guard.
  Guard<FastMutex> g(_mapLock);

  // Insert the new mapping
  _detached.insert(std::make_pair((pthread_t)*impl, impl) );

}


/**
 * 
 *
 */
void ThreadManager::attachThread(ThreadImpl* impl) {
  
  assert(impl != 0);
  ZTDEBUG("Attaching DAEMON thread\n");

  // Acquire the detached thread map lock witha Guard.
  Guard<FastMutex> g(_mapLock);
    
  ThreadMap::iterator i = _detached.find((pthread_t)*impl);
  if(i != _detached.end() )
    _detached.erase(i);
 
}

/**
 *
 */
void ThreadManager::referenceThread(ThreadImpl* impl) {
  
  assert(impl != 0);

  // Acquire the detached thread map lock witha Guard.
  Guard<FastMutex> g(_mapLock);
  _reference.insert( std::make_pair((pthread_t)*impl, impl) );


}
