// Copyright (C) 1999-2005 Open Source Telecom Corporation.
//  
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
// 
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software 
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
// 
// As a special exception, you may use this file as part of a free software
// library without restriction.  Specifically, if other files instantiate
// templates or use macros or inline functions from this file, or you compile
// this file and link it with other files to produce an executable, this
// file does not by itself cause the resulting executable to be covered by
// the GNU General Public License.  This exception does not however    
// invalidate any other reasons why the executable file might be covered by
// the GNU General Public License.    
//
// This exception applies only to the code released under the name GNU
// Common C++.  If you copy code from other releases into a copy of GNU
// Common C++, as the General Public License permits, the exception does
// not apply to the code that you add in this way.  To avoid misleading
// anyone as to the status of such modified files, you must delete
// this exception notice from them.
//
// If you write modifications of your own for GNU Common C++, it is your choice
// whether to permit this exception to apply to your modifications.
// If you do not wish that, delete this exception notice.
//

#include <cc++/config.h>
#include <cc++/export.h>
#include <cc++/thread.h>
#include <cc++/exception.h>
#ifdef	__BORLANDC__
#include <stdio.h>
#else
#include <cstdio>
#endif
#include "private.h"

#ifdef CCXX_HAVE_NEW_INIT
#include <new>
#else
inline void* operator new(size_t s,void* p)
{ return p;}
#endif

#ifdef WIN32
#include <process.h>
#endif

