Tekkotsu Homepage
Demos
Overview
Downloads
Dev. Resources
Reference
Credits

Thread.cc

Go to the documentation of this file.
00001 #ifndef PLATFORM_APERIOS
00002 #include "Thread.h"
00003 #include "Shared/ReferenceCounter.h"
00004 #include "ProcessID.h"
00005 
00006 #include <pthread.h>
00007 #include <string.h>
00008 #include <iostream>
00009 #include <signal.h>
00010 #include <unistd.h>
00011 #include <cassert>
00012 #include <errno.h>
00013 #ifdef __APPLE__
00014 # include <AvailabilityMacros.h>
00015 #endif
00016 
00017 using namespace std;
00018 
00019 #define THREADCANCEL_SANITY_CHECKS
00020 
00021 /*! @cond INTERNAL */
00022 //! provides the system-dependent implementation of a thread
00023 struct Threadstorage_t {
00024   //! constructor
00025   Threadstorage_t() : threadInfo() {}
00026   
00027   //! the main POSIX reference to the thread
00028   pthread_t threadInfo;
00029   
00030   //! storage which will be set up as a thread-specific memory value, so threads can tell themselves apart
00031   static pthread_key_t selfKey;
00032 private:
00033   Threadstorage_t(const Threadstorage_t& r); //!< don't call
00034   Threadstorage_t& operator=(const Threadstorage_t& r); //!< don't call
00035 };
00036 pthread_key_t Threadstorage_t::selfKey=0;
00037 /*! @endcond */
00038 
00039 Thread::Thread()
00040   : pt(new Threadstorage_t), started(false), running(false), returnValue(NULL),
00041   noCancelDepth(0), cancelOrig(PTHREAD_CANCEL_ENABLE), group(NULL)
00042 {
00043   Thread* cur=getCurrent();
00044   if(cur!=NULL)
00045     group=cur->getGroup();
00046 }
00047 
00048 Thread::~Thread() {
00049   //can only happen externally
00050   if(started) {
00051     stop();
00052     join();
00053   }
00054   /*if(pt==NULL) {
00055     std::cerr << "Thread storage already deleted!?!?!" << std::endl;
00056     *(int*)NULL=0xDEADDEAD;
00057   }*/
00058   assert(pt!=NULL);
00059   delete pt;
00060   pt=NULL;
00061 }
00062 
00063 void Thread::start() {
00064   if(started) {
00065     std::cerr << "Thread::start() -- thread is already started!" << std::endl;
00066     std::cerr << "   make another instance if you want to run another copy of this thread" << std::endl;
00067     return;
00068   }
00069   started=true;
00070   if(int err=pthread_create(&pt->threadInfo, NULL, launch, this))
00071     cerr << "Thread start(), pthread_create: " << strerror(err) << endl;
00072 }
00073 
00074 void * Thread::run() {
00075   for(;;) {
00076     unsigned int sleeptime=runloop();
00077     if(sleeptime==-1U)
00078       return returnValue;
00079     if(sleeptime>0)
00080       usleep(sleeptime);
00081     testCancel();
00082   }
00083   // this return is just to satisfy warnings with silly compiler
00084   return returnValue; //never happens -- cancel or max sleep time would exit
00085 }
00086 
00087 void Thread::interrupt() {
00088   if(!isRunning()) //can't interrupt before thread has been launched!
00089     return;
00090   if(signal(SIGALRM,handleInterrupt)==SIG_ERR)
00091     perror("PollThread::run(): initial signal()");
00092   sendSignal(SIGALRM);
00093 }
00094 
00095 void Thread::stop() {
00096   if(!started && !running) {
00097     std::cerr << "Thread::stop() -- thread has not been started!" << std::endl;
00098     return;
00099   }
00100   if(started && !running)
00101     usleep(50000);
00102   if(started && !running)
00103     std::cerr << "Thread::stop(): Waiting for thread launch to complete (stillborn thread?)" << std::endl;
00104   while(started && !running)
00105     usleep(100000);
00106   if(!running)
00107     return;
00108   if(int err=pthread_cancel(pt->threadInfo))
00109     cerr << "Thread cancel(), pthread_cancel("<<pt->threadInfo<<"): " << strerror(err) << endl;
00110 #if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5
00111   /* Mac OS X Tiger and earlier (v10.4 and prior) don't handle pthread_cancel in system calls,
00112    * (namely sleep, read, listen, etc.) so we need to send a signal to wake it up.
00113    * Mac OS X Leopard seems to fix this, although the thread usually gets canceled before we
00114    * get to signal it, causing a warning in sendSignal()... hence the Mac OS version check.
00115    * This is generally fine to do on other platforms too, but seems to cause a problem on Fedora Core 5
00116    * where it causes the pthread_cleanup to skip Thread::handle_exit and cause mayhem.
00117    * So we'll restrict the interrupt signal to only those platforms which won't break out of sleep on a cancel */
00118   interrupt(); // break thread out of any long sleep commands
00119 #endif
00120 }
00121 
00122 void Thread::kill() {
00123   sendSignal(SIGUSR1);
00124 }
00125 
00126 void Thread::murder() {
00127   if(int err=pthread_detach(pt->threadInfo))
00128     cerr << "Thread kill(), thread_detach: " << strerror(err) << endl;
00129   sendSignal(SIGSTOP);
00130   started=running=false;
00131 }
00132 
00133 void Thread::sendSignal(int sig) {
00134   if(started && !running)
00135     usleep(50000);
00136   if(started && !running)
00137     std::cerr << "Thread::stop(): Waiting for thread launch to complete (stillborn thread?)" << std::endl;
00138   while(started && !running)
00139     usleep(100000);
00140   if(!isRunning())
00141     return;
00142   if(int err=pthread_kill(pt->threadInfo,sig))
00143     cerr << "Thread sendSignal(), pthread_kill("<<sig<<"): " << strerror(err) << endl;
00144 }
00145 
00146 void * Thread::join() {
00147   void * ans=NULL;
00148   if(int err=pthread_join(pt->threadInfo, &ans))
00149     cerr << "Thread join(), pthread_join: " << strerror(err) << endl;
00150   return ans;
00151 }
00152 
00153 Thread* Thread::getCurrent() {
00154   if(Threadstorage_t::selfKey==0) {
00155     static bool gaveError=false;
00156     if(!gaveError) {
00157       cerr << "ERROR: In Thread::getCurrent(), selfKey uninitialized; Thread::initMainThread was not called." << endl;
00158       cerr << "       (This error will only be displayed once)" << endl;
00159       gaveError=true;
00160     }
00161     return NULL;
00162   }
00163   return static_cast< Thread* >(pthread_getspecific(Threadstorage_t::selfKey));
00164 }
00165 
00166 void Thread::initMainThread() {
00167   if(int err=pthread_key_create(&Threadstorage_t::selfKey,warnSelfUndestructed))
00168     cerr << "WARNING: In Thread::initMainThread(), pthread_key_create(selfKey) returned " << strerror(err) << endl;
00169   if(int err=pthread_setspecific(Threadstorage_t::selfKey,NULL))
00170     cerr << "WARNING: In Thread::initMainThread(), pthread_setspecific(selfKey) returned " << strerror(err) << endl;
00171 }
00172 
00173 void Thread::releaseMainThread() {
00174   //handle_exit(NULL);
00175   if(int err=pthread_key_delete(Threadstorage_t::selfKey))
00176     cerr << "WARNING: In Thread::releaseMainThread, pthread_key_delete(selfKey) returned " << strerror(err) << endl;
00177 }
00178 
00179 void Thread::testCancel() {
00180 #ifdef DEBUG
00181   if(noCancelDepth!=0) {
00182     cerr << "WARNING: Thread::testCancel called with noCancelDepth=="<<noCancelDepth<<" (process="<<ProcessID::getID()<<", thread="<<pthread_self()<<")"<<endl;
00183   }
00184 #endif
00185   pthread_testcancel();
00186 }
00187 
00188 void * Thread::launch(void * msg) {
00189   //cout << "Spawn thread " << pthread_self() << " from process " << ProcessID::getID() << endl;
00190   Thread* cur=static_cast<Thread*>(msg);
00191   if(cur==NULL) {
00192     cerr << "ERROR: Thread::launch with NULL msg" << endl;
00193     return NULL;
00194   }
00195 
00196   if(int err=pthread_setspecific(Threadstorage_t::selfKey,msg))
00197     cerr << "WARNING: In ThreadNS::launch(), pthread_setspecific(selfKey) returned " << strerror(err) << endl;
00198   
00199   //disable cancel while calling launch()
00200   if(int err=pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL))
00201     cerr << "Thread launch(), pthread_setcanceltype: " << strerror(err) << endl;
00202   ++(cur->noCancelDepth);
00203   if(signal(SIGUSR1,Thread::handle_launch_signal)==SIG_ERR)
00204     perror("Thread launch(), signal(SIGUSR1,handle_launch_signal)");
00205   cur->running=true;
00206   if(!cur->launched()) {
00207     //subclass's launch cancelled launch
00208     --(cur->noCancelDepth);
00209     handle_exit(NULL);
00210     return cur->returnValue;
00211   }
00212   --(cur->noCancelDepth);
00213   
00214   //These pthread functions actually define a scope between them (ugh)
00215   //I've added braces of my own to make this explicitly clear
00216   pthread_cleanup_push(Thread::handle_exit,msg); {
00217     
00218     if(signal(SIGUSR1,Thread::handle_signal)==SIG_ERR)
00219       perror("Thread launch(), signal(SIGUSR1,handle_signal)");
00220     
00221     if(cur->noCancelDepth==0) {
00222       //reset cancelability before run
00223       if(int err=pthread_setcancelstate(cur->cancelOrig,NULL))
00224         cerr << "Thread launch(), pthread_setcanceltype: " << strerror(err) << endl;
00225       if(int err=pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,NULL))
00226         cerr << "Thread launch(), pthread_setcanceltype: " << strerror(err) << endl;
00227     }
00228     cur->returnValue=cur->run();
00229     
00230   } pthread_cleanup_pop(true);
00231   return cur->returnValue;
00232 }
00233 
00234 void Thread::handle_launch_signal(int /*sig*/) {
00235   handle_exit(NULL);
00236   pthread_exit(NULL);
00237 }
00238 
00239 void Thread::handle_signal(int /*sig*/) {
00240   pthread_exit(NULL);
00241 }
00242 
00243 void Thread::handle_exit(void * th) {
00244   //cout << "Cull thread " << pthread_self() << endl;
00245   Thread* cur=getCurrent();
00246   if(cur==NULL) {
00247     cerr << "ERROR: handle_exit called for a NULL thread" << endl;
00248     if(th!=NULL) {
00249       static_cast<Thread*>(th)->cancelled();
00250       static_cast<Thread*>(th)->started=static_cast<Thread*>(th)->running=false;
00251     }
00252     return;
00253   }
00254   
00255   if(th!=NULL && th!=cur)
00256     cerr << "WARNING: handle_exit argument does not match selfKey" << endl;
00257   if(cur->noCancelDepth!=0) {
00258     cerr << "WARNING: thread " << pthread_self() << " of ProcessID_t " << ProcessID::getID() << " exited while noCancelDepth>0 (was " << cur->noCancelDepth << ")" << endl;
00259     cerr << "         This may indicate a mutex was left locked." << endl;
00260   }
00261   if(int err=pthread_setspecific(Threadstorage_t::selfKey,NULL))
00262     cerr << "WARNING: In Thread::handle_exit(), pthread_setspecific(selfKey) returned " << err << endl;
00263   cur->cancelled();
00264   cur->started=cur->running=false;
00265 }
00266 
00267 void Thread::pushNoCancel() {
00268   Thread * cur=getCurrent();
00269   if(cur==NULL) {
00270     //cerr << "ERROR: Thread::pushNoCancel was given NULL thread by getCurrent, thread=" << pthread_self() << endl;
00271     //not so bad, indicates already canceled -- don't test cancel again, don't want to cancel-recurse
00272     if(int err=pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL))
00273       cerr << "ERROR: Thread pushNoCancel(), pthread_setcanceltype: " << strerror(err) << endl;
00274   } else {
00275     ++(cur->noCancelDepth);
00276     int previous=-1;
00277     if(int err=pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,&previous))
00278       cerr << "ERROR: Thread pushNoCancel(), pthread_setcanceltype: " << strerror(err) << endl;
00279 #ifdef THREADCANCEL_SANITY_CHECKS
00280     if(cur->noCancelDepth==1 && previous!=cur->cancelOrig)
00281       cerr << "WARNING: In Thread::pushNoCancel, cancel state was wrong (was " << previous << ", expected " << cur->cancelOrig << ")" << endl;
00282     else if(cur->noCancelDepth!=1 && previous!=PTHREAD_CANCEL_DISABLE)
00283       cerr << "WARNING: In Thread::pushNoCancel, cancel state was somehow re-enabled" << endl;
00284 #endif
00285   }
00286 }
00287 void Thread::popNoCancel() {
00288   Thread * cur=getCurrent();
00289   if(cur==NULL) {
00290     //cerr << "ERROR: Thread::popNoCancel was given NULL thread by getCurrent, thread=" << pthread_self() << endl;
00291     //not so bad, indicates already canceled -- don't test cancel again, don't want to cancel-recurse
00292     return; //no point in continuing
00293   } else if(cur->noCancelDepth==0) {
00294     cerr << "ERROR: Thread::popNoCancel underflow" << endl;
00295   } else
00296     --(cur->noCancelDepth);
00297   int previous=-1;
00298   if(cur->noCancelDepth==0) {
00299     if(int err=pthread_setcancelstate(cur->cancelOrig,&previous))
00300       cerr << "ERROR: Thread popNoCancel(), pthread_setcanceltype: " << strerror(err) << endl;
00301   }
00302 #ifdef THREADCANCEL_SANITY_CHECKS
00303   else { //still disabled, double check it
00304     if(int err=pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,&previous))
00305       cerr << "ERROR: Thread popNoCancel(), pthread_setcanceltype: " << strerror(err) << endl;
00306   }
00307   if(previous!=PTHREAD_CANCEL_DISABLE)
00308     cerr << "WARNING: In Thread::popNoCancel, cancel state was somehow re-enabled" << endl;
00309 #endif
00310 }
00311 
00312 void Thread::handleInterrupt(int /*signal*/) {
00313   //if(signal(SIGALRM,SIG_DFL)==SIG_ERR)
00314   //  perror("PollThread::handleInterrupt(): could not re-enable signal");
00315   Thread * cur=Thread::getCurrent();
00316   if(cur==NULL) {
00317     std::cerr << "Thread::handleInterrupt called from non-Thread" << endl;
00318     return;
00319   }
00320   if(cur->noCancelDepth==0)
00321     cur->testCancel();
00322   cur->interrupted();
00323 }
00324 
00325 void Thread::warnSelfUndestructed(void* msg) {
00326   cerr << "ERROR: Thread local data (selfKey) not deleted by Thread::handle_exit" << endl;
00327   Thread* cur = getCurrent();
00328   if(cur!=NULL)
00329     cerr << "       Weird, key wasn't cleared... (" << cur << ") " << cur->noCancelDepth << " locks on stack? " << endl;;
00330   if(msg==NULL) {
00331     cerr << "       Message is null, warnCancelDepthUndestructed shouldn't have been called." << endl;
00332   } else {
00333     if(cur!=NULL && cur!=msg)
00334       cerr << "       and current thread does not match msg (" << cur << " vs " << msg << ")" << endl;
00335     cur = static_cast<Thread*>(msg);
00336   }
00337   if(cur!=NULL) {
00338     //try to recover
00339     if(cur->noCancelDepth==0) {
00340       cerr << "       But at least the depth is 0" << endl;
00341     } else {
00342       cerr << "       The depth indicates there may be " << cur->noCancelDepth << " locks left in place" << endl;
00343     }
00344     cur->cancelled();
00345     cur->started=cur->running=false;
00346     pthread_setspecific(Threadstorage_t::selfKey,NULL);
00347   }
00348 }
00349 
00350 
00351 namespace ThreadNS {
00352     
00353   /*! @cond INTERNAL */
00354   //! This handles the actual lock implementation, which allows Lock to provide an abstract interface
00355   class Lock::LockStorage : public ReferenceCounter {
00356     friend class Condition;
00357   public:
00358     //! constructor
00359     LockStorage() : ReferenceCounter(), locklevel(0), mutex(), attr(), threadkey() {
00360       AddReference();
00361       pthread_mutexattr_init(&attr);
00362       pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_RECURSIVE);
00363       pthread_mutex_init(&mutex,&attr);
00364     }
00365     //! destructor, releases any pending locks (with warning
00366     ~LockStorage() {
00367       pthread_mutexattr_destroy(&attr);
00368       pthread_mutex_destroy(&mutex);
00369       if(locklevel>1) //having one left is ok, perhaps even good (keeping the lock as it is destroyed)
00370         cerr << "WARNING: lockstorage destructed with " << locklevel << " locks still in effect" << endl;
00371       while(locklevel>0) {
00372         locklevel--;
00373         Thread::popNoCancel();
00374       }
00375     }
00376     //! copy constructor (functional!) -- both locks will wind up referencing the same system resource, so this is more of an alias than a clone
00377     LockStorage(const LockStorage& ls) : ReferenceCounter(ls), locklevel(ls.locklevel), mutex(ls.mutex), attr(ls.attr), threadkey(ls.threadkey) {}
00378     //! assignment (functional!) -- both locks will wind up referencing the same system resource, so this is more of an alias than a clone
00379     LockStorage& operator=(const LockStorage& ls) { ReferenceCounter::operator=(ls); locklevel=ls.locklevel; mutex=ls.mutex; attr=ls.attr; threadkey=ls.threadkey; return *this; }
00380     
00381     //! trigger and wait for a mutual exclusion lock, recursively
00382     void lock() {
00383       Thread::pushNoCancel();
00384       if(int err=pthread_mutex_lock(&mutex)) {
00385         cerr << "ERROR: ThreadNS::Lock::lock() failed: " << strerror(err) << endl;
00386         Thread::popNoCancel();
00387       } else
00388         locklevel++;
00389     }
00390     //! attempt to get a lock, but return false if it is not immediately available
00391     bool trylock() {
00392       Thread::pushNoCancel();
00393       if(!pthread_mutex_trylock(&mutex)) {
00394         locklevel++;
00395         return true;
00396       } else {
00397         Thread::popNoCancel();
00398         return false;
00399       }
00400     }
00401     //! release a lock (recursively, won't actually release the lock resource until all calls to lock() have been balanced)
00402     void unlock() {
00403       if(locklevel==0)
00404         cerr << "ERROR: ThreadNS::Lock::unlock() underflow" << endl;
00405       locklevel--;
00406       if(int err=pthread_mutex_unlock(&mutex))
00407         cerr << "ERROR: ThreadNS::Lock::unlock() failed: " << strerror(err) << endl;
00408       Thread::popNoCancel();
00409     }
00410     //! returns the depth of the lock recursion (#locklevel)
00411     unsigned int getLockLevel() { return locklevel; }
00412     
00413   protected:
00414     unsigned int locklevel; //!< depth of lock recursion (i.e. number of calls to lock() minus calls to unlock())
00415     pthread_mutex_t mutex; //!< system lock resource
00416     pthread_mutexattr_t attr; //!< system lock resource attributes (used to specify #mutex is recursive in the system as well)
00417     pthread_key_t threadkey; //!< not making use of the thread specific nature of these, but we are making use of the call to a destructor (emergencyUnlock) on cancel
00418   };
00419 
00420   Lock::LockStorage* Lock::glock=NULL;
00421   /*! @endcond */
00422 
00423   Lock::Lock() : mylock(new LockStorage), locklevel(0) {
00424     if(glock==NULL)
00425       setup();
00426   }
00427   /*Lock::Lock(const Lock& l)
00428     : mylock(l.mylock), locklevel(0)
00429   {
00430     glock->lock();
00431     mylock->AddReference();
00432     glock->unlock();
00433     lock();
00434   }
00435   Lock::Lock(const Lock& l, bool autolock)
00436     : mylock(l.mylock), locklevel(0)
00437   {
00438     glock->lock();
00439     mylock->AddReference();
00440     glock->unlock();
00441     if(autolock)
00442       lock();
00443   }
00444   Lock& Lock::operator=(const Lock& l) {
00445     glock->lock();
00446     lock();
00447     if(locklevel>2)
00448       cerr << "WARNING: ThreadNS::Lock overwritten with "<<locklevel<<" locks still in effect" << endl;
00449     if(!mylock->RemoveReference())
00450       while(locklevel>0)
00451         unlock();
00452     mylock=l.mylock;
00453     locklevel=0;
00454     glock->unlock();
00455     return *this;
00456   }*/
00457   Lock::~Lock() {
00458     glock->lock();
00459     if(locklevel>1)
00460       cerr << "WARNING: ThreadNS::Lock destructed with "<<locklevel<<" locks still in effect" << endl;
00461     if(!mylock->RemoveReference())
00462       while(locklevel>0)
00463         unlock();
00464     glock->unlock();
00465   }
00466 
00467   void Lock::lock() {
00468     mylock->lock();
00469     locklevel++;
00470   }
00471   bool Lock::trylock() {
00472     if(mylock->trylock()) {
00473       locklevel++;
00474       return true;
00475     } else {
00476       return false;
00477     }
00478   }
00479   void Lock::unlock() {
00480     locklevel--;
00481     mylock->unlock();
00482   }
00483   unsigned int Lock::getLockLevel() const {
00484     return mylock->getLockLevel();
00485   }
00486   void Lock::setup() {
00487     if(glock==NULL)
00488       glock=new LockStorage;
00489   }
00490   
00491   /*! @cond INTERNAL */
00492   //! Implement system-dependent portion of a thread condition, a signaling mechanism.
00493   /*! This is a very basic wrapper -- just adds a constructor and destructor to the POSIX pthread_cond_t. */
00494   class Condition::ConditionStorage {
00495   public:
00496     //! constructor
00497     ConditionStorage() : cond() {
00498       if(int err=pthread_cond_init(&cond,NULL)) {
00499         cerr << "ERROR: ThreadNS::Condition::ConditionStorage() failed: " << strerror(err) << endl;
00500       }
00501     }
00502     //! destructor
00503     ~ConditionStorage() {
00504       if(int err=pthread_cond_destroy(&cond)) {
00505         cerr << "ERROR: ThreadNS::Condition::~ConditionStorage() failed: " << strerror(err) << endl;
00506       }
00507     }
00508     //! system resource storage
00509     pthread_cond_t cond;
00510   };
00511   /*! @endcond */
00512   
00513   Condition::Condition() : mycond(new ConditionStorage) {}
00514   Condition::~Condition() { delete mycond; mycond=NULL; }
00515   
00516   void Condition::broadcast() const {
00517     if(int err=pthread_cond_broadcast(&mycond->cond)) {
00518       cerr << "ERROR: ThreadNS::Condition::broadcast() failed: " << strerror(err) << endl;
00519     }
00520   }
00521   void Condition::signal() const {
00522     if(int err=pthread_cond_signal(&mycond->cond)) {
00523       cerr << "ERROR: ThreadNS::Condition::signal() failed: " << strerror(err) << endl;
00524     }
00525   }
00526   bool Condition::timedwait(Lock& l, const timespec* abstime) const {
00527     if(int err=pthread_cond_timedwait(&mycond->cond,&l.mylock->mutex,abstime)) {
00528       if(err!=ETIMEDOUT)
00529         cerr << "ERROR: ThreadNS::Condition::timedwait() failed: " << strerror(err) << endl;
00530       return false;
00531     }
00532     return true;
00533   }
00534   void Condition::wait(Lock& l) const {
00535     if(int err=pthread_cond_wait(&mycond->cond,&l.mylock->mutex)) {
00536       cerr << "ERROR: ThreadNS::Condition::wait() failed: " << strerror(err) << endl;
00537     }
00538   }
00539   
00540 }
00541 
00542 #endif // PLATFORM check
00543 
00544 /*! @file
00545 * @brief Describes the Thread class and its AutoThread templated subclass
00546 * @author ejt (Creator)
00547 *
00548 * $Author: ejt $
00549 * $Name: tekkotsu-4_0 $
00550 * $Revision: 1.32 $
00551 * $State: Exp $
00552 * $Date: 2007/11/11 05:58:13 $
00553 */

Tekkotsu v4.0
Generated Thu Nov 22 00:54:56 2007 by Doxygen 1.5.4