// 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 #include #include #include #include #ifdef __BORLANDC__ #include #include #else #include #include #endif #include "private.h" #ifdef CCXX_HAVE_NEW_INIT #include #else inline void* operator new(size_t s,void* p) { return p;} #endif #ifdef WIN32 #include #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 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; 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 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 < 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__ #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) 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(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: */