#ifdef	CCXX_NAMESPACES
namespace ost {
#endif

#ifdef	_THR_UNIXWARE
#undef	_POSIX_THREAD_PRIORITY_SCHEDULING
#define	sigwait(x, y) _thr_sigwait(x, y)
#endif

#ifdef  __linux__
#define CCXX_SIG_THREAD_ALARM
// NOTE: Comment this line to test Resume/Signal using one signal method
#define CCXX_SIG_THREAD_STOPCONT
#endif

#ifndef WIN32
typedef	void	*(*exec_t)(void *);
typedef	RETSIGTYPE (*signalexec_t)(int);
extern "C"
{
#ifndef CCXX_SIG_THREAD_STOPCONT
#ifndef _THR_SUNOS5
#ifndef HAVE_PTHREAD_SUSPEND
static RETSIGTYPE ccxx_sigsuspend(int);
#endif
#endif
#endif

static void ccxx_exec_handler(Thread *th);
static void ccxx_thread_cleanup(void* arg);
static void ccxx_thread_destructor(void* arg);
static void ccxx_sig_handler(int signo);
}

#endif // ndef WIN32

#ifdef	CCXX_SIG_THREAD_CANCEL

extern "C" RETSIGTYPE _th_sigcancel(int sig)
{
	pthread_exit(NULL);
}

#endif

#ifdef WIN32
typedef	unsigned (__stdcall *exec_t)(void *);
#ifndef CCXX_NO_DLL
# if defined(_MSC_VER) && !defined(_DLL)
#  error This project cannot be compiled as a static library. Some implementation stuff require DLL
# endif
#endif // CCXX_NO_DLL
#endif // WIN32

/*
 * Start Suspend/Resume stuff
 */

// method to suspend are
// - system suspend/resume recursive
// - system suspend/resume not recursive
// - one signal only, not recursive
#ifndef WIN32
#define CCXX_SUSPEND_MODE_RECURSIVE 1
#define CCXX_SUSPEND_MODE_NOT_RECURSIVE 2
#define CCXX_SUSPEND_MODE_ONE_SIGNAL 3
#define	CCXX_SUSPEND_MODE_MACH 4

#if defined(_THR_MACH) && !defined(MACOSX)
#define	CCXX_SUSPEND_MODE CCXX_SUSPEND_MODE_MACH
#elif defined(HAVE_PTHREAD_SUSPEND)
#define CCXX_SUSPEND_MODE CCXX_SUSPEND_MODE_NOT_RECURSIVE
static inline void ccxx_resume(cctid_t tid) { pthread_continue(tid); }
static inline void ccxx_suspend(cctid_t tid) { pthread_suspend(tid); }
#else
# if defined(_THR_SUNOS5) || defined(CCXX_SIG_THREAD_STOPCONT)
#  define CCXX_SUSPEND_MODE CCXX_SUSPEND_MODE_NOT_RECURSIVE
#  ifdef _THR_SUNOS5
    static inline void ccxx_resume(cctid_t tid) { thr_continue((thread_t)tid); }
    static inline void ccxx_suspend(cctid_t tid) { thr_suspend((thread_t)tid); }
#  else
#   define CCXX_SIG_THREAD_SUSPEND SIGSTOP
#   define CCXX_SIG_THREAD_RESUME  SIGCONT
    static inline void ccxx_resume(cctid_t tid) { 
	    pthread_kill(tid, CCXX_SIG_THREAD_RESUME); 
    }
    static inline void ccxx_suspend(cctid_t tid) { 
	    pthread_kill(tid, CCXX_SIG_THREAD_SUSPEND); 
    }
#  endif
# else
#  define CCXX_SUSPEND_MODE CCXX_SUSPEND_MODE_ONE_SIGNAL
#  ifndef SIGUSR3
#  ifdef  SIGWINCH
#  define SIGUSR3 SIGWINCH
#  else
#  define SIGUSR3 SIGINT
#  endif
#  endif
#  define CCXX_SIG_THREAD_SUSPEND SIGUSR3
#  define CCXX_SIG_THREAD_RESUME SIGUSR3
   static inline void ccxx_resume(cctid_t tid) { 
	   pthread_kill(tid, CCXX_SIG_THREAD_RESUME); 
   }
   static inline void ccxx_suspend(cctid_t tid) { 
	   pthread_kill(tid, CCXX_SIG_THREAD_SUSPEND); 
   }
# endif
#endif
#endif // ndef WIN32

Thread::Cancel Thread::enterCancel(void)
{
	Thread *th = getThread();

	if(!th)
		return cancelInitial;

	Cancel old = th->_cancel;	
	if(old != cancelDisabled && old != cancelImmediate)
	{
		th->setCancel(cancelImmediate);
#ifdef	WIN32
		Thread::yield();
#else
		pthread_testcancel();
#endif
	}

	return old;
}

void Thread::exitCancel(Cancel old)
{
	Thread *th = getThread();

	if(!th)
		return;

	if(old != th->_cancel)
	{
#ifndef	WIN32
		pthread_testcancel();
#endif
		th->setCancel(old);
	}
}

void Thread::suspend(void)
{
	if(!priv)
		return;

#ifdef WIN32
	if (!priv->_active || !priv->_suspendEnable)
	{
#ifdef	CCXX_EXCEPTIONS
		if (Thread::getException() != throwNothing)
			throw this;
#endif
		return;
	}
	SuspendThread(priv->_hThread);

#else

	if (!priv->_suspendEnable) return;
#if CCXX_SUSPEND_MODE == CCXX_SUSPEND_MODE_MACH
	thread_suspend(priv->_mach);
#endif
#if CCXX_SUSPEND_MODE == CCXX_SUSPEND_MODE_RECURSIVE
	ccxx_suspend(priv->_tid);
#endif
#if (CCXX_SUSPEND_MODE == CCXX_SUSPEND_MODE_NOT_RECURSIVE) \
  || (CCXX_SUSPEND_MODE == CCXX_SUSPEND_MODE_ONE_SIGNAL)
	if (++priv->_suspendcount != 1) return;
	ccxx_suspend(priv->_tid);
#endif

#endif // WIN32
}

#if defined(__FreeBSD__)
#define	AUTOSTACK	0x10000
#endif

#if defined(MACOSX)
#define	AUTOSTACK	0
#endif

#ifndef	AUTOSTACK
#define	AUTOSTACK	0x100000
#endif

size_t Thread::_autostack = AUTOSTACK;

void Thread::resume(void)
{
	if(!priv)
		return;

#ifdef WIN32
	if (!priv->_active || !priv->_suspendEnable)
	{
#ifdef	CCXX_EXCEPTIONS
		if (Thread::getException() != throwNothing)
			throw this;
#endif
		return;
	}
	ResumeThread(priv->_hThread);

#else
	if (!priv->_suspendEnable) return;
#if CCXX_SUSPEND_MODE == CCXX_SUSPEND_MODE_MACH
	thread_resume(priv->_mach);
#endif
#if CCXX_SUSPEND_MODE == CCXX_SUSPEND_MODE_RECURSIVE
	ccxx_resume(priv->_tid);
#endif
#if (CCXX_SUSPEND_MODE == CCXX_SUSPEND_MODE_NOT_RECURSIVE) \
  || (CCXX_SUSPEND_MODE == CCXX_SUSPEND_MODE_ONE_SIGNAL)
	int c;
	if ( (c = --priv->_suspendcount) > 0) return;
	if ( c < 0 ) 
	{
		++priv->_suspendcount;
		return;
	}
	ccxx_resume(priv->_tid);
#endif

#endif // WIN32
}

void Thread::join(void)
{
	bool detached = isDetached();
	joinSem.wait();
	if(detached)
	{
		joinSem.post();
		return;
	}

#ifdef	WIN32		// wait for real w32 thread to cleanup

	if(priv->_hThread)
	{
		WaitForSingleObject(priv->_hThread, INFINITE);
		::CloseHandle(priv->_hThread);
		priv->_hThread = NULL;
	}		

#else			// make sure we cleanup exiting thread
	if(priv->_jtid)
	{
		pthread_join(priv->_jtid, NULL);
	}

	priv->_jtid = 0;
#endif
	joinSem.post();	// enable next waiting thread after cleanup
}

#ifndef WIN32
#if CCXX_SUSPEND_MODE == CCXX_SUSPEND_MODE_ONE_SIGNAL
// NOTE: Do not modify _suspendcount here, one program can call
// Suspend 2 or more time but this function can be called only once
inline RETSIGTYPE ThreadImpl::ThreadSigSuspend(int)
{
	sigset_t sigs;

	sigemptyset(&sigs);
	sigaddset(&sigs, SIGUSR3);
	while ( (getThread()->priv->_suspendcount) > 0)
	{
#ifdef	HAVE_SIGWAIT2
		int signo;
		sigwait(&sigs, &signo);
#else
		sigwait(&sigs);
#endif
	}
}

static RETSIGTYPE ccxx_sigsuspend(int signo)
{
	return ThreadImpl::ThreadSigSuspend(signo);
}
#endif

void	Thread::setSuspend(Suspend mode)
{
	if(!priv)
		return;

	priv->_suspendEnable = (mode == suspendEnable);
#ifndef	HAVE_PTHREAD_SUSPEND
#ifdef  CCXX_SIG_THREAD_SUSPEND
	sigset_t mask;

	sigemptyset(&mask);
	sigaddset(&mask, CCXX_SIG_THREAD_SUSPEND);

	switch(mode)
	{
	case suspendEnable:
		pthread_sigmask(SIG_UNBLOCK, &mask, NULL);
		return;
	case suspendDisable:
		pthread_sigmask(SIG_BLOCK, &mask, NULL);
	}
#endif
#endif
}

/*
 * End Suspend/Resume stuff
 */

static	sigset_t *blocked_signals(sigset_t *sig)
{
	sigemptyset(sig);
	sigaddset(sig, SIGINT);
	sigaddset(sig, SIGKILL);
	sigaddset(sig, SIGHUP);
	sigaddset(sig, SIGABRT);
	sigaddset(sig, SIGALRM);
	sigaddset(sig, SIGPIPE);
#if CCXX_SUSPEND_MODE == CCXX_SUSPEND_MODE_ONE_SIGNAL
	sigaddset(sig, SIGUSR3);
#endif
	return sig;
}
#endif // ndef WIN32

typedef enum ThreadType { 
	threadTypeNormal=0, 
	threadTypeMain, 
	threadTypePosix,
	threadTypeDummy
} ThreadType;

class MainThread : public Thread
{
protected:
	void run(void) {return;};
#ifndef WIN32
	void onSignal(int signo) { std::exit(signo);};
#endif

public:
	MainThread() : Thread(true) {};
};

// mantain info on thread creation
class DummyThread : public Thread
{
protected:
	void run() {};
public:
	DummyThread() : Thread(false) { priv->_type = threadTypeDummy; }
#ifdef WIN32
	static void CheckDelete();
#endif
};

#ifdef WIN32
static ThreadKey _self;
#else
// NOTE: _self instantiation MUST appear before _mainthread !!
ThreadKey ThreadImpl::_self(ccxx_thread_destructor);
#endif

#ifdef WIN32
void DummyThread::CheckDelete()
{
	Thread *th = (Thread*)_self.getKey();
	if (!th) return;

	// delete if dummy thread
	if (th->priv->_type == threadTypeDummy)
		delete th;
}
#endif

static	MainThread _mainthread;
Thread	*Thread::_main = NULL;

// invalid pointer to thread used to test deleted thread
// point in the middle of mainthread...
#define DUMMY_INVALID_THREAD ((Thread*)(((char*)((Thread*)&_mainthread))+1))

#if !defined(WIN32)
#ifndef	CCXX_SIG_THREAD_ALARM
PosixThread *PosixThread::_timer = NULL;
Mutex	PosixThread::_arm;
#endif
#endif

//void PosixThread::sigInstall(int);

Thread::Thread(bool isMain):
_cancel(cancelDefault), _start(NULL), priv(new ThreadImpl(threadTypeDummy))
{
#ifdef WIN32
	priv->_tid = GetCurrentThreadId();

	// FIXME: error handling
	HANDLE process = GetCurrentProcess();
	DuplicateHandle(process,GetCurrentThread(),process,&priv->_hThread,0,FALSE,DUPLICATE_SAME_ACCESS);
	_parent = this;
	priv->_cancellation = CreateEvent(NULL, TRUE, FALSE, NULL);
	
	if(isMain)
	{
		setName("main()");
		priv->_type   = threadTypeMain;
		_main = this;
	}
	else
		setName("-dummy-");
	_self.setKey(this);

#else
	priv->_suspendEnable = false;
	priv->_tid = pthread_self();
	_parent = NULL;
	struct sigaction act;
	
	// NOTE: for race condition (signal handler can use getThread)
	// you should initialize _main and _self before registering signals
	ThreadImpl::_self.setKey(this);
	if(isMain == true)
	{
		_main = this;
		priv->_type = threadTypeMain;
#if !defined(__CYGWIN32__) && !defined(__MINGW32__)
		PosixThread::sigInstall(SIGHUP);
		PosixThread::sigInstall(SIGALRM);
		PosixThread::sigInstall(SIGPIPE);
		PosixThread::sigInstall(SIGABRT);

		memset(&act, sizeof(act), 0);
		act.sa_handler = (signalexec_t)&ccxx_sig_handler;
		sigemptyset(&act.sa_mask);
# ifdef	SA_RESTART
		act.sa_flags = SA_RESTART;
# else
		act.sa_flags = 0;
# endif
# ifdef	SA_INTERRUPT
		act.sa_flags |= SA_INTERRUPT;
# endif
# ifdef	SIGPOLL
		sigaction(SIGPOLL, &act, NULL);
# else
		sigaction(SIGIO, &act, NULL);
# endif		

# if CCXX_SUSPEND_MODE == CCXX_SUSPEND_MODE_ONE_SIGNAL
		act.sa_handler = ccxx_sigsuspend;
		sigemptyset(&act.sa_mask);
#  ifdef	SA_RESTART
		act.sa_flags = SA_RESTART;
#  else
		act.sa_flags = 0;
#  endif
		sigaction(SIGUSR3, &act, NULL);
# endif

# ifdef	CCXX_SIG_THREAD_CANCEL
		memset(&act, sizeof(act), 0);
		act.sa_flags = 0;
		act.sa_handler = _th_sigcancel;
		sigemptyset(&act.sa_mask);
		sigaddset(&act.sa_mask, SIGHUP);
		sigaddset(&act.sa_mask, SIGALRM);
		sigaddset(&act.sa_mask, SIGPIPE);

		sigaction(CCXX_SIG_THREAD_CANCEL, &act, NULL);
# endif
#endif
    	}

#endif // WIN32
}	

Thread::Thread(int pri, size_t stack):
_cancel(cancelDefault), _start(NULL), priv(new ThreadImpl(threadTypeNormal))
{
#ifdef WIN32
	if(!_main)
	{
		_self.setKey(NULL);
		_main = this;
		setName("main()");
	}
	else
#ifdef	WIN32
		_name[0] = 0;
#else
		snprintf(_name, sizeof(_name), "%d", getId());
#endif

	_parent = Thread::get();
	if(_parent)
		priv->_throw = _parent->priv->_throw;
	else
		_parent = this;
	
	priv->_cancellation = CreateEvent(NULL, TRUE, FALSE, NULL);
	if(!priv->_cancellation)
		THROW(this);

	if(stack <= _autostack)
		priv->_stack = 0;
	else
		priv->_stack = stack;

	if(pri > 2)
		pri = 2;
	if(pri < -2)
		pri = -2;

	switch(pri)
	{
	case 1:
		priv->_priority = THREAD_PRIORITY_ABOVE_NORMAL;
		break;
	case -1:
		priv->_priority = THREAD_PRIORITY_BELOW_NORMAL;
		break;
	case 2:
		priv->_priority = THREAD_PRIORITY_HIGHEST;
		break;
	case -2:
		priv->_priority = THREAD_PRIORITY_LOWEST;
		break;
	default:
		priv->_priority = THREAD_PRIORITY_NORMAL;
	}

#else
	int	salign;

	pthread_attr_init(&priv->_attr);
	pthread_attr_setdetachstate(&priv->_attr, PTHREAD_CREATE_JOINABLE);

#ifdef	PTHREAD_STACK_MIN
	if(stack && stack <=  _autostack)
		pthread_attr_setstacksize(&priv->_attr, _autostack);
	else if(stack > _autostack)
	{
		if(stack < PTHREAD_STACK_MIN)
			stack = PTHREAD_STACK_MIN;
		else	// align to nearest min boundry
		{	
			salign = stack / PTHREAD_STACK_MIN;
			if(stack % PTHREAD_STACK_MIN)
				++salign;
			stack = salign * PTHREAD_STACK_MIN;
		}
		if(stack && pthread_attr_setstacksize(&priv->_attr, stack))
		{
#ifdef	CCXX_EXCEPTIONS
			switch(Thread::getException())
			{
			case throwObject:
				throw(this);
				return;
#ifdef	COMMON_STD_EXCEPTION
			case throwException:
				throw(ThrException("no stack space"));
				return;
#endif
			default:
				return;
			}
#else
			return;
#endif
		}
	}
#endif

#ifndef	__FreeBSD__
#ifdef	_POSIX_THREAD_PRIORITY_SCHEDULING
#ifdef HAVE_SCHED_GETSCHEDULER
#define	__HAS_PRIORITY_SCHEDULING__	
	if(pri)
	{
		struct sched_param sched;
		int policy;

		policy = sched_getscheduler(0);
		if(policy < 0)
		{
#ifdef	CCXX_EXCEPTIONS
			switch(Thread::getException())
			{
			case throwObject:
				throw(this);
				return;
#ifdef	COMMON_STD_EXCEPTION
			case throwException:
				throw(ThrException("invalid scheduler"));
				return;
#endif
			default:
				return;
			}
#else
			return;
#endif
		}

		sched_getparam(0, &sched);

		pri = sched.sched_priority - pri;
		if(pri 	> sched_get_priority_max(policy))
			pri = sched_get_priority_max(policy);

		if(pri < sched_get_priority_min(policy))
			pri = sched_get_priority_min(policy);

		sched.sched_priority = pri;
		pthread_attr_setschedpolicy(&priv->_attr, policy);
		pthread_attr_setschedparam(&priv->_attr, &sched);
	}	
#endif // ifdef HAVE_SCHED_GETSCHEDULER
#endif // ifdef _POSIX_THREAD_PRIORITY_SCHEDULING
#endif // ifndef __FreeBSD__

#ifdef	__HAS_PRIORITY_SCHEDULING__
	if(!pri)
	        pthread_attr_setinheritsched(&priv->_attr, PTHREAD_INHERIT_SCHED);             
#else
        pthread_attr_setinheritsched(&priv->_attr, PTHREAD_INHERIT_SCHED);             
#endif

	_parent = getThread();
	priv->_throw = _parent->priv->_throw;

	_cancel = cancelInitial;

#endif // WIN32
}

#ifndef WIN32
Thread::Thread(const Thread &th)
{
	priv = new ThreadImpl(threadTypeNormal);
	_parent = th._parent;
	priv->_attr = th.priv->_attr;
	_cancel = cancelInitial;
	_start = NULL;
	priv->_throw = th.priv->_throw;
	priv->_suspendEnable = false;

	setName(NULL);
	
//	sigset_t mask, newmask;
//	int rc;
//
//	pthread_sigmask(SIG_BLOCK, blocked_signals(&newmask), &mask);
//	rc = pthread_create(&_tid, &_attr, exec_t(&ccxx_exec_handler), this);
//	pthread_sigmask(SIG_SETMASK, &mask, NULL);
//	if(rc && Thread::getException() == throwObject)
//		throw(this);
//#ifdef	COMMON_STD_EXCEPTION
//	else if(rc && Thread::getException() == throwException)
//		throw(ThrException("cannot start copy"));
//#endif
}
#endif // ndef WIN32

Thread::~Thread()
{
	if(!priv)
		return;

#ifndef	WIN32
	if(this == &_mainthread)
		return;
#endif

	if(priv->_type == threadTypeDummy)
	{
		delete priv;
		priv = NULL;
		return;
	}

	terminate();
}

void Thread::setName(const char *text)
{
	if(text)
		snprintf(_name, sizeof(_name), "%s", text);
	else
		snprintf(_name, sizeof(_name), "%ld", (long)getId());
}	

void Thread::initial(void)
{
}

void Thread::final(void)
{
}

void *Thread::getExtended(void)
{
	return NULL;
}

void Thread::notify(Thread *)
{
}

bool Thread::isThread(void)
{
	if(!priv)
		return false;

#ifdef WIN32
	return ((priv->_tid == GetCurrentThreadId())) ? true : false;
#else
	return (priv->_tid == pthread_self()) ? true : false;
#endif
}

bool Thread::isDetached(void)
{
        if(!priv)
                return false;

#ifdef  WIN32
	// win32 doesn't support detached threads directly
	return priv->_detached;
#else
        int state;

        pthread_attr_getdetachstate(&priv->_attr, &state);
        if(state == PTHREAD_CREATE_DETACHED)
                return true;
        return false;
#endif
}

cctid_t Thread::getId(void) const
{
	return priv->_tid;
}

bool Thread::isRunning(void)
{
	if(!priv)
		return false;
#ifdef WIN32
	return (priv->_tid != 0 && priv->_active) ? true : false;
#else
	return (priv->_tid != 0) ? true : false;
#endif // WIN32
}

int Thread::start(Semaphore *st)
{
	if(!priv)
		return -1;

#ifdef WIN32
	if(priv->_active)
		return -1;

	_start = st;

	priv->_hThread = (HANDLE)_beginthreadex(NULL, (unsigned)priv->_stack, (exec_t)&Execute, (void *)this, CREATE_SUSPENDED, (unsigned *)&priv->_tid);
	if(!priv->_hThread)
 	{
		CloseHandle(priv->_cancellation);
		priv->_cancellation = NULL;
		return -1;
 	}

	setCancel(cancelInitial);

	SetThreadPriority(priv->_hThread, priv->_priority);

	ResumeThread(priv->_hThread);

	priv->_active = true;

	return 0;

#else
	if(priv->_tid)
	{
		if(_start)
		{
			_start->post();
			return 0;
		}
		else
			return -1;
	}

	_start = st;
	return pthread_create(&priv->_tid, &priv->_attr, exec_t(&ccxx_exec_handler), this);
#endif
}

int Thread::detach(Semaphore *st)
{
	_parent = NULL;
#ifdef WIN32
	// win32 we emulate detach	
	if(!priv)
		return -1;
	priv->_detached = true;
	if(!priv->_active)
		return Thread::start(st);
	else if(_start)
		_start->post();
	return 0;
#else
	int rtn;
	if(!priv)
		return -1;

	if(priv->_tid)
	{
		pthread_detach(priv->_tid);
		if(_start)
		{	
			_start->post();
		        pthread_attr_setdetachstate(&priv->_attr, PTHREAD_CREATE_DETACHED);
			return 0;
		}
		return -1;
	}

	pthread_attr_setdetachstate(&priv->_attr, PTHREAD_CREATE_DETACHED);

	_start = st;
	rtn = pthread_create(&priv->_tid, &priv->_attr, exec_t(&ccxx_exec_handler), this);
	if(!rtn && priv->_tid)
		return 0;
	return -1;
#endif
}

void Thread::terminate(void)
{
#ifdef WIN32
	HANDLE hThread;

	if(!priv)
		return;

	hThread = priv->_hThread;

	if (!priv->_tid || isThread())
	{
		if( priv->_cancellation)
			::CloseHandle(priv->_cancellation);
      		if(hThread)
            		::CloseHandle(hThread);
		delete priv;
		priv = NULL;
      		return;
	}

	bool terminated = false;
	if(!priv->_active && hThread != NULL)
	{
		// NOTE: add a test in testthread for multiple
		// suspended Terminate
		ResumeThread(hThread);
		TerminateThread(hThread, 0);
		terminated = true;
	}
	else if(hThread != NULL)
	{
		switch(_cancel)	{
		case cancelImmediate:
			TerminateThread(hThread, 0);
			terminated = true;
			break;
		default:
			SetEvent(priv->_cancellation);
		}
	}
	if(hThread != NULL)
	{
		WaitForSingleObject(hThread, INFINITE);
		CloseHandle(hThread);
		hThread = NULL;
	}
	if(_parent)
		_parent->notify(this);
	if(priv->_cancellation != NULL)
		CloseHandle(priv->_cancellation);
	priv->_cancellation = NULL;
	priv->_tid = 0;
	if(getThread() == this)
		_self.setKey(DUMMY_INVALID_THREAD);

	if (terminated)
		final();
#else
	if(!priv)
		return;

        cctid_t jtid = priv->_jtid, tid = priv->_tid;

	if(jtid && (pthread_self() != jtid))
	{
		pthread_join(jtid, NULL);
		priv->_jtid = 0;
	}
	else if((pthread_self() != tid) && tid)
	{
		// in suspend thread cannot be cancelled or signaled
		// ??? rigth
		// ccxx_resume(priv->_tid);
		

		// assure thread has ran before we try to cancel...
		if(_start)
			_start->post();

		pthread_cancel(tid);
		if(!isDetached())
		{
			pthread_join(tid,NULL);
			priv->_tid = 0;
		}
	}

        pthread_attr_destroy(&priv->_attr);
#endif
	delete priv;
	priv = NULL;
}

void Thread::sync(void)
{
#if defined(__MACH__) || defined(__GNU__)
	Thread::exit();
#else
	Thread::sleep(TIMEOUT_INF);
#endif
}

void Thread::exit(void)
{
	if (isThread())
	{
		setCancel(cancelDisabled);
#ifdef WIN32
		close();
		ExitThread(0);
#else
		pthread_exit(NULL);
#endif // WIN32
	}
	
}
		
void Thread::close()
{
	bool detached = isDetached();

#if !defined(CCXX_SIG_THREAD_ALARM) && !defined(__CYGWIN32__) && !defined(__MINGW32__) && !defined(WIN32)
	if(this == PosixThread::_timer)
		PosixThread::_arm.leaveMutex();
#endif
	setCancel(cancelDisabled);
	if(_parent)
		_parent->notify(this);
      
	// final can call destructor (that call Terminate)
	final();

	// test if this class is self-exiting thread

#ifdef WIN32
	if (_self.getKey() == this)
#else
	if (ThreadImpl::_self.getKey() == this)
#endif
	{
		if(priv)
		{
#ifndef	WIN32
			priv->_jtid = priv->_tid;
#else			
			priv->_active = false;
#endif
		}
		joinSem.post();
	}

	// see if detached, and hence self deleting

	if(detached)
		delete this;
}

#ifndef WIN32
inline void ThreadImpl::ThreadCleanup(Thread* th)
{
	// close thread
	// (freddy77) Originally I thougth to throw an exception for deferred
	// for capture it and cleanup using C++ destructor
	// this doesn't work out!! 
	// Throwing exception here (in cleanup) core dump app
	th->close();
}
static void ccxx_thread_cleanup(void* arg)
{
	ThreadImpl::ThreadCleanup( (Thread*)arg );
}

inline void ThreadImpl::ThreadExecHandler(Thread *th)
{
	ThreadImpl::_self.setKey(th);
	sigset_t mask;

	pthread_sigmask(SIG_BLOCK, blocked_signals(&mask), NULL);
	th->priv->_tid = pthread_self();
#if defined(HAVE_PTHREAD_MACH_THREAD_NP)
	th->priv->_mach = pthread_mach_thread_np(th->priv->_tid);
#elif defined(_THR_MACH)
	th->priv->_mach = mach_thread_self();
#endif
	th->setCancel(Thread::cancelInitial);
	// using SIGUSR3 do not enable suspend by default
	th->setSuspend(Thread::suspendEnable);
	th->yield();
	if(th->_start)
	{
		th->_start->wait();
		th->_start = NULL;
	}

	pthread_cleanup_push(ccxx_thread_cleanup,th);
	th->initial();
	if(th->getCancel() == Thread::cancelInitial)
		th->setCancel(Thread::cancelDefault);
	th->run();
	th->setCancel(Thread::cancelDisabled);

	pthread_cleanup_pop(0);
	if(th->isDetached())
		ThreadImpl::_self.setKey(NULL);
	th->close();
	pthread_exit(NULL);
}

// delete Thread class created for no CommonC++ thread
inline void ThreadImpl::ThreadDestructor(Thread* th)
{
	if (!th || th == DUMMY_INVALID_THREAD || !th->priv)
		return;
	if(!th->priv)
		return;
	if (th->priv->_type == threadTypeDummy)
		delete th;
}

static void ccxx_thread_destructor(void* arg)
{
	ThreadImpl::ThreadDestructor( (Thread*)arg );
}

static void ccxx_exec_handler(Thread *th)
{
	ThreadImpl::ThreadExecHandler(th);
}
#endif // ndef WIN32

#ifdef	CCXX_SIG_THREAD_CANCEL

void	Thread::setCancel(Cancel mode)
{
	sigset_t	mask;

	sigemptyset(&mask);
	sigaddset(&mask, CCXX_SIG_THREAD_CANCEL);
	
	switch(mode)
	{
	case cancelImmediate:
		pthread_sigmask(SIG_UNBLOCK, &mask, NULL);
		break;
	case cancelInitial:
	case cancelDisabled:
	case cancelDeferred:
		pthread_sigmask(SIG_BLOCK, &mask, NULL);
		break;
	}
	_cancel = mode;
}
#else

void	Thread::setCancel(Cancel mode)
{
#ifdef WIN32
	switch(mode)	
	{
	case cancelDeferred:
	case cancelImmediate:
		_cancel = mode;
		yield();
		break;
	case cancelDisabled:
	case cancelInitial:
		_cancel = mode;
	}

#else
	int old;

	switch(mode)
	{
	case cancelImmediate:
		pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &old);
		pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &old);
		break;
	case cancelDeferred:
		pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &old);
		pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &old);
		break;
	case cancelInitial:
	case cancelDisabled:
		pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old);
		break;
	default:
		return;
	}
	_cancel = mode;
#endif // WIN32
}

#endif

void	Thread::yield(void)
{
#ifdef WIN32
	Thread::sleep(1);   // note: on Win32, Sleep(0) is "optimized" to NOP.
#else

#ifdef	CCXX_SIG_THREAD_CANCEL
	Thread* th = getThread();
	sigset_t cancel, old;

	sigemptyset(&cancel);
	sigaddset(&cancel, CCXX_SIG_THREAD_CANCEL);

	if(th && th->_cancel != cancelDisabled && 
	   th->_cancel != cancelInitial)
		pthread_sigmask(SIG_UNBLOCK, &cancel, &old);
#else
	pthread_testcancel();
#endif
#ifdef	HAVE_PTHREAD_YIELD
	pthread_yield();
#endif

#ifdef	CCXX_SIG_THREAD_CANCEL
	if(th && th->_cancel != cancelDisabled && th->_cancel != cancelInitial)
		pthread_sigmask(SIG_SETMASK, &old, NULL);
#endif

#endif // WIN32
}

void Thread::setException(Thread::Throw mode)
{
	Thread *thread = getThread();
	thread->priv->_throw = mode;
}

Thread::Throw Thread::getException(void)
{
	Thread *thread = getThread();
	return thread->priv->_throw;
}

Cancellation::Cancellation(Thread::Cancel cancel)
{
	Thread *thread = getThread();
	if(!thread)
		return;

	prior = thread->getCancel();
	thread->setCancel(cancel);
}

