Thread.cc

00001 // ----------------------------------------------------------------------^
00002 // Copyright (C) 2004, 2005, 2006, 2007, 2008 Giorgio Calderone
00003 // (mailto: <gcalderone@ifc.inaf.it>)
00004 // 
00005 // This file is part of MCS.
00006 // 
00007 // MCS is free software; you can redistribute it and/or modify
00008 // it under the terms of the GNU General Public License as published by
00009 // the Free Software Foundation; either version 2 of the License, or
00010 // (at your option) any later version.
00011 // 
00012 // MCS is distributed in the hope that it will be useful,
00013 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015 // GNU General Public License for more details.
00016 // 
00017 // You should have received a copy of the GNU General Public License
00018 // along with MCS; if not, write to the Free Software
00019 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00020 // 
00021 // ----------------------------------------------------------------------$
00022 
00023 #include <assert.h>
00024 
00025 #include "mcs.hh"
00026 using namespace mcs;
00027 
00028 #ifndef HAVE_PTHREAD_MUTEXATTR_SETTYPE
00029 extern int pthread_mutexattr_settype (pthread_mutexattr_t *__attr, int __kind)
00030     __THROW;
00031 #endif
00032 
00033 
00034 
00035 
00036 //--------------------------------------------------------
00037 mcs::Synchro::Synchro()
00038 {
00039   MCS_DEBUG_SETUP(0, "Synchro");
00040   MCS_DEBUG_ENTER(NOARGS);
00041 
00042   Count = 0;
00043   isActive = false;
00044 
00045   MCS_DEBUG_LEAVE(NOARGS);
00046 }
00047 
00048 
00049 mcs::Synchro::~Synchro()
00050 {
00051   MCS_DEBUG_ENTER(NOARGS);
00052 
00053   synchronize(false);
00054 
00055   MCS_DEBUG_LEAVE(NOARGS);
00056 }
00057 
00058 int mcs::Synchro::count()
00059 { return Count; }
00060 
00061 
00062 void mcs::Synchro::synchronize(bool setactive)
00063 {
00064   MCS_DEBUG_ENTER( << isActive);
00065 
00066   if (Count != 0)
00067     throw MCS_FATAL( MSG_CANT_SET_SYNCHRO_STATE );
00068 
00069   if ((! isActive)   &&   (setactive)) {
00070     pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE_NP);
00071 #ifdef HAVE_PTHREAD_MUTEXATTR_SETPROTOCOL
00072     pthread_mutexattr_setprotocol(&attr, PTHREAD_PRIO_NONE);
00073 #endif
00074     pthread_mutex_init(&mutex, &attr);
00075   }
00076 
00077   if ((isActive)   &&   (! setactive)) {
00078     int ret = pthread_mutex_destroy(&mutex);
00079     assert(ret == 0);
00080   }
00081 
00082   isActive = setactive;
00083   MCS_DEBUG_LEAVE( << isActive );
00084 }
00085 
00086 
00087 bool mcs::Synchro::enter(int op, unsigned int to)
00088 {
00089   int ret = 0;
00090 
00091   if (! isActive) return true;
00092   MCS_DEBUG_ENTER(NOARGS);
00093 
00094   switch (op) {
00095   case MCS_SYNCHRO_LOCK:
00096     ret = pthread_mutex_lock(&mutex);
00097     break;
00098   case MCS_SYNCHRO_TRY_LOCK:
00099     ret = pthread_mutex_trylock(&mutex);
00100     break;
00101   case MCS_SYNCHRO_TRY_TIMED:
00102     struct timeval now;
00103     struct timespec timeout;
00104     unsigned int millisec, seconds;
00105     millisec = to % 1000;
00106     seconds  = (unsigned int) floor((double) (to / 1000.0));
00107     gettimeofday(&now, NULL);
00108     timeout.tv_sec = now.tv_sec + seconds;
00109     timeout.tv_nsec = now.tv_usec * 1000 + millisec * 1000000;
00110 
00111     ret = pthread_mutex_timedlock(&mutex, &timeout);
00112     break;
00113   }
00114 
00115   assert((ret == 0)          ||
00116      (ret == EBUSY)      ||
00117      (ret == ETIMEDOUT)    );
00118 
00119 
00120   if (ret == 0) {
00121     Count++;
00122   }
00123 
00124 
00125   MCS_DEBUG_LEAVE(NOARGS);
00126   return (ret == 0);
00127 }
00128 
00129 
00130 
00131 int mcs::Synchro::leave()
00132 {
00133   if (! isActive) return 0;
00134   MCS_DEBUG_ENTER(<< Count);
00135 
00136   int ret;
00137   ret = pthread_mutex_unlock(&mutex);
00138 
00139   assert(ret == 0);
00140 
00141   if (ret == 0)
00142     Count--;
00143 
00144   return Count;
00145   MCS_DEBUG_LEAVE(NOARGS);
00146 }
00147 
00148 
00149 bool mcs::Synchro::tryenter(unsigned int timeout)
00150 {
00151   if (timeout == 0)
00152     return enter(MCS_SYNCHRO_TRY_LOCK);
00153   else
00154     return enter(MCS_SYNCHRO_TRY_TIMED, timeout);
00155 }
00156 
00157 
00158 
00159 //bool mcs::Synchro::wait(unsigned int to)
00160 //{
00161 //  int ret;
00162 //  bool bret;
00163 //  if (! isActive) return true;
00164 //
00165 //  //waiting = true;
00166 //
00167 //  MCS_DEBUG_ENTER(NOARGS);
00168 //
00169 //  //Always use TIMED wait.
00170 //  struct timeval now;
00171 //  struct timespec timeout;
00172 //  unsigned int millisec, seconds;
00173 //  millisec = to % 1000;
00174 //  seconds  = (unsigned int) floor((double) (to / 1000.0));
00175 //  gettimeofday(&now, NULL);
00176 //  timeout.tv_sec = now.tv_sec + seconds;
00177 //  timeout.tv_nsec = now.tv_usec * 1000 + millisec * 1000000;
00178 //
00179 //  ret = pthread_cond_timedwait(&cond, &mutex, &timeout);
00180 //  bret = (! ((ret == ETIMEDOUT)   ||   (ret == EINTR)));
00181 //
00182 //  waiting = false;
00183 //  MCS_DEBUG_LEAVE(NOARGS);
00184 //  return bret;
00185 //}
00186 
00187 
00188 
00189 
00190 
00191 
00192 
00193 
00194 //--------------------------------------------------------
00195 mcs::Thread::Thread(int id, Thread* parent) : syn_lstate()
00196 {
00197   MCS_DEBUG_SETUP(0, "Thread");
00198 
00199   lparent = parent;
00200   lid = id;
00201   lerror = NULL;
00202 
00203   lstate = MCS_STATE_CREATED;
00204   syn_lstate.synchronize(true);
00205 
00206   detached = false;
00207   selfDelete = false;
00208 
00209   MCS_DEBUG_LEAVE(<< lid);
00210 }
00211 
00212 
00213 mcs::Thread::~Thread()
00214 {
00215   MCS_DEBUG_ENTER(<< lid);
00216   if (pthread_self() != lthrID)
00217     stop();
00218 
00219   if (lerror)
00220     delete lerror;
00221   MCS_DEBUG_LEAVE(<< lid);
00222 }
00223 
00224 void mcs::Thread::initial()
00225 {}
00226 
00227 void mcs::Thread::final()
00228 {}
00229 
00230 
00231 int mcs::Thread::state()
00232 { return lstate; }
00233 
00234 int mcs::Thread::id()
00235 { return lid; }
00236 
00237 Event* mcs::Thread::error()
00238 { return lerror; }
00239 
00240 Thread* mcs::Thread::parent()
00241 { return lparent; }
00242 
00243 void mcs::Thread::cleanup_Handler(void* p)
00244 { ((Thread*) p)->final(); }
00245 
00246 
00247 void mcs::Thread::run()
00248 { throw MCS_ERROR(MSG_METHOD_MUST_BE_OVERLOADED, "Thread::run"); }
00249 
00250 void mcs::Thread::notify(int id, Thread* ref)
00251 {
00252 #if ENABLE_DEBUG
00253     char buf[10];
00254     sprintf(buf, "%d", id);
00255     string s = "Notify from " + string(buf);
00256     MCS_DEBUG(lid << " " << s.c_str());
00257 #endif
00258 }
00259 
00260 
00261 
00262 void mcs::Thread::set_cancel_state(bool cancel)
00263 {
00264   int ret;
00265   if (cancel) {
00266     ret = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
00267     assert(ret == 0);
00268     ret = pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
00269     assert(ret == 0);
00270   }
00271   else {
00272     ret = pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
00273     assert(ret == 0);
00274   }
00275 }
00276 
00277 
00278 void mcs::Thread::test_cancel()
00279 {
00280   int old, ret;
00281   ret = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &old);
00282   assert(ret == 0);
00283   ret = pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
00284   assert(ret == 0);
00285 
00286   pthread_testcancel();
00287 
00288   ret = pthread_setcancelstate(old, NULL);
00289   assert(ret == 0);
00290 }
00291 
00292 
00293 
00294 void mcs::Thread::startDetached(bool selfdelete)
00295 {
00296   detached = true;
00297   selfDelete = selfdelete;
00298   start();
00299 }
00300 
00301 
00302 void mcs::Thread::start()
00303 {
00304   int ret;
00305   MCS_DEBUG_ENTER(<< lid);
00306 
00307   ret = pthread_create(&lthrID, NULL, Thread::RunThread, this);
00308   if (ret)
00309       throw MCS_FATAL(MSG_CALLING_PTHREAD_CREATE);
00310 
00311   if (detached) {
00312     ret = pthread_detach(lthrID);
00313     if (ret != 0)
00314       throw MCS_FATAL(MSG_CALLING_PTHREAD_CREATE);
00315   }
00316 
00317   //Continue only after initial() have been executed
00318   while (lstate < MCS_STATE_RUNNING)
00319     sleep_ms(1);
00320 
00321   MCS_DEBUG_LEAVE(<< lid);
00322 }
00323 
00324 
00325 
00326 void* mcs::Thread::RunThread(void* args)
00327 {
00328   Thread* thr = (Thread*) args;
00329 
00330   //Install the cleanup handler
00331   pthread_cleanup_push(cleanup_Handler, thr);
00332 
00333   thr->set_cancel_state(true);
00334 
00335   try {
00336     thr->initial();
00337     thr->lstate = MCS_STATE_RUNNING;
00338 
00339     //From the run() method it is possible to
00340     // - return
00341     // - throw an exception
00342     // - another thread call the stop() method
00343     thr->run();
00344   }
00345   catch (Event e) {
00346     thr->lerror = new Event(e);
00347     //cerr << e.msg() << endl;
00348   }
00349 
00350   thr->set_cancel_state(false);
00351 
00352   if (thr->checkTerminating()) {
00353     pthread_exit(NULL);
00354     return NULL;
00355   }
00356 
00357   pthread_cleanup_pop(0);
00358   thr->final();
00359 
00360   thr->lstate = MCS_STATE_END;
00361 
00362   //The thread is terminating by itself and will eventually notify its
00363   //parent who won't call the stop() method. In this case the
00364   //pthread_cancel and pthread_join functions won't be called and the
00365   //resources won't be freed. Calling pthread_detach() here will
00366   //detach the thread and resources will be freed at the
00367   //pthread_exit() call.
00368   pthread_detach(thr->lthrID);
00369 
00370   if (thr->lparent)
00371     thr->lparent->notify(thr->lid, thr);
00372 
00373   if (thr->selfDelete)
00374     delete thr;
00375 
00376 
00377   pthread_exit(NULL);
00378   return NULL;
00379 }
00380 
00381 
00382 
00383 void mcs::Thread::stop()
00384 {
00385   //This method MUST not be called from the thread itself
00386   assert(pthread_self() != lthrID);
00387 
00388   if (detached)
00389     return; //ERROR: Can't stop a detached thread
00390 
00391   bool wasterminating = checkTerminating();
00392   //If the thread was already terminating the rest of the job has been
00393   //performed in the RunThread() method, so we can return.
00394   if (wasterminating)
00395     return;
00396 
00397   //If the thread is not detached then the parent thread MUST call
00398   //pthread_cancel() and pthread_join() to free resources.
00399 
00400   pthread_cancel(lthrID); //final() will be called here if the cleanup
00401               //handler is still installed
00402   pthread_join(lthrID, NULL);
00403 
00404   //Il controllo wasterminating ==> return era qui.
00405 
00406   lstate = MCS_STATE_END;
00407 
00408   if (lparent)
00409     lparent->notify(lid, this);
00410 }
00411 
00412 
00413 bool mcs::Thread::checkTerminating()
00414 {
00415   bool ret;
00416 
00417   syn_lstate.enter();
00418   ret = (lstate >= MCS_STATE_TERMINATING);
00419   if (!ret)
00420     lstate = MCS_STATE_TERMINATING;
00421   syn_lstate.leave();
00422 
00423   return ret;
00424 }
00425 
00426 
00427 
00428 
00429 
00430 mcs::ThreadFunc::ThreadFunc(int (*start_routine)(void*),
00431                 void* arg) :
00432   Thread(0, NULL)
00433 {
00434   this->start_routine1 = start_routine;
00435   this->start_routine2 = NULL;
00436   this->arg = arg;
00437   ret = 0;
00438 }
00439 
00440 mcs::ThreadFunc::ThreadFunc(Event* (*start_routine)(void*),
00441                 void* arg) :
00442   Thread(0, NULL)
00443 {
00444   this->start_routine1 = NULL;
00445   this->start_routine2 = start_routine;
00446   this->arg = arg;
00447   ret = 0;
00448 }
00449 
00450 
00451 void mcs::ThreadFunc::run()
00452 {
00453   if (start_routine1)
00454     ret = (start_routine1)(arg);
00455   else
00456     lerror = (start_routine2)(arg);
00457 }
00458 
00459 int mcs::ThreadFunc::retcode()
00460 { return ret; }
00461 
00462 
00463 
00464 
00465 
00466 
00467 
00468 void mcs::sleep_ms(unsigned int millisec)
00469 {
00470   struct timespec tt, tmp;
00471   tt.tv_sec = 0;
00472   tt.tv_nsec = 0;
00473 
00474 
00475   if (millisec >= 1000) {
00476     tt.tv_sec = ((long int) (millisec / 1000.0));
00477   }
00478 
00479   tt.tv_nsec = (millisec % 1000) * 1000000;
00480   nanosleep(&tt, &tmp);
00481 }
00482 
00483 
00484 

mcslogo

MCS (My Customizable Server) ver. 0.3.3-alpha3
Documentation generated on Thu Mar 22 13:22:23 UTC 2012