// $Id: UIThread.hxx,v 1.16 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 // /// ///////////////////////////////////////////////////////////////////////////// // // // AUTHOR : Yoon Kyung Koo (yoonforh@yahoo.com) // // // ///////////////////////////////////////////////////////////////////////////// #ifndef _UITHREAD_HXX #define _UITHREAD_HXX extern "C" { #include #include // for synchronization objects #include // for timestruc_t #include // for return value of trylock() } #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: sema_t sem; }; /** * Semaphore class * semaphore wrapper class * inline function implementation */ int Semaphore::wait() { return ::sema_wait(&sem); } int Semaphore::tryWait() { return ::sema_trywait(&sem); } int Semaphore::notify() { return ::sema_post(&sem); } /** * Mutex class * mutex wrapper class * declaration */ class Mutex { public: Mutex(); ~Mutex(); inline mutex_t& getMutex(); inline int lock(); inline int trylock(); inline int unlock(); private: mutex_t mutex; }; /** * Mutex class * mutex wrapper class * inline function implementation */ mutex_t& Mutex::getMutex() { return mutex; } int Mutex::lock() { return ::mutex_lock(&mutex); } int Mutex::trylock() { return ::mutex_trylock(&mutex); } // return EBUSY when already locked int Mutex::unlock() { return ::mutex_unlock(&mutex); } /** * CondVar class * condition variable wrapper class * declaration */ class CondVar { public: CondVar(Mutex& mutex); ~CondVar(); inline int lock(); inline int wait(); inline int timedWait(int sec, int nsec=0); inline int timedWait(timestruc_t& timeout); inline int notify(); inline int notifyAll(); private: Mutex& mutex; cond_t cv; }; /** * CondVar class * condition variable wrapper class * inline function implementation */ int CondVar::lock() { return mutex.lock(); } int CondVar::wait() { return ::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) { timestruc_t timeout; timeout.tv_sec = ::time(NULL) + sec; timeout.tv_nsec = nsec; int result = ::cond_timedwait(&cv, &mutex.getMutex(), &timeout); if (result == 0) return CONDVAR_SIGNALED; else if (result == ETIME) // timed out return CONDVAR_TIME_EXPIRED; cerr << "CondVar::timedWait(sec = " << dec << sec << ", nsec = " << nsec << ") failed. " << ::strerror(result); return CONDVAR_ERROR; } int CondVar::timedWait(timestruc_t& timeout) { int result = ::cond_timedwait(&cv, &mutex.getMutex(), &timeout); if (result == 0) return CONDVAR_SIGNALED; else if (result == ETIME) // timed out return CONDVAR_TIME_EXPIRED; cerr << "CondVar::timedWait(sec = " << dec << timeout.tv_sec << ", nsec = " << timeout.tv_nsec << ") failed. " << ::strerror(result); return CONDVAR_ERROR; } int CondVar::notify() { return ::cond_signal(&cv); } int CondVar::notifyAll() { return ::cond_broadcast(&cv); } /** * Thread class * thread wrapper class * declaration * NOTE * the detach model of UI thread impl. is different from * that of posix thread impl. * posix thread uses cleanup routine but * UI thread don't have such routine instead, * can wait for any thread to terminate using thr_join. * use joinAll() to elimnate non-detached thread problem * TO DO * -. support for TSD(thread specific data) * -. support for ThreadAttr */ 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; } stackSize = size; return 0; } int getStackSize() { return stackSize; } thread_t getID() { return id; } bool equal(thread_t id) { return (this->id==id); } bool equal(Thread& thread) { return (id==thread.getID()); } int start() { running = true; return ::thr_create(NULL, stackSize, _run, (void *)this, 0 /* thread creation attribute */, &id); } int kill(int signo) { return ::thr_kill(id, signo); } const char* getName() { return name.c_str(); }; void setName(const string& name) { this->name = name; } // waiting for this thread to exit int join() { thread_t departed; return ::thr_join(id, &departed, (void **)NULL); } const int getStatus() { return status; } // below 4 functions are available only in UI thread int pause() { return ::thr_suspend(id); } int resume() { return ::thr_continue(id); } int getPriority() { int prio; ::thr_getprio(id, &prio); return prio; } int setPriority(int prio) { return ::thr_setprio(id, prio); } // static members static thread_t joinAny() { // waiting for any thread to exit (only available in UI thread) int *status; thread_t departed; ::thr_join(0/* any thread */, &departed, (void **) &status); // you can print several exit status information here // sure to free status ::free ((void *)status); return departed; } // only availabe in UI thread static int getConcurrencyLevel() { return ::thr_getconcurrency(); } // new concurrency level will not set to the exact new level arguement, // but also act as a hint to beused by thread library. 0 level means default. // (only availabe in UI thread) static int setConcurrencyLevel(int newLevel=0) { return ::thr_setconcurrency(newLevel); } static thread_t self() { return ::thr_self(); } static void exit() { ::thr_exit(NULL); } static void yield() { ::thr_yield(); } // how : SIG_BLOCK SIG_UNBLOCK SIG_SETMASK static int setSignalMask(int how, const sigset_t * set, sigset_t * oset) { int res; if ((res = ::thr_sigsetmask(how, set, oset)) != 0) { cerr << "thr_sigsetmask failed:" << ::strerror(res) << endl; return -1; } return 0; } static int signalWait(sigset_t * set) { int res; #ifdef _POSIX_PTHREAD_SEMANTICS 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: thread_t id; bool running; int status; int stackSize; // This function is called in new thread context static void * _run(void * arg) { ((Thread *)arg)->run(); ((Thread *)arg)->cleanup(); ((Thread *)arg)->setFatalStatus(); // after run, thread exits return NULL; } }; #endif // _UITHREAD_HXX