Cancellation::~Cancellation()
{
	Thread *thread = getThread();
	if(!thread)
		return;

	thread->setCancel(prior);
}

bool Thread::testCancel(void)
{
#ifdef WIN32
	switch(_cancel)
	{
	case cancelInitial:
	case cancelDisabled:
		break;
	default:
		if(WaitForSingleObject(priv->_cancellation, 0) == WAIT_OBJECT_0)
		{
			if (_cancel == cancelManual)
				THROW(InterruptException());
			else
				exit();
		}
	}
	return false;

#else // WIN32

#ifdef  CCXX_SIG_THREAD_CANCEL
        sigset_t cancel, old;
 
        sigemptyset(&cancel);
        sigaddset(&cancel, CCXX_SIG_THREAD_CANCEL);
 
        if(_cancel != cancelDisabled &&
           _cancel != cancelInitial)
                pthread_sigmask(SIG_UNBLOCK, &cancel, &old);
#else
        pthread_testcancel();
#endif
 
#ifdef  CCXX_SIG_THREAD_CANCEL
        if(_cancel != cancelDisabled)
                pthread_sigmask(SIG_SETMASK, &old, NULL);
#endif
	return false;

#endif // WIN32
} 

#ifdef WIN32

bool Thread::isCancelled()
{
	return waitThread(priv->_cancellation, 0) == WAIT_OBJECT_0;
}

