// $Id: PThread.hxx,v 1.17 1999/09/21 17:55:56 yoonforh Exp $ ///////////////////////////////////////////////////////////////////////////// /* * Copyright (c) 1998-2002 Yoon Kyung Koo(yoonforh@yahoo.com). * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL YOON KYUNG KOO OR THE OTHER * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ ///////////////////////////////////////////////////////////////////////////// // // DESCRIPTION // a Thread class implementation // // NOTE // This posix thread class implementation is based upon // POSIX.1c standards(draft 10). // Using this class in prior draft environments // without serious revision could lead to malfunction. // TODO // -. This implementation lacks of thread attribute support. // -. Several enhancements are added in UI thread version. // Should apply those enhancements in this pthread class impl. // -. DG/UX (draft 6) sets error number in errno variable // but standard PTHREAD DOES NOT SET errno BUT pass // as a RETURN VALUE!!! // so, this Thread class DOES NOT SUPPORT DG/UX // /// ///////////////////////////////////////////////////////////////////////////// // // // AUTHOR : Yoon Kyung Koo (yoonforh@yahoo.com) // // // ///////////////////////////////////////////////////////////////////////////// #ifndef _PTHREAD_HXX #define _PTHREAD_HXX extern "C" { #include #include #include #include } #include /** * Semaphore class * semaphore wrapper class * declaration */ class Semaphore { public: Semaphore(int value=0); ~Semaphore(); inline int wait(); inline int tryWait(); inline int notify(); private: sem_t sem; }; /** * Semaphore class * semaphore wrapper class * inline function implementation */ int Semaphore::wait() { return ::sem_wait(&sem); } int Semaphore::tryWait() { return ::sem_trywait(&sem); } int Semaphore::notify() { return ::sem_post(&sem); } /** * Mutex class * mutex wrapper class * declaration */ class Mutex { public: Mutex(); ~Mutex(); inline pthread_mutex_t& getMutex(); inline int lock(); inline int trylock(); inline int unlock(); private: pthread_mutex_t mutex; }; /** * Mutex class * mutex wrapper class * inline function implementation */ pthread_mutex_t& Mutex::getMutex() { return mutex; } int Mutex::lock() { return ::pthread_mutex_lock(&mutex); } int Mutex::trylock() { return ::pthread_mutex_trylock(&mutex); } // return EBUSY when already locked int Mutex::unlock() { return ::pthread_mutex_unlock(&mutex); } /** * CondVar class * condition variable wrapper class * declaration */ class CondVar { public: CondVar(Mutex& mutex); ~CondVar(); inline int lock(); inline int wait(bool doLock=false); inline int timedWait(int sec, int nsec=0, bool doLock=false); inline int notify(bool doLock=false); inline int notifyAll(bool doLock=false); private: Mutex& mutex; pthread_cond_t cv; }; /** * CondVar class * condition variable wrapper class * inline function implementation */ int CondVar::lock() { return mutex.lock(); } int CondVar::wait(bool doLock) { if (doLock) lock(); return ::pthread_cond_wait(&cv, &mutex.getMutex()); } /** * returns 0, if success * 1, if time expired * -1, if error */ #define CONDVAR_SIGNALED (0) #define CONDVAR_TIME_EXPIRED (1) #define CONDVAR_ERROR (-1) int CondVar::timedWait(int sec, int nsec, bool doLock) { struct timespec timeout; if (doLock) lock(); timeout.tv_sec = ::time(NULL) + sec; timeout.tv_nsec = nsec; int result = ::pthread_cond_timedwait(&cv, &mutex.getMutex(), &timeout); if (result == 0) return CONDVAR_SIGNALED; else if (result == ETIMEDOUT) // timed out return CONDVAR_TIME_EXPIRED; cerr << "CondVar::timedWait(sec = " << dec << sec << ", nsec = " << nsec << ") failed. " << ::strerror(result); return CONDVAR_ERROR; } int CondVar::notify(bool doLock) { if (doLock) lock(); return ::pthread_cond_signal(&cv); } int CondVar::notifyAll(bool doLock) { if (doLock) lock(); return ::pthread_cond_broadcast(&cv); } /** * Thread class * thread wrapper class * declaration * TO DO * -. support for TSD(thread specific data) * -. support for ThreadAttr * (currently only stack size supported) */ enum ThreadStatus { THREAD_STATUS_NORMAL = 0, THREAD_STATUS_PARTIAL = 1, THREAD_STATUS_FATAL = 2, THREAD_STATUS_APP_FATAL = 3, THREAD_STATUS_UNKNOWN = 4 }; class Thread { public: Thread(const string& name="Anonymous Thread", int stackSize=0); virtual ~Thread(); int setStackSize(int size) { if (running) { cerr << "Thread::setStackSize failed. cannot change stack size of running thread" << endl; return -1; } int res; if ((res = ::pthread_attr_setstacksize(&attr, (size_t) size)) != 0) { cerr << "pthread_attr_setstacksize failed. " << ::strerror(res) << endl; return -1; } return 0; } int getStackSize() { size_t size; int res; if ((res = ::pthread_attr_getstacksize(&attr, &size)) != 0) { cerr << "pthread_attr_getstacksize failed:" << ::strerror(res) << endl; return -1; } return size; } pthread_t getID() { return id; } int cancel() { return ::pthread_cancel(id); } bool equal(pthread_t id) { return (::pthread_equal(this->id, id)?true:false); } int start() { running = true; return ::pthread_create(&id, &attr, _run, this); } int detach() { return ::pthread_detach(id); } int kill(int signo) { return ::pthread_kill(id, signo); } const char* getName() { return name.c_str(); } void setName(const string& name) { this->name = name; } int join() { void * status; int val; if ((val = ::pthread_join(id, &status)) != 0) // NOTE that if try to join self, EDEADLK will be returned!!! cerr << "pthread_join failed:" << ::strerror(val) << endl; #if defined (DEBUG) else cerr << "joined tid[0x" << hex << (int) id << "] having exit value [" << dec << (int) status <<"]"<< endl; #endif return val; } const int getStatus() { return status; } //// // static members // Requests delivery of any pending cancel to the current thread. static void testCancel() { ::pthread_testcancel(); } static int setCancelState(bool enable, int *oldState) { return ::pthread_setcancelstate( (enable?PTHREAD_CANCEL_ENABLE:PTHREAD_CANCEL_DISABLE), oldState); } static int setCancelType(bool isAsync, int *oldType) { return ::pthread_setcanceltype( (isAsync?PTHREAD_CANCEL_ASYNCHRONOUS:PTHREAD_CANCEL_DEFERRED), oldType); } static pthread_t self() { return ::pthread_self(); } static void exit() { ::pthread_exit(NULL); } static void yield() { ::sched_yield(); } // how : SIG_BLOCK SIG_UNBLOCK SIG_SETMASK static int setSignalMask(int how, const sigset_t * set, sigset_t * oset) { int res; if ((res = ::pthread_sigmask(how, set, oset)) != 0) { cerr << "pthread_sigmask failed : " << ::strerror(res) << endl; return -1; } return 0; } static int signalWait(sigset_t * set) { int res; #if defined(_POSIX_PTHREAD_SEMANTICS) || defined(__osf__) int signo; if ((res = ::sigwait(set, &signo)) != 0) { cerr << "sigwait failed : " << ::strerror(res) << endl; return -1; } return signo; #else if ((res = ::sigwait(set)) < 0) { cerr << "sigwait failed : " << ::strerror(errno) << endl; return -1; } return res; #endif } protected: virtual void init(); virtual void run(); virtual void cleanup(); void setStatus(const int status) { this->status=status; } void setNormalStatus() { status=THREAD_STATUS_NORMAL; } void setBadStatus() { status=THREAD_STATUS_PARTIAL; } void setFatalStatus() { running = false; status=THREAD_STATUS_FATAL; } void setAppFatalStatus() { status=THREAD_STATUS_APP_FATAL; } void setUnknownStatus() { status=THREAD_STATUS_UNKNOWN; } protected: string name; private: pthread_t id; pthread_attr_t attr; int status; bool running; // This function is run in new thread context static void * _run(void * arg); // when pthread cancelled or exited, this will call cleanup() method static void _cleanup(void * arg) { ((Thread *)arg)->cleanup(); } }; #endif // _PTHREAD_HXX