ithread.cpp

Go to the documentation of this file.
00001 #ifndef ITHREAD_IMPLEMENTATION_FILE
00002 #define ITHREAD_IMPLEMENTATION_FILE
00003 
00004 /*****************************************************************************\
00005 *                                                                             *
00006 *  Name   : ithread                                                           *
00007 *  Author : Chris Koeritz                                                     *
00008 *                                                                             *
00009 *******************************************************************************
00010 * Copyright (c) 1998-$now By Author.  This program is free software; you can  *
00011 * redistribute it and/or modify it under the terms of the GNU General Public  *
00012 * License as published by the Free Software Foundation; either version 2 of   *
00013 * the License or (at your option) any later version.  This is online at:      *
00014 *     http://www.fsf.org/copyleft/gpl.html                                    *
00015 * Please send any updates to: fred@gruntose.com                               *
00016 \*****************************************************************************/
00017 
00018 #include "ithread.h"
00019 #include "time_stamp.h"
00020 
00021 #include <basis/function.h>
00022 #include <basis/guards.h>
00023 #include <basis/istring.h>
00024 #include <basis/log_base.h>
00025 #include <basis/portable.h>
00026 #include <data_struct/static_memory_gremlin.h>
00027 
00028 #ifdef __WIN32__
00029   #include <process.h>
00030 #elif defined(__UNIX__)
00031   #include <pthread.h>
00032 #else
00033   #error unknown OS for thread support.
00034 #endif
00035 
00036 //#define COUNT_THREADS
00037   // if this is enabled, then threads will be counted when they are created
00038   // or destroyed.
00039 
00040 #undef LOG
00041 #define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger(), s)
00042 
00043 const int MAXIMUM_CREATE_ATTEMPTS = 20;
00044   // the number of retries we allow to try creating a thread, if the first
00045   // attempt fails.
00046 
00047 const int MINIMUM_SLEEP_PERIOD = 10;
00048   // this is the smallest time we'll sleep for if we're slack.
00049 
00050 const int MAXIMUM_SLEEP_PERIOD = 200;
00051   // the number of milliseconds we use for breaking up longer sleep periods.
00052 
00053 const int SNOOZE_FOR_RETRY = 100;
00054   // how long to sleep when a thread creation fails.
00055 
00056 #ifdef COUNT_THREADS
00057   // singleton thread counter code.
00058   class thread_counter : public virtual object_base {
00059   public:
00060     thread_counter() : _count(0) {}
00061     IMPLEMENT_CLASS_NAME("thread_counter");
00062     void increment() { 
00063       auto_synchronizer l(_lock);
00064       _count++;
00065     }
00066     void decrement() {
00067       auto_synchronizer l(_lock);
00068       _count--;
00069     }
00070   private:
00071     int _count;
00072     mutex _lock;
00073   };
00074 
00075   SAFE_STATIC(thread_counter, _current_threads, )
00076 
00077 //hmmm: this seems to not be used anywhere yet.  it needs to be accessible
00078 //      externally if it's going to serve any useful purpose.
00079 
00080 #endif
00081 
00082 ithread::ithread()
00083 : _thread_ready(false),
00084   _thread_active(false),
00085   _stop_thread(false),
00086   _data(NIL),
00087 #ifdef __UNIX__
00088   _handle(new pthread_t),
00089 #elif defined(__WIN32__)
00090   _handle(0),
00091 #endif
00092   _sleep_time(0),
00093   _periodic(false),
00094   _next_activation(new time_stamp),
00095   _how(TIGHT_INTERVAL)  // unused.
00096 {
00097   FUNCDEF("constructor [one-shot]");
00098 }
00099 
00100 ithread::ithread(int sleep_timer, timed_thread_types how)
00101 : _thread_ready(false),
00102   _thread_active(false),
00103   _stop_thread(false),
00104   _data(NIL),
00105 #ifdef __UNIX__
00106   _handle(new pthread_t),
00107 #elif defined(__WIN32__)
00108   _handle(0),
00109 #endif
00110   _sleep_time(sleep_timer),
00111   _periodic(true),
00112   _next_activation(new time_stamp),
00113   _how(how)
00114 {
00115   FUNCDEF("constructor [periodic]");
00116   if (sleep_timer < MINIMUM_SLEEP_PERIOD) {
00117     _sleep_time = MINIMUM_SLEEP_PERIOD;
00118   }
00119 }
00120 
00121 ithread::~ithread()
00122 {
00123   stop();
00124   WHACK(_next_activation);
00125 #ifdef __UNIX__
00126   WHACK(_handle);
00127 #endif
00128 }
00129 
00130 void ithread::pre_thread() {}
00131 
00132 void ithread::post_thread() {}
00133 
00134 // both of the reschedule operations assume that assignment to a time stamp
00135 // object (based on a real numbers) happens indivisibly.
00136 
00137 void ithread::reschedule()
00138 {
00139   reschedule(-1);  // start immediately.
00140 }
00141 
00142 void ithread::reschedule(int delay)
00143 {
00144   *_next_activation = time_stamp(delay);  // start after the delay.
00145 }
00146 
00147 bool ithread::start(void *thread_data)
00148 {
00149   FUNCDEF("start");
00150   if (!thread_finished()) return false;  // already running.
00151   _data = thread_data;  // store the thread's data pointer.
00152   _stop_thread = false;  // don't stop now.
00153   _thread_ready = true;  // we're starting it now.
00154   _next_activation->reset();  // make "now" the next time to activate.
00155   bool success = false;
00156   int error = 0;
00157   int attempts = 0;
00158   while (attempts++ < MAXIMUM_CREATE_ATTEMPTS) {
00159 #ifdef __UNIX__
00160     pthread_attr_t attribs;  // special flags for creation of thread.
00161     int aret = pthread_attr_init(&attribs);
00162     if (aret) LOG("failed to init attribs.");
00163     aret = pthread_attr_setdetachstate(&attribs, PTHREAD_CREATE_DETACHED);
00164     if (aret) LOG("failed to set detach state.");
00165     int ret = -1;
00166     if (_periodic)
00167       ret = pthread_create(_handle, &attribs, periodic_thread_driver,
00168           (void *)this);
00169     else
00170       ret = pthread_create(_handle, &attribs, one_shot_thread_driver,
00171           (void *)this);
00172     if (!ret) success = true;
00173     else error = ret;
00174 #elif defined(__WIN32__)
00175     if (_periodic)
00176       _handle = _beginthread(periodic_thread_driver, 0, (void *)this);
00177     else
00178       _handle = _beginthread(one_shot_thread_driver, 0, (void *)this);
00179     if (_handle != -1) success = true;
00180     else error = portable::system_error();
00181 #endif
00182     if (success) break;  // got it created.
00183     LOG("failed to create thread; trying again...");
00184     portable::sleep_ms(SNOOZE_FOR_RETRY);
00185   }
00186   if (!success) {
00187     // couldn't start it, so reset our state.
00188     LOG(istring("failed to create thread, error is ")
00189         + portable::system_error_text(error));
00190     exempt_stop();
00191     return false;
00192   }
00193   return true;
00194 }
00195 
00196 void ithread::stop()
00197 {
00198   cancel();  // tell thread to leave.
00199   if (!thread_started()) return;  // not running.
00200   while (!thread_finished()) {
00201 #ifdef __WIN32__
00202     int result = 0;
00203     if (!GetExitCodeThread((HANDLE)_handle, (LPDWORD)&result)
00204         || (result != STILL_ACTIVE)) {
00205       exempt_stop();
00206       break;
00207     }
00208 #endif
00209     portable::sleep_ms(10);  // wait for thread to leave.
00210   }
00211 }
00212 
00213 void ithread::exempt_stop()
00214 {
00215   _thread_active = false;
00216   _thread_ready = false;
00217 #ifdef __WIN32__
00218   _handle = 0;
00219 #endif
00220 }
00221 
00222 #ifdef __UNIX__
00223 void *ithread::one_shot_thread_driver(void *hidden_pointer)
00224 #elif defined(__WIN32__)
00225 void ithread::one_shot_thread_driver(void *hidden_pointer)
00226 #else
00227 #error unknown thread signature.
00228 #endif
00229 {
00230   FUNCDEF("one_shot_thread_driver");
00231   ithread *manager = (ithread *)hidden_pointer;
00232 #ifdef __UNIX__
00233   if (!manager) return NIL;
00234 #else
00235   if (!manager) return;
00236 #endif
00237 #ifdef COUNT_THREADS
00238   _current_threads().increment();
00239 #endif
00240   manager->pre_thread();
00241   manager->_thread_active = true;
00242   manager->perform_activity(manager->_data);
00243   manager->post_thread();
00244   manager->exempt_stop();
00245 #ifdef COUNT_THREADS
00246   _current_threads().decrement();
00247 #endif
00248 #ifdef __UNIX__
00249   pthread_exit(NIL);
00250   return NIL;
00251 #else
00252   _endthread();
00253 #endif
00254 }
00255 
00256 #ifdef __UNIX__
00257 void *ithread::periodic_thread_driver(void *hidden_pointer)
00258 #elif defined(__WIN32__)
00259 void ithread::periodic_thread_driver(void *hidden_pointer)
00260 #else
00261 #error unknown thread signature.
00262 #endif
00263 {
00264   FUNCDEF("periodic_thread_driver");
00265   ithread *manager = (ithread *)hidden_pointer;
00266 #ifdef __UNIX__
00267   if (!manager) return NIL;
00268 #elif defined(__WIN32__)
00269   if (!manager) return;
00270 #endif
00271 #ifdef COUNT_THREADS
00272   _current_threads().increment();
00273 #endif
00274   manager->pre_thread();
00275 
00276   while (!manager->_stop_thread) {
00277     // for TIGHT_INTERVAL, we reset the next active time here.  this is safe
00278     // relative to the reschedule() method, since we're about to do
00279     // perform_activity() right now anyway.  this brings about a pretty hard
00280     // interval; if perform_activity() takes N milliseconds, then there will
00281     // only be sleep_time - N (min zero) ms before the next invocation.
00282     if (manager->_how == TIGHT_INTERVAL)
00283       *manager->_next_activation = time_stamp(manager->_sleep_time);
00284 
00285     manager->_thread_active = true;
00286     manager->perform_activity(manager->_data);
00287     manager->_thread_active = false;
00288 
00289     // SLACK_INTERVAL means between activations.  we reset the next activation
00290     // here to ensure we wait the period specified for sleep time, including
00291     // whatever time was taken for the activity itself.
00292     if (manager->_how == SLACK_INTERVAL)
00293       *manager->_next_activation = time_stamp(manager->_sleep_time);
00294 
00295     // we do the sleep timing in chunks so that there's not such a huge wait
00296     // when the user stops the thread before the sleep interval elapses.
00297     // snooze until time for the next activation.
00298     while (!manager->_stop_thread) {
00299       int time_diff = int(manager->_next_activation->value()
00300           - time_stamp().value());
00301       if (time_diff < 0) time_diff = 0;  // time keeps on slipping.
00302       // make sure we take our time if we're slack intervalled.
00303       if (manager->_how == SLACK_INTERVAL) {
00304         if (time_diff < MINIMUM_SLEEP_PERIOD)
00305           time_diff = MINIMUM_SLEEP_PERIOD;
00306       }
00307       if (time_diff > MAXIMUM_SLEEP_PERIOD)
00308         time_diff = MAXIMUM_SLEEP_PERIOD;
00309       if (!manager->_stop_thread)
00310         portable::sleep_ms(time_diff);
00311       if (time_stamp() >= *manager->_next_activation)
00312         break;
00313     }
00314   }
00315   manager->post_thread();
00316   manager->exempt_stop();
00317 #ifdef COUNT_THREADS
00318   _current_threads().decrement();
00319 #endif
00320 #ifdef __UNIX__
00321   pthread_exit(NIL);
00322   return NIL;
00323 #elif defined(__WIN32__)
00324   _endthread();
00325 #endif
00326 }
00327 
00328 
00329 #endif //ITHREAD_IMPLEMENTATION_FILE
00330 

Generated on Thu Nov 20 04:28:59 2008 for HOOPLE Libraries by  doxygen 1.5.1