DWORD Thread::waitThread(HANDLE hRef, timeout_t timeout)
{
	Thread *th = getThread();

	if(th)
		return th->waitHandle(hRef, timeout);
	else
		return WaitForSingleObject(hRef, timeout);
}

void Thread::sleep(timeout_t timeout) 
{
	Thread *th = getThread();
	if(!th)
	{
		SleepEx(timeout, FALSE);
		return;
	}

	switch(th->_cancel)
	{
	case cancelInitial:
	case cancelDisabled:
		SleepEx(timeout, FALSE);
		break;
	default:
		if(WaitForSingleObject(th->priv->_cancellation, timeout) == WAIT_OBJECT_0)
		{
			if (th->_cancel == cancelManual)
				THROW(InterruptException());
			else
				th->exit();
		}
	}
}

DWORD Thread::waitHandle(HANDLE obj, timeout_t timeout) 
{
	HANDLE objects[2];
	DWORD stat;

	objects[0] = priv->_cancellation;
	objects[1] = obj;

	// FIXME: what should happen if someone enable cancellation on wait??
	switch(_cancel)
	{
	case cancelInitial:
	case cancelDisabled:
		return WaitForSingleObject(obj, timeout);
	default:
		switch(stat = WaitForMultipleObjects(2, objects, false, timeout)) 	{
		case WAIT_OBJECT_0:
			if (_cancel == cancelManual)
				THROW(InterruptException());
			else
				exit();
		case WAIT_OBJECT_0 + 1:
			return WAIT_OBJECT_0;
		default:
			return stat;
		}
	}
}

