/*
 *  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 "WIN32_ThreadManager.h"
#include "WIN32_ThreadImpl.h"
#include "StdAfx.h"

#include <assert.h>
#include "zthread/Guard.h"
#include "WIN32_ThreadLocalImpl.h"
#include <stdio.h>
#include <stdlib.h>

using namespace ZThread;


ThreadManager::ThreadManager() throw() {
  
  if(::atexit(&cleanup) != 0)
    fatal("Could not install cleanup handler\n");

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

  // For a static build try to simply resolve the location of the TECS function
  // from the kernel modules which should always already be loaded
  HINSTANCE hInst = ::GetModuleHandle("kernel32.dll");
  if (hInst == 0) {
    ::MessageBox(0, "Unable to find attached library (kernel32.dll)", "Fatal Error", MB_OK);
    ::ExitProcess(100);
  }

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

}

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");

}


void __cdecl ThreadManager::cleanup() {
  ThreadManager* mgr = instance();

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

  // Destroy detached 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();
        }

    }

    // Wait for the thread
    switch( ::WaitForSingleObjectEx((HANDLE)(i->first), INFINITE, FALSE) ) {
    case WAIT_OBJECT_0:
      break;
    case WAIT_TIMEOUT:
    case WAIT_FAILED:
      throw Synchronization_Exception("Thread::join() : Unexpected exception");
    default:
      break;
    }

  }

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

}


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

  fprintf(stderr, msg); 
  abort();

}


/**
 *
 */
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((HANDLE)*impl, impl) );

}


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

  // Acquire the detached thread map lock witha Guard.
  Guard<FastMutex> g(_mapLock);
    
  ThreadMap::iterator i = _detached.find((HANDLE)*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((HANDLE)*impl, impl) );
  
}

//////////////////////////////////////////////////////////////////////


#ifndef ZTHREAD_STATIC

BOOL APIENTRY DllMain(HANDLE hModule, DWORD  ul_reason_for_call, LPVOID) {

  switch (ul_reason_for_call) {

  case DLL_PROCESS_ATTACH:
  case DLL_THREAD_ATTACH:
  case DLL_THREAD_DETACH:
  case DLL_PROCESS_DETACH:
    break;

  }

  return TRUE;

}

#endif


