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 #include "Shared/StackTrace.h"
00006 #include "Shared/MarkScope.h"
00007 
00008 #include <pthread.h>
00009 #include <string.h>
00010 #include <iostream>
00011 #include <signal.h>
00012 #include <unistd.h>
00013 #include <cassert>
00014 #include <cstdio>
00015 #include <errno.h>
00016 #include <stdexcept>
00017 
00018 using namespace std;
00019 
00020 #define THREADCANCEL_SANITY_CHECKS
00021 
00022 /*! @cond INTERNAL */
00023 //! provides the system-dependent implementation of a thread
00024 struct Threadstorage_t {
00025   //! constructor
00026   Threadstorage_t() : threadInfo(), hasThread(false) {}
00027   
00028   //! the main POSIX reference to the thread
00029   pthread_t threadInfo;
00030   //! Set to true when threadInfo is given to pthread_create (can't rely on 0/NULL being an invalid thread), so it can pthread_detach before overwrite
00031   /*! We'll assume joinability, leave the thread joinable (i.e. not detached) until the main Thread class either starts a new thread, is deleted, or is joined. */
00032   bool hasThread;
00033   
00034   //! storage which will be set up as a thread-specific memory value, so threads can tell themselves apart
00035   static pthread_key_t selfKey;
00036 private:
00037   Threadstorage_t(const Threadstorage_t& r); //!< don't call
00038   Threadstorage_t& operator=(const Threadstorage_t& r); //!< don't call
00039 };
00040 static const pthread_key_t INVALID_THREADKEY=(pthread_key_t)-1;
00041 pthread_key_t Threadstorage_t::selfKey=INVALID_THREADKEY;
00042 /*! @endcond */
00043 
00044 void* Thread::CANCELLED = PTHREAD_CANCELED;
00045 
00046 Thread::Thread()
00047   : pt(new Threadstorage_t), started(false), running(false), exited(false), returnValue(NULL),
00048   noCancelDepth(0), 
00049 #ifndef USE_SIGNAL_TO_CANCEL_THREAD
00050   reqIntrDepth(0),
00051 #endif
00052   cancelOrig(PTHREAD_CANCEL_ENABLE), cancelRequested(false), cancelInProgress(false),
00053   group(NULL), startTrace(NULL), startLock(), stopLock()
00054 {
00055   Thread* cur=getCurrent();
00056   if(cur!=NULL)
00057     group=cur->getGroup();
00058 }
00059 
00060 Thread::~Thread() {
00061   startLock.lock();
00062   if(started && this!=getCurrent()) {
00063     stop();
00064     join();
00065   }
00066   /*if(pt==NULL) {
00067     std::cerr << "Thread storage already deleted!?!?!" << std::endl;
00068     *(int*)NULL=0xDEADDEAD;
00069   }*/
00070   assert(pt!=NULL);
00071   if(pt->hasThread) {
00072     if(int err=pthread_detach(pt->threadInfo)) {
00073       cerr << "~Thread(), thread_detach: " << strerror(err) << endl;
00074       stacktrace::displayStackTrace(startTrace);
00075     }
00076     pt->hasThread=false;
00077   }
00078   delete pt;
00079   pt=NULL;
00080   if(startTrace!=NULL)
00081     stacktrace::freeStackTrace(startTrace);
00082 }
00083 
00084 void Thread::start() {
00085   MarkScope sl(startLock);
00086   if(started) {
00087     std::cerr << "Thread::start() -- thread is already started!\n"
00088     "   Make another instance if you want to run another copy of this thread\n"
00089     "   ** Original start:" << std::endl;
00090     stacktrace::displayStackTrace(startTrace);
00091     std::cerr << "   ** Duplicate start:" << std::endl;
00092     stacktrace::displayCurrentStackTrace();
00093     return;
00094   }
00095   if(startTrace!=NULL)
00096     stacktrace::freeStackTrace(startTrace);
00097   startTrace = stacktrace::recordStackTrace();
00098   exited=cancelRequested=false;
00099   started=true;
00100   pthread_attr_t   threadAttributes;
00101   if(int err = pthread_attr_init(&threadAttributes))
00102     cerr << "Thread start(), could not init stack attributes: " << strerror(err) << endl;
00103   const size_t REQ_STACK = 2*1024*1024; // OS X default is 512KB, let's up that to 2MB (portably)
00104   size_t stackSize=0;
00105   if(int err = pthread_attr_getstacksize(&threadAttributes, &stackSize)) 
00106     cerr << "Thread start(), get stack size: " << strerror(err) << endl;
00107   if(stackSize < REQ_STACK) {
00108     if(int err = pthread_attr_setstacksize(&threadAttributes, REQ_STACK)) 
00109       cerr << "Thread start(), set stack size: " << strerror(err) << endl;
00110   }
00111   if(pt->hasThread) {
00112     if(int err=pthread_detach(pt->threadInfo))
00113       cerr << "Thread start(), thread_detach of previous thread: " << strerror(err) << endl;
00114     pt->hasThread=false;
00115   }
00116   if(int err=pthread_create(&pt->threadInfo, &threadAttributes, launch, this))
00117     cerr << "Thread start(), pthread_create: " << strerror(err) << endl;
00118   else
00119     pt->hasThread=true;
00120 }
00121 
00122 void * Thread::run() {
00123   for(;;) {
00124     unsigned int sleeptime=runloop();
00125     if(sleeptime==-1U)
00126       return returnValue;
00127     if(sleeptime>0)
00128       usleep(sleeptime);
00129     testCancel();
00130   }
00131   // this return is just to satisfy warnings with silly compiler
00132   return returnValue; //never happens -- cancel or max sleep time would exit
00133 }
00134 
00135 Thread& Thread::interrupt() {
00136   if(!isRunning()) //can't interrupt before thread has been launched!
00137     return *this;
00138   struct sigaction sa;
00139   sa.sa_handler = handleInterrupt;
00140   if(sigemptyset(&sa.sa_mask)!=0)
00141     perror("Thread::interrupt(): clearing signal set via sigemptyset()");
00142   sa.sa_flags = 0;
00143   if(sigaction(SIGALRM,&sa,NULL)!=0)
00144     perror("Thread::interrupt(): installing interrupt handler via sigaction()");
00145   sendSignal(SIGALRM);
00146   return *this;
00147 }
00148 
00149 Thread& Thread::stop() {
00150   MarkScope l(stopLock);
00151   if(exited)
00152     return *this;
00153   if(!started && !running) {
00154     std::cerr << "Thread::stop() -- thread has not been started!" << std::endl;
00155     dereference();  // added by DST on 3/2014 to stop infinite loop
00156     // stacktrace::displayCurrentStackTrace();
00157     return *this;
00158   }
00159 #ifdef USE_PTHREAD_CANCEL
00160   if(started && !running)
00161     usleep(50000);
00162   if(started && !running)
00163     std::cerr << "Thread::stop(): Waiting for thread launch to complete (stillborn thread?)" << std::endl;
00164   while(started && !running)
00165     usleep(100000);
00166   if(!running)
00167     return *this;
00168   if(int err=pthread_cancel(pt->threadInfo))
00169     cerr << "Thread cancel(), pthread_cancel("<<pt->threadInfo<<"): " << strerror(err) << endl;
00170 #else
00171   // not using pthread_cancel, don't have to wait for launch to complete -- just set flag now
00172   cancelRequested=true;
00173 #endif
00174 #ifdef USE_SIGNAL_TO_CANCEL_THREAD
00175 #  ifndef USE_PTHREAD_CANCEL
00176   // not using pthread_cancel, if launch hasn't completed, don't wait around just to signal it.  Launch will test cancellation after calling launched().
00177   if(!running)
00178     return *this;
00179 #  endif
00180   if(noCancelDepth>0)
00181     return *this;
00182   interrupt(); // break thread out of any long sleep commands
00183 #else
00184   if(reqIntrDepth>0)
00185     interrupt();
00186 #endif
00187   return *this;
00188 }
00189 
00190 Thread& Thread::kill() {
00191   sendSignal(SIGUSR1);
00192   return *this;
00193 }
00194 
00195 Thread& Thread::murder() {
00196   if(pt->hasThread) {
00197     if(int err=pthread_detach(pt->threadInfo))
00198       cerr << "Thread kill(), thread_detach: " << strerror(err) << endl;
00199     pt->hasThread=false;
00200   }
00201   sendSignal(SIGSTOP);
00202   started=running=false;
00203   exited=true;
00204   return *this;
00205 }
00206 
00207 void Thread::sendSignal(int sig) {
00208   if(started && !running)
00209     usleep(50000);
00210   if(started && !running)
00211     std::cerr << "Thread::stop(): Waiting for thread launch to complete (stillborn thread?)" << std::endl;
00212   while(started && !running)
00213     usleep(100000);
00214   if(!isRunning())
00215     return;
00216   if(int err=pthread_kill(pt->threadInfo,sig))
00217     if(err!=ESRCH) // thread exit during send?
00218       cerr << "Thread sendSignal(), pthread_kill("<<sig<<"): " << strerror(err) << endl;
00219 }
00220 
00221 void * Thread::join() const {
00222   MarkScope l(startLock);
00223   if(!started || !pt->hasThread) // already gone?
00224     return cancelRequested ? CANCELLED : returnValue;
00225   void * ans=NULL;
00226   pthread_t cur = pt->threadInfo;
00227   pt->hasThread = false; // one way or another, don't try to detach or join again...
00228   if(int err=pthread_join(cur, &ans)) {
00229     cerr << "thread join() returned " << err << " " << strerror(err) << endl;
00230     if((err==EINVAL || err==ESRCH) && (!started || !pthread_equal(cur, pt->threadInfo))) // already gone?
00231       return cancelRequested ? CANCELLED : returnValue;
00232     cerr << "Thread join() " << err << " (" << EINVAL << ',' << ESRCH << "), pthread_join: " << strerror(err) << endl;
00233     stacktrace::displayCurrentStackTrace();
00234   }
00235   return ans;
00236 }
00237 
00238 Thread* Thread::getCurrent() {
00239   if(Threadstorage_t::selfKey==INVALID_THREADKEY) {
00240     static bool gaveError=false;
00241     if(!gaveError) {
00242       cerr << "ERROR: In Thread::getCurrent(), selfKey uninitialized; Thread::initMainThread was not called." << endl;
00243       cerr << "       (This error will only be displayed once)" << endl;
00244       gaveError=true;
00245     }
00246     return NULL;
00247   }
00248   return static_cast< Thread* >(pthread_getspecific(Threadstorage_t::selfKey));
00249 }
00250 
00251 void Thread::initMainThread() {
00252   if(int err=pthread_key_create(&Threadstorage_t::selfKey,warnSelfUndestructed))
00253     cerr << "WARNING: In Thread::initMainThread(), pthread_key_create(selfKey) returned " << strerror(err) << endl;
00254   if(int err=pthread_setspecific(Threadstorage_t::selfKey,NULL))
00255     cerr << "WARNING: In Thread::initMainThread(), pthread_setspecific(selfKey) returned " << strerror(err) << endl;
00256 }
00257 
00258 void Thread::releaseMainThread() {
00259   //handle_exit(NULL);
00260   if(Threadstorage_t::selfKey==INVALID_THREADKEY)
00261     return;
00262   if(int err=pthread_key_delete(Threadstorage_t::selfKey))
00263     cerr << "WARNING: In Thread::releaseMainThread, pthread_key_delete(selfKey) returned " << strerror(err) << endl;
00264 }
00265 
00266 void Thread::testCurrentCancel() {
00267   Thread * cur = getCurrent();
00268   if(cur==NULL) { // already gave warning in getCurrent
00269 #ifdef USE_PTHREAD_CANCEL
00270     pthread_testcancel();
00271 #endif
00272     return;
00273   }
00274 #ifdef DEBUG
00275   if(cur->noCancelDepth!=0) {
00276     cerr << "WARNING: Thread::testCancel called with noCancelDepth=="<<cur->noCancelDepth<<" (process="<<ProcessID::getID()<<", thread="<<pthread_self()<<")"<<endl;
00277     cerr << "The thread was started at:" << endl;
00278     stacktrace::displayStackTrace(cur->startTrace);
00279     cerr << "The testCancel call is from:" << endl;
00280     stacktrace::displayCurrentStackTrace();
00281   }
00282 #endif
00283   if(cur->noCancelDepth!=0 || cur->cancelInProgress)
00284     return;
00285 #ifdef USE_PTHREAD_CANCEL
00286   cur->cancelInProgress=true; // if next line engages cancellation, don't throw again during this throw!
00287   pthread_testcancel();
00288   cur->cancelInProgress=false; // phew, guess we're still running, reset flag
00289 #else
00290   if(cur->cancelRequested) {
00291     cur->cancelInProgress=true; // don't throw again during this throw!
00292     throw cancellation_exception();
00293   }
00294 #endif
00295 }
00296 
00297 void * Thread::launch(void * msg) {
00298   //cout << "Spawn thread " << pthread_self() << " from process " << ProcessID::getID() << endl;
00299   Thread* cur=static_cast<Thread*>(msg);
00300   if(cur==NULL) {
00301     cerr << "ERROR: Thread::launch with NULL msg" << endl;
00302     return NULL;
00303   }
00304 
00305   if(int err=pthread_setspecific(Threadstorage_t::selfKey,msg))
00306     cerr << "WARNING: In Thread::launch(), pthread_setspecific(selfKey) returned " << strerror(err) << endl;
00307   
00308   cur->cancelInProgress=false;
00309   
00310   //disable cancel while calling launch()
00311   if(int err=pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL))
00312     cerr << "Thread launch(), pthread_setcanceltype: " << strerror(err) << endl;
00313   ++(cur->noCancelDepth);
00314   if(signal(SIGUSR1,Thread::handle_launch_signal)==SIG_ERR)
00315     perror("Thread launch(), signal(SIGUSR1,handle_launch_signal)");
00316   cur->running=true;
00317   if(!cur->launched()) {
00318     //subclass's launch cancelled launch
00319     --(cur->noCancelDepth);
00320     handle_exit(NULL);
00321     return cur->returnValue;
00322   }
00323   --(cur->noCancelDepth);
00324   
00325   // handle_exit calls dereference(), which may delete this; store the return value since 'cur' can't be trusted
00326   // also, if this is not overwritten, indicates cancellation occurred.
00327   cur->returnValue=CANCELLED; 
00328   
00329   //These pthread functions actually define a scope between them (ugh)
00330   //I've added braces of my own to make this explicitly clear
00331   pthread_cleanup_push(Thread::handle_exit,msg); {
00332     
00333     if(signal(SIGUSR1,Thread::handle_signal)==SIG_ERR)
00334       perror("Thread launch(), signal(SIGUSR1,handle_signal)");
00335     
00336     try {
00337       if(cur->noCancelDepth==0) {
00338         //reset cancelability before run
00339         if(int err=pthread_setcancelstate(cur->cancelOrig,NULL))
00340           cerr << "Thread launch(), pthread_setcancelstate: " << strerror(err) << endl;
00341         if(int err=pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,NULL))
00342           cerr << "Thread launch(), pthread_setcanceltype: " << strerror(err) << endl;
00343         cur->testCancel();
00344       }
00345       cur->returnValue=cur->run();
00346     } catch(const Thread::cancellation_exception&) {
00347       // if on a system not using pthread_cancel, we throw this exception from testCancel to trigger stack unwinding so we can exit...
00348       // note returnedValue remains 'CANCELLED' since we excepted instead of returning
00349     }
00350     /* catch(const std::exception& e) {
00351       std::cout << "WTF is this exception? " << e.what() << std::endl;
00352       cancelDetected=true;
00353     } catch(...) {
00354       std::cout << "WTF is this unknown exception?" << std::endl;
00355     }*/
00356     
00357   } pthread_cleanup_pop(true);
00358   return cur->returnValue;
00359 }
00360 
00361 void Thread::handle_launch_signal(int /*sig*/) {
00362   handle_exit(NULL);
00363   pthread_exit(NULL);
00364 }
00365 
00366 void Thread::handle_signal(int /*sig*/) {
00367   pthread_exit(NULL);
00368 }
00369 
00370 void Thread::handle_exit(void * th) {
00371   //cout << "Cull thread " << pthread_self() << endl;
00372   Thread* cur=getCurrent();
00373   if(cur==NULL) {
00374     cerr << "ERROR: handle_exit called for a NULL thread" << endl;
00375     if(th!=NULL) {
00376       static_cast<Thread*>(th)->cancelled();
00377       static_cast<Thread*>(th)->started=static_cast<Thread*>(th)->running=false;
00378     }
00379     return;
00380   }
00381   
00382   { //scope limiting for stop lock, release lock before dereference in case it deletes this
00383     MarkScope l(cur->stopLock);
00384     if(th!=NULL && th!=cur)
00385       cerr << "WARNING: handle_exit argument does not match selfKey" << endl;
00386     if(cur->noCancelDepth!=0) {
00387       cerr << "WARNING: thread " << pthread_self() << " of ProcessID_t " << ProcessID::getID() << " exited while noCancelDepth>0 (was " << cur->noCancelDepth << ")" << endl;
00388       cerr << "         This may indicate a mutex was left locked." << endl;
00389       cur->noCancelDepth=0; // reset in case run again
00390     }
00391     if(int err=pthread_setspecific(Threadstorage_t::selfKey,NULL))
00392       cerr << "WARNING: In Thread::handle_exit(), pthread_setspecific(selfKey) returned " << err << endl;
00393     cur->cancelled();
00394     cur->started=cur->running=false;
00395     cur->exited=true;
00396   }
00397   cur->dereference();
00398 }
00399 
00400 void Thread::pushNoCancel() {
00401   Thread * cur=getCurrent();
00402   if(cur==NULL) {
00403     //cerr << "ERROR: Thread::pushNoCancel was given NULL thread by getCurrent, thread=" << pthread_self() << endl;
00404     //not so bad, indicates already canceled -- don't test cancel again, don't want to cancel-recurse
00405     if(int err=pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL))
00406       cerr << "ERROR: Thread pushNoCancel(), pthread_setcanceltype: " << strerror(err) << endl;
00407   } else {
00408     ++(cur->noCancelDepth);
00409     int previous=-1;
00410     if(int err=pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,&previous))
00411       cerr << "ERROR: Thread pushNoCancel(), pthread_setcanceltype: " << strerror(err) << endl;
00412 #ifdef THREADCANCEL_SANITY_CHECKS
00413     if(cur->noCancelDepth==1 && previous!=cur->cancelOrig)
00414       cerr << "WARNING: In Thread::pushNoCancel, cancel state was wrong (was " << previous << ", expected " << cur->cancelOrig << ")" << endl;
00415     else if(cur->noCancelDepth!=1 && previous!=PTHREAD_CANCEL_DISABLE)
00416       cerr << "WARNING: In Thread::pushNoCancel, cancel state was somehow re-enabled" << endl;
00417 #endif
00418   }
00419 }
00420 void Thread::popNoCancel(bool doTestCancel/*=true*/) {
00421   Thread * cur=getCurrent();
00422   if(cur==NULL) {
00423     //cerr << "ERROR: Thread::popNoCancel was given NULL thread by getCurrent, thread=" << pthread_self() << endl;
00424     //not so bad, indicates already canceled -- don't test cancel again, don't want to cancel-recurse
00425     return; //no point in continuing
00426   } else if(cur->noCancelDepth==0) {
00427     cerr << "ERROR: Thread::popNoCancel underflow" << endl;
00428   } else
00429     --(cur->noCancelDepth);
00430   int previous=-1;
00431   if(cur->noCancelDepth==0) {
00432     if(int err=pthread_setcancelstate(cur->cancelOrig,&previous))
00433       cerr << "ERROR: Thread popNoCancel(), pthread_setcanceltype: " << strerror(err) << endl;
00434     if(cur->cancelOrig==PTHREAD_CANCEL_ENABLE && doTestCancel)
00435       cur->testCancel(); // I thought setcancelstate(ENABLE) implied this, but apparently not
00436   }
00437 #ifdef THREADCANCEL_SANITY_CHECKS
00438   else { //still disabled, double check it
00439     if(int err=pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,&previous))
00440       cerr << "ERROR: Thread popNoCancel(), pthread_setcanceltype: " << strerror(err) << endl;
00441   }
00442   if(previous!=PTHREAD_CANCEL_DISABLE)
00443     cerr << "WARNING: In Thread::popNoCancel, cancel state was somehow re-enabled" << endl;
00444 #endif
00445 }
00446 
00447 void Thread::requestInterruptOnCancel() {
00448 #ifndef USE_SIGNAL_TO_CANCEL_THREAD
00449   Thread * cur=getCurrent();
00450   if(cur==NULL)
00451     return; //no point in continuing
00452   pushNoCancel();
00453   ++(cur->reqIntrDepth);
00454 #ifdef DEBUG
00455   if(cur->reqIntrDepth!=1)
00456     std::cerr << "WARNING: recursive Thread::requestInterruptOnCancel() " << cur->reqIntrDepth << std::endl;
00457 #endif
00458 #endif
00459 }
00460   
00461 void Thread::unrequestInterruptOnCancel() {
00462 #if !defined(USE_SIGNAL_TO_CANCEL_THREAD) || !defined(USE_TESTCANCEL_IN_INTERRUPT)
00463   Thread * cur=getCurrent();
00464   if(cur==NULL)
00465     return; //no point in continuing
00466 #endif
00467 #ifndef USE_SIGNAL_TO_CANCEL_THREAD
00468   if(cur->reqIntrDepth==0) {
00469     cerr << "ERROR: Thread::unrequestInterruptOnCancel underflow" << endl;
00470   } else {
00471     --(cur->reqIntrDepth);
00472   }
00473   popNoCancel();
00474 #endif
00475 #ifndef USE_TESTCANCEL_IN_INTERRUPT
00476   if(cur->noCancelDepth==0)
00477     cur->testCancel();
00478 #endif
00479 }
00480 
00481 void Thread::handleInterrupt(int /*signal*/) {
00482   //if(signal(SIGALRM,SIG_DFL)==SIG_ERR)
00483   //  perror("Thread::handleInterrupt(): could not re-enable signal");
00484   Thread * cur=Thread::getCurrent();
00485   if(cur==NULL) {
00486     // implies signal handler was delivered when in the process of shutting down
00487     // just give up on it silently
00488     //std::cerr << "Thread::handleInterrupt called from non-Thread" << endl;
00489     return;
00490   }
00491 #ifdef USE_TESTCANCEL_IN_INTERRUPT
00492   if(cur->noCancelDepth==0)
00493     cur->testCancel();
00494 #endif
00495   cur->interrupted();
00496 }
00497 
00498 void Thread::warnSelfUndestructed(void* msg) {
00499   cerr << "ERROR: Thread local data (selfKey) not deleted by Thread::handle_exit" << endl;
00500   Thread* cur = getCurrent();
00501   if(cur!=NULL)
00502     cerr << "       Weird, key wasn't cleared... (" << cur << ") " << cur->noCancelDepth << " locks on stack? " << endl;;
00503   if(msg==NULL) {
00504     cerr << "       Message is null, warnCancelDepthUndestructed shouldn't have been called." << endl;
00505   } else {
00506     if(cur!=NULL && cur!=msg)
00507       cerr << "       and current thread does not match msg (" << cur << " vs " << msg << ")" << endl;
00508     cur = static_cast<Thread*>(msg);
00509   }
00510   if(cur!=NULL) {
00511     //try to recover
00512     if(cur->noCancelDepth==0) {
00513       cerr << "       But at least the depth is 0" << endl;
00514     } else {
00515       cerr << "       The depth indicates there may be " << cur->noCancelDepth << " locks left in place" << endl;
00516     }
00517     cur->cancelled();
00518     cur->started=cur->running=false;
00519     pthread_setspecific(Threadstorage_t::selfKey,NULL);
00520   }
00521 }
00522 
00523 
00524 /*! @cond INTERNAL */
00525 //! This handles the actual lock implementation, which allows Lock to provide an abstract interface
00526 class Thread::Lock::LockStorage : public ReferenceCounter {
00527   friend class Thread::Condition;
00528 public:
00529   //! constructor
00530   LockStorage() : ReferenceCounter(), locklevel(0), mutex(), attr(), threadkey() {
00531     addReference();
00532     pthread_mutexattr_init(&attr);
00533     pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_RECURSIVE);
00534     pthread_mutex_init(&mutex,&attr);
00535   }
00536   //! destructor, releases any pending locks (with warning
00537   ~LockStorage() {
00538     pthread_mutexattr_destroy(&attr);
00539     pthread_mutex_destroy(&mutex);
00540     if(locklevel>1) //having one left is ok, perhaps even good (keeping the lock as it is destroyed)
00541       cerr << "WARNING: lockstorage destructed with " << locklevel << " locks still in effect" << endl;
00542     while(locklevel>0) {
00543       locklevel--;
00544       Thread::popNoCancel(false); // no testCancel because we could be in unwind from previous exception, cancellation here is non-portable (causes terminate on OS X)
00545     }
00546   }
00547   //! copy constructor (functional!) -- both locks will wind up referencing the same system resource, so this is more of an alias than a clone
00548   LockStorage(const LockStorage& ls) : ReferenceCounter(ls), locklevel(ls.locklevel), mutex(ls.mutex), attr(ls.attr), threadkey(ls.threadkey) {}
00549   //! assignment (functional!) -- both locks will wind up referencing the same system resource, so this is more of an alias than a clone
00550   LockStorage& operator=(const LockStorage& ls) { ReferenceCounter::operator=(ls); locklevel=ls.locklevel; mutex=ls.mutex; attr=ls.attr; threadkey=ls.threadkey; return *this; }
00551   
00552   //! trigger and wait for a mutual exclusion lock, recursively
00553   void lock() {
00554     if(int err=pthread_mutex_lock(&mutex)) {
00555       cerr << "ERROR: Thread::Lock::lock() failed: " << strerror(err) << endl;
00556     } else {
00557       locklevel++;
00558     }
00559   }
00560   //! attempt to get a lock, but return false if it is not immediately available
00561   bool trylock() {
00562     if(!pthread_mutex_trylock(&mutex)) {
00563       locklevel++;
00564       return true;
00565     } else {
00566       return false;
00567     }
00568   }
00569   //! release a lock (recursively, won't actually release the lock resource until all calls to lock() have been balanced)
00570   void unlock() {
00571     if(locklevel==0) {
00572       cerr << "ERROR: Thread::Lock::unlock() underflow" << endl;
00573       stacktrace::displayCurrentStackTrace();
00574     }
00575     locklevel--;
00576     if(int err=pthread_mutex_unlock(&mutex))
00577       cerr << "ERROR: Thread::Lock::unlock() failed: " << strerror(err) << endl;
00578   }
00579   //! returns the depth of the lock recursion (#locklevel)
00580   unsigned int getLockLevel() { return locklevel; }
00581   
00582 protected:
00583   unsigned int locklevel; //!< depth of lock recursion (i.e. number of calls to lock() minus calls to unlock())
00584   pthread_mutex_t mutex; //!< system lock resource
00585   pthread_mutexattr_t attr; //!< system lock resource attributes (used to specify #mutex is recursive in the system as well)
00586   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
00587 };
00588 
00589 Thread::Lock::LockStorage* Thread::Lock::glock=NULL;
00590 /*! @endcond */
00591 
00592 Thread::Lock::Lock() : mylock(new LockStorage), locklevel(0) {
00593   if(glock==NULL)
00594     setup();
00595 }
00596 /*Thread::Lock::Lock(const Lock& l)
00597   : mylock(l.mylock), locklevel(0)
00598 {
00599   glock->lock();
00600   mylock->addReference();
00601   glock->unlock();
00602   lock();
00603 }
00604 Thread::Lock::Lock(const Lock& l, bool autolock)
00605   : mylock(l.mylock), locklevel(0)
00606 {
00607   glock->lock();
00608   mylock->addReference();
00609   glock->unlock();
00610   if(autolock)
00611     lock();
00612 }
00613 Thread::Lock& Thread::Lock::operator=(const Lock& l) {
00614   glock->lock();
00615   lock();
00616   if(locklevel>2)
00617     cerr << "WARNING: Thread::Lock overwritten with "<<locklevel<<" locks still in effect" << endl;
00618   if(!mylock->removeReference())
00619     while(locklevel>0)
00620       unlock();
00621   mylock=l.mylock;
00622   locklevel=0;
00623   glock->unlock();
00624   return *this;
00625 }*/
00626 Thread::Lock::~Lock() {
00627   glock->lock();
00628   if(locklevel>1)
00629     cerr << "WARNING: Thread::Lock destructed with "<<locklevel<<" locks still in effect" << endl;
00630   if(!mylock->removeReference()) {
00631     std::cerr << "Lock was deleted with external reference?" << std::endl;
00632     stacktrace::displayCurrentStackTrace();
00633     while(locklevel>0)
00634       unlock();
00635   }
00636   mylock=NULL;
00637   glock->unlock();
00638 }
00639 
00640 void Thread::Lock::useResource(Resource::Data&) {
00641   mylock->lock();
00642   locklevel++;
00643 }
00644 bool Thread::Lock::trylock() {
00645   Thread::pushNoCancel();
00646   if(mylock->trylock()) {
00647     locklevel++;
00648     return true;
00649   } else {
00650     Thread::popNoCancel();
00651     return false;
00652   }
00653 }
00654 void Thread::Lock::releaseResource(Resource::Data&) {
00655   locklevel--;
00656   mylock->unlock();
00657 }
00658 unsigned int Thread::Lock::getLockLevel() const {
00659   return mylock->getLockLevel();
00660 }
00661 void Thread::Lock::setup() {
00662   if(glock==NULL)
00663     glock=new LockStorage;
00664 }
00665 
00666 /*! @cond INTERNAL */
00667 //! Implement system-dependent portion of a thread condition, a signaling mechanism.
00668 /*! This is a very basic wrapper -- just adds a constructor and destructor to the POSIX pthread_cond_t. */
00669 class Thread::Condition::ConditionStorage {
00670 public:
00671   //! constructor
00672   ConditionStorage() : cond() {
00673     if(int err=pthread_cond_init(&cond,NULL)) {
00674       cerr << "ERROR: Thread::Condition::ConditionStorage() failed: " << strerror(err) << endl;
00675     }
00676   }
00677   //! destructor
00678   ~ConditionStorage() {
00679     if(int err=pthread_cond_destroy(&cond)) {
00680       cerr << "ERROR: Thread::Condition::~ConditionStorage() failed: " << strerror(err) << endl;
00681     }
00682   }
00683   //! system resource storage
00684   pthread_cond_t cond;
00685 };
00686 /*! @endcond */
00687 
00688 Thread::Condition::Condition() : mycond(new ConditionStorage) {}
00689 Thread::Condition::~Condition() { delete mycond; mycond=NULL; }
00690 
00691 void Thread::Condition::broadcast() const {
00692   if(int err=pthread_cond_broadcast(&mycond->cond)) {
00693     cerr << "ERROR: Thread::Condition::broadcast() failed: " << strerror(err) << endl;
00694   }
00695 }
00696 void Thread::Condition::signal() const {
00697   if(int err=pthread_cond_signal(&mycond->cond)) {
00698     cerr << "ERROR: Thread::Condition::signal() failed: " << strerror(err) << endl;
00699   }
00700 }
00701 bool Thread::Condition::timedwait(Lock& l, const timespec* abstime, bool noWarn/*=false*/) const {
00702 #ifdef USE_SIGNAL_TO_CANCEL_THREAD
00703   Thread::testCurrentCancel();
00704 #endif
00705   unsigned int locklevel=l.mylock->locklevel;
00706   if(locklevel==1) {
00707     // no-op for common case to shortcut other tests
00708   } else if(locklevel>1) {
00709     if(!noWarn)
00710       displayRecursiveLockWarning("timedwait",locklevel);
00711     while(l.mylock->locklevel>1)
00712       l.mylock->unlock();
00713   } else { // 0
00714     throw std::logic_error("Thread::Condition::timedwait() called without holding lock");
00715   }
00716   if(int err=pthread_cond_timedwait(&mycond->cond,&l.mylock->mutex,abstime)) {
00717     if(err!=ETIMEDOUT)
00718       cerr << "ERROR: Thread::Condition::timedwait() failed: " << strerror(err) << endl;
00719     while(l.mylock->locklevel<locklevel)
00720       l.mylock->lock();
00721     return false;
00722   }
00723   while(l.getLockLevel()<locklevel)
00724     l.mylock->lock();
00725 #ifdef USE_SIGNAL_TO_CANCEL_THREAD
00726   Thread::testCurrentCancel();
00727 #endif
00728   return true;
00729 }
00730 void Thread::Condition::wait(Lock& l, bool noWarn/*=false*/) const {
00731 #ifdef USE_SIGNAL_TO_CANCEL_THREAD
00732   Thread::testCurrentCancel();
00733 #endif
00734   unsigned int locklevel=l.mylock->locklevel;
00735   if(locklevel==1) {
00736     // no-op for common case to shortcut other tests
00737   } else if(locklevel>1) {
00738     if(!noWarn)
00739       displayRecursiveLockWarning("wait",locklevel);
00740     while(l.mylock->locklevel>1)
00741       l.mylock->unlock();
00742   } else { // 0
00743     throw std::logic_error("Thread::Condition::wait() called without holding lock");
00744   }
00745   if(int err=pthread_cond_wait(&mycond->cond,&l.mylock->mutex)) {
00746     cerr << "ERROR: Thread::Condition::wait() failed: " << strerror(err) << endl;
00747   }
00748   while(l.getLockLevel()<locklevel)
00749     l.mylock->lock();
00750 #ifdef USE_SIGNAL_TO_CANCEL_THREAD
00751   Thread::testCurrentCancel();
00752 #endif
00753 }
00754 
00755 void Thread::Condition::displayRecursiveLockWarning(const char * fn, unsigned int locklevel) {
00756   std::cerr << "WARNING: Thread::Condition::"<<fn<<"() called holding a recursive lock. (depth " << locklevel << ")\n"
00757   "  You should verify outer lock scopes are safe to temporarily free during wait,\n"
00758   "  then pass 'true' for the noWarn argument to timedwait() to disable this message." << std::endl;
00759   stacktrace::displayCurrentStackTrace();
00760 }
00761 
00762 
00763 #endif // PLATFORM check
00764 
00765 /*! @file
00766 * @brief Describes the Thread class and its AutoThread templated subclass
00767 * @author ejt (Creator)
00768 */

Tekkotsu v5.1CVS
Generated Mon May 9 04:58:52 2016 by Doxygen 1.6.3