// Entry point linked for default disable thread call, not suitable
// for threading library...
BOOL WINAPI DllMain(
        HANDLE  hDllHandle,
        DWORD   dwReason,
        LPVOID  lpreserved
        )
{
	switch(dwReason)
	{
	case DLL_THREAD_DETACH:
		DummyThread::CheckDelete();
		break;
	}
        return TRUE ;
}

#endif // WIN32

#ifndef WIN32
Thread *Thread::get(void)
{
	Thread *thread;
	
	// fix strange no-init on Solaris
        if(!Thread::_main)
	{
		new (&_mainthread) MainThread();
		return &_mainthread;
	}

	thread = (Thread *)ThreadImpl::_self.getKey();

	// class have been deleted, return NULL
	if (thread == DUMMY_INVALID_THREAD)
		return NULL;

	if(!thread)
	{
		// this Thread will be deleted by ccxx_thread_destruct
		thread = new DummyThread;
		ThreadImpl::_self.setKey(thread);
	}
	return thread;
}

#else // WIN32

Thread *Thread::get(void)
{
	Thread *th = (Thread *)_self.getKey();
	if (th == DUMMY_INVALID_THREAD) return NULL;
	// for no common c++ thread construct a dummy thread
	if (!th)
		th = new DummyThread();
	return th;
}

