blob: eb43c67b847f877dd7c8a3d583506410022fdb27 [file] [log] [blame]
// Copyright (C) 1999-2005 Open Source Telecom Corporation.
// Copyright (C) 2006-2010 David Sugar, Tycho Softworks.
//
// 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>
#include <cc++/process.h>
#ifdef __BORLANDC__
#include <stdio.h>
#include <stdlib.h>
#else
#include <cstdio>
#include <cstdlib>
#endif
#include "private.h"
#include <asm/page.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
extern "C"
{
typedef void *(*exec_t)(void *);
typedef RETSIGTYPE (*signalexec_t)(int);
#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
#ifndef ANDROID
pthread_testcancel();
#endif
#endif
}
return old;
}
void Thread::exitCancel(Cancel old)
{
Thread *th = getThread();
if(!th)
return;
if(old != th->_cancel) {
#ifndef WIN32
#ifndef ANDROID
pthread_testcancel();
#endif
#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, 0, sizeof(act));
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;
if(Process::isRealtime() && pri < 0)
pri = 0;
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
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
int 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 < 0 && Process::isRealtime())
pri = 0;
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__
#ifndef ANDROID
#ifdef __HAS_PRIORITY_SCHEDULING__
if(!pri)
pthread_attr_setinheritsched(&priv->_attr, PTHREAD_INHERIT_SCHED);
#else
pthread_attr_setinheritsched(&priv->_attr, PTHREAD_INHERIT_SCHED);
#endif
#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) const
{
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) const
{
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
{
if(!priv)
return (cctid_t)-1;
return priv->_tid;
}
bool Thread::isRunning(void) const
{
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
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;
if(!pthread_create(&priv->_tid, &priv->_attr, exec_t(&ccxx_exec_handler), this))
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;
}
// what if parent already exited?
// 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;
priv->_tid = 0;
#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();
}
extern "C" {
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;
}
extern "C" {
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() const
{
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);
}
}
extern "C" {
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_t now;
time(&now);
temp.tv_sec = (long)now;
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++
* tab-width: 4
* c-basic-offset: 4
* End:
*/