unsigned __stdcall Thread::Execute(Thread *th)
{
	_self.setKey(th);
	th->yield();

	if(th->_start)
	{
		th->_start->wait();
		th->_start = NULL;
	}

	try
	{
		th->priv->_tid = GetCurrentThreadId();
		if(!th->_name[0])
			snprintf(th->_name, sizeof(th->_name), "%d", GetCurrentThreadId());
		th->initial();
		if(th->getCancel() == cancelInitial) 
			th->setCancel(cancelDefault);
		th->run();
	}
	// ignore cancellation exception
	catch(const InterruptException&)
	{ ; }

	th->close();
	return 0;
}
#endif //WIN32

#if !defined(WIN32)
/*
 * PosixThread implementation
 */
inline void ThreadImpl::PosixThreadSigHandler(int signo)
{
	Thread	*t = getThread();
	PosixThread *th = NULL;

#ifdef	CCXX_EXCEPTIONS
	if (t) th = dynamic_cast<PosixThread*>(t);
#else
        if (t) th = (PosixThread*)(t);
#endif

	if (!th) return;

	switch(signo)
	{
	case SIGHUP:
		if(th)
			th->onHangup();
		break;
	case SIGABRT:
		if(th)
			th->onException();
		break;
	case SIGPIPE:
		if(th)
			th->onDisconnect();
		break;
	case SIGALRM:
#ifndef	CCXX_SIG_THREAD_ALARM
		if(PosixThread::_timer)
		{
			PosixThread::_timer->_alarm = 0;
			PosixThread::_timer->onTimer();
		}
		else
#endif 
		    if(th)
			th->onTimer();
		break;
#ifdef	SIGPOLL
	case SIGPOLL:
#else
	case SIGIO:
#endif
		if(th)
			th->onPolling();
		break;
	default:
		if(th)
			th->onSignal(signo);
	}
}
static void ccxx_sig_handler(int signo)
{
	ThreadImpl::PosixThreadSigHandler(signo);
}

PosixThread::PosixThread(int pri, size_t stack):
	Thread(pri,stack)
{
       SysTime::getTime(&_alarm);
}

void PosixThread::onTimer(void)
{
}

void PosixThread::onHangup(void)
{
}

void PosixThread::onException(void)
{
}

void PosixThread::onDisconnect(void)
{
}

void PosixThread::onPolling(void)
{
}

void PosixThread::onSignal(int sig)
{
}


void PosixThread::setTimer(timeout_t timer, bool periodic)
{
	sigset_t sigs;

#ifdef	HAVE_SETITIMER
	struct itimerval itimer;
	
	memset(&itimer, 0, sizeof(itimer));
	itimer.it_value.tv_usec = (timer * 1000) % 1000000;
	itimer.it_value.tv_sec = timer / 1000;
	if (periodic) {
		itimer.it_interval.tv_usec = itimer.it_value.tv_usec;
		itimer.it_interval.tv_sec = itimer.it_value.tv_sec;
	}
#else
	timer /= 1000;
#endif

#ifndef	CCXX_SIG_THREAD_ALARM	
	_arm.enterMutex();
	_timer = this;
#endif
       SysTime::getTime(&_alarm);
	sigemptyset(&sigs);
	sigaddset(&sigs, SIGALRM);
	pthread_sigmask(SIG_UNBLOCK, &sigs, NULL);
#ifdef	HAVE_SETITIMER
	setitimer(ITIMER_REAL, &itimer, NULL);
#else
	alarm(timer);
#endif
}	

timeout_t PosixThread::getTimer(void) const
{
#ifdef	HAVE_SETITIMER
	struct itimerval itimer;
#endif

	if(!_alarm)
		return 0;

#ifdef	HAVE_SETITIMER
	getitimer(ITIMER_REAL, &itimer);
	return (timeout_t)(itimer.it_value.tv_sec * 1000 +
		itimer.it_value.tv_usec / 1000);
#else
	time_t now = SysTime::getTime();
	return (timeout_t)(((now - _alarm) * 1000) + 500);
#endif
}

void PosixThread::endTimer(void)
{
#ifdef	HAVE_SETITIMER
	static const struct itimerval itimer = {{0, 0},{0,0}};
#endif

	sigset_t sigs;
#ifndef	CCXX_SIG_THREAD_ALARM
	if(_timer != this)
		return;
#endif

#ifdef	HAVE_SETITIMER
	setitimer(ITIMER_REAL, (struct itimerval *)&itimer, NULL);
#else
	alarm(0);
#endif
	sigemptyset(&sigs);
	sigaddset(&sigs, SIGALRM);
	pthread_sigmask(SIG_BLOCK, &sigs, NULL);
#ifndef	CCXX_SIG_THREAD_ALARM
	_arm.leaveMutex();
	_timer = NULL;
#endif
}

#if defined(HAVE_SIGWAIT) || defined(HAVE_SIGWAIT2)
void	PosixThread::waitSignal(signo_t signo)
{
	sigset_t	mask;

	sigemptyset(&mask);
	sigaddset(&mask, signo);
#ifndef HAVE_SIGWAIT2
	signo = sigwait(&mask);
#else
	sigwait(&mask, &signo);
#endif
}
#endif // ifdef HAVE_SIGWAIT

void	PosixThread::setSignal(int signo, bool mode)
{
	sigset_t sigs;

	sigemptyset(&sigs);
	sigaddset(&sigs, signo);

	if(mode)
		pthread_sigmask(SIG_UNBLOCK, &sigs, NULL);
	else
		pthread_sigmask(SIG_BLOCK, &sigs, NULL);
}	

void	PosixThread::signalThread(Thread* th,signo_t signo)
{
	pthread_kill(th->priv->_tid, signo);
}

pthread_attr_t * PosixThread::getPthreadAttrPtr(void)
{
 	return &priv->_attr;
}

pthread_t PosixThread::getPthreadId(void)
{
	return priv->_tid;
}

void PosixThread::sigInstall(int signo)
{
	struct sigaction act;
	
	act.sa_handler = (signalexec_t)&ccxx_sig_handler;
	sigemptyset(&act.sa_mask);

#ifdef	SA_INTERRUPT
	act.sa_flags = SA_INTERRUPT;
#else
	act.sa_flags = 0;
#endif
	sigaction(signo, &act, NULL);
}
#endif

#ifdef	USE_POLL

Poller::Poller()
{
	nufds = 0;
	ufds = NULL;
}

Poller::~Poller()
{
	if(ufds)
	{
		delete[] ufds;
		ufds = NULL;
	}
}

pollfd *Poller::getList(int cnt)
{
	if(nufds < cnt)
	{
		if(ufds)
			delete[] ufds;
		ufds = new pollfd[cnt];
		nufds = cnt;
	}
	return ufds;
}

#endif

Mutex SysTime::timeLock;

time_t SysTime::getTime(time_t *tloc) 
{
    time_t ret;
    lock();
    time_t temp;
#ifdef	WIN32
    ::time(&temp);
#else
    std::time(&temp);
#endif
    memcpy(&ret, &temp, sizeof(time_t));
    if (tloc != NULL) 
        memcpy(tloc, &ret, sizeof(time_t));
    unlock();
    return ret;   
}

int SysTime::getTimeOfDay(struct timeval *tp) 
{
	struct timeval temp;
    int ret(0);
    lock();

#ifdef	WIN32
    // We could use _ftime(), but it is not available on WinCE.
    // (WinCE also lacks time.h)
    // Note also that the average error of _ftime is around 20 ms :)
    time(&temp.tv_sec);
    temp.tv_usec = (GetTickCount() % 1000) * 1000;
	memcpy(tp, &temp, sizeof(struct timeval));
#else
    ret = ::gettimeofday(&temp, NULL);
    if (ret == 0) 
        memcpy(tp, &temp, sizeof(struct timeval));
#endif

    unlock();
    return ret;
}

struct tm *SysTime::getLocalTime(const time_t *clock, struct tm* result) 
{
    lock();
#ifdef	WIN32
    struct tm *temp = ::localtime(clock);
#else
    struct tm *temp = std::localtime(clock);
#endif
    memcpy(result, temp, sizeof(struct tm));
    unlock();
	return result;
}

struct tm *SysTime::getGMTTime(const time_t *clock, struct tm* result) 
{
    lock();
#ifdef	WIN32
    struct tm *temp = ::gmtime(clock);
#else
    struct tm *temp = std::gmtime(clock);
#endif
    memcpy(result, temp, sizeof(struct tm));
    unlock();
	return result;
}

// C stuff
// this function must declared as extern "C" for some compiler

#ifdef CCXX_NAMESPACES
}
#endif
 
/** EMACS **
 * Local variables:
 * mode: c++
 * c-basic-offset: 8
 * End:
 */

