feisty meow concerns codebase  2.140
ethread.cpp
Go to the documentation of this file.
1 /*****************************************************************************\
2 * *
3 * Name : ethread *
4 * Author : Chris Koeritz *
5 * *
6 *******************************************************************************
7 * Copyright (c) 1998-$now By Author. This program is free software; you can *
8 * redistribute it and/or modify it under the terms of the GNU General Public *
9 * License as published by the Free Software Foundation; either version 2 of *
10 * the License or (at your option) any later version. This is online at: *
11 * http://www.fsf.org/copyleft/gpl.html *
12 * Please send any updates to: fred@gruntose.com *
13 \*****************************************************************************/
14 
15 #include "ethread.h"
16 
18 #include <basis/astring.h>
19 #include <basis/functions.h>
20 #include <basis/guards.h>
24 #include <timely/time_control.h>
25 
26 //#ifdef _MSC_VER
27 // #include <process.h>
28 //#elif defined(__UNIX__) || defined(__GNU_WINDOWS__)
29  #include <pthread.h>
30 //#else
31  //#error unknown OS for thread support.
32 //#endif
33 
34 using namespace basis;
35 using namespace loggers;
36 using namespace structures;
37 using namespace timely;
38 
39 //#define COUNT_THREADS
40  // if this is enabled, then threads will be counted when they are created
41  // or destroyed.
42 
43 #undef LOG
44 #define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), s)
45 
46 namespace processes {
47 
48 const int MAXIMUM_CREATE_ATTEMPTS = 20;
49  // the number of retries we allow to try creating a thread, if the first
50  // attempt fails.
51 
52 const int MINIMUM_SLEEP_PERIOD = 10;
53  // this is the smallest time we'll sleep for if we're slack.
54 
55 const int MAXIMUM_SLEEP_PERIOD = 200;
56  // the number of milliseconds we use for breaking up longer sleep periods.
57 
58 const int SNOOZE_FOR_RETRY = 100;
59  // how long to sleep when a thread creation fails.
60 
61 #ifdef COUNT_THREADS
62  // singleton thread counter code.
63  class thread_counter : public virtual root_object {
64  public:
65  thread_counter() : _count(0) {}
66  DEFINE_CLASS_NAME("thread_counter");
67  void increment() {
68  auto_synchronizer l(_lock);
69  _count++;
70  }
71  void decrement() {
72  auto_synchronizer l(_lock);
73  _count--;
74  }
75  private:
76  int _count;
77  mutex _lock;
78  };
79 
80  SAFE_STATIC(thread_counter, _current_threads, )
81 
82 //hmmm: this seems to not be used anywhere yet. it needs to be accessible
83 // externally if it's going to serve any useful purpose.
84 
85 #endif
86 
87 ethread::ethread()
88 : _thread_ready(false),
89  _thread_active(false),
90  _stop_thread(false),
91  _data(NULL_POINTER),
92 //#ifdef _MSC_VER
93 // _handle(0),
94 //#else
95  _handle(new pthread_t),
96 //#endif
97  _sleep_time(0),
98  _periodic(false),
99  _next_activation(new time_stamp),
100  _how(TIGHT_INTERVAL) // unused.
101 {
102  FUNCDEF("constructor [one-shot]");
103 }
104 
106 : _thread_ready(false),
107  _thread_active(false),
108  _stop_thread(false),
109  _data(NULL_POINTER),
110 //#ifdef _MSC_VER
111 // _handle(0),
112 //#else
113  _handle(new pthread_t),
114 //#endif
115  _sleep_time(sleep_timer),
116  _periodic(true),
117  _next_activation(new time_stamp),
118  _how(how)
119 {
120  FUNCDEF("constructor [periodic]");
121  if (sleep_timer < MINIMUM_SLEEP_PERIOD) {
122  _sleep_time = MINIMUM_SLEEP_PERIOD;
123  }
124 }
125 
127 {
128  stop();
129  WHACK(_next_activation);
130 //#ifndef _MSC_VER
131  WHACK(_handle);
132 //#endif
133 }
134 
136 
138 
139 // the reschedule operation assumes that assignment to a time stamp
140 // object (based on a real numbers) happens indivisibly.
141 void ethread::reschedule(int delay)
142 {
143  *_next_activation = time_stamp(delay); // start after the delay.
144 }
145 
146 bool ethread::start(void *thread_data)
147 {
148  FUNCDEF("start");
149  if (!thread_finished()) return false; // already running.
150  _data = thread_data; // store the thread's data pointer.
151  _stop_thread = false; // don't stop now.
152  _thread_ready = true; // we're starting it now.
153  _next_activation->reset(); // make "now" the next time to activate.
154  bool success = false;
155  int error = 0;
156  int attempts = 0;
157  while (attempts++ < MAXIMUM_CREATE_ATTEMPTS) {
158 //#ifndef _MSC_VER
159  pthread_attr_t attribs; // special flags for creation of thread.
160  int aret = pthread_attr_init(&attribs);
161  if (aret) LOG("failed to init attribs.");
162  aret = pthread_attr_setdetachstate(&attribs, PTHREAD_CREATE_DETACHED);
163  if (aret) LOG("failed to set detach state.");
164  int ret = -1;
165  if (_periodic)
166  ret = pthread_create(_handle, &attribs, periodic_thread_driver,
167  (void *)this);
168  else
169  ret = pthread_create(_handle, &attribs, one_shot_thread_driver,
170  (void *)this);
171  if (!ret) success = true;
172  else error = ret;
173 /*
174 #else
175  if (_periodic)
176  _handle = _beginthread(periodic_thread_driver, 0, (void *)this);
177  else
178  _handle = _beginthread(one_shot_thread_driver, 0, (void *)this);
179  if (_handle != -1) success = true;
180  else error = critical_events::system_error();
181 #endif
182 */
183  if (success) break; // got it created.
184  LOG("failed to create thread; trying again...");
185  time_control::sleep_ms(SNOOZE_FOR_RETRY);
186  }
187  if (!success) {
188  // couldn't start it, so reset our state.
189  LOG(astring("failed to create thread, error is ")
190  + critical_events::system_error_text(error));
191  exempt_stop();
192  return false;
193  }
194  return true;
195 }
196 
198 {
199  cancel(); // tell thread to leave.
200  if (!thread_started()) return; // not running.
201  while (!thread_finished()) {
202 /*
203 #ifdef _MSC_VER
204  int result = 0;
205  if (!GetExitCodeThread((HANDLE)_handle, (LPDWORD)&result)
206  || (result != STILL_ACTIVE)) {
207  exempt_stop();
208  break;
209  }
210 #endif
211 */
212  time_control::sleep_ms(10); // wait for thread to leave.
213  }
214 }
215 
217 {
218  _thread_active = false;
219  _thread_ready = false;
220 //#ifdef _MSC_VER
221 // _handle = 0;
222 //#endif
223 }
224 
225 //#if defined(__UNIX__) || defined(__GNU_WINDOWS__)
226 void *ethread::one_shot_thread_driver(void *hidden_pointer)
227 //#elif defined(_MSC_VER)
228 //void ethread::one_shot_thread_driver(void *hidden_pointer)
229 //#else
230 //#error unknown thread signature.
231 //#endif
232 {
233  FUNCDEF("one_shot_thread_driver");
234  ethread *manager = (ethread *)hidden_pointer;
235 //#ifndef _MSC_VER
236  if (!manager) return NULL_POINTER;
237 //#else
238  //if (!manager) return;
239 //#endif
240 #ifdef COUNT_THREADS
241  _current_threads().increment();
242 #endif
244  manager->_thread_active = true;
245  manager->perform_activity(manager->_data);
247  manager->exempt_stop();
248 #ifdef COUNT_THREADS
249  _current_threads().decrement();
250 #endif
251 //#ifndef _MSC_VER
252  pthread_exit(NULL_POINTER);
253  return NULL_POINTER;
254 //#else
255  //_endthread();
256 //#endif
257 }
258 
259 //#if defined(__UNIX__) || defined(__GNU_WINDOWS__)
260 void *ethread::periodic_thread_driver(void *hidden_pointer)
261 //#elif defined(_MSC_VER)
262 //void ethread::periodic_thread_driver(void *hidden_pointer)
263 //#else
264 //#error unknown thread signature.
265 //#endif
266 {
267  FUNCDEF("periodic_thread_driver");
268  ethread *manager = (ethread *)hidden_pointer;
269 //#if defined(__UNIX__) || defined(__GNU_WINDOWS__)
270  if (!manager) return NULL_POINTER;
271 //#elif defined(_MSC_VER)
272 // if (!manager) return;
273 //#endif
274 #ifdef COUNT_THREADS
275  _current_threads().increment();
276 #endif
278 
279  while (!manager->_stop_thread) {
280  // for TIGHT_INTERVAL, we reset the next active time here. this is safe
281  // relative to the reschedule() method, since we're about to do
282  // perform_activity() right now anyway. this brings about a pretty hard
283  // interval; if perform_activity() takes N milliseconds, then there will
284  // only be sleep_time - N (min zero) ms before the next invocation.
285  if (manager->_how == TIGHT_INTERVAL)
286  *manager->_next_activation = time_stamp(manager->_sleep_time);
287 
288  manager->_thread_active = true;
289  manager->perform_activity(manager->_data);
290  manager->_thread_active = false;
291 
292  // SLACK_INTERVAL means between activations. we reset the next activation
293  // here to ensure we wait the period specified for sleep time, including
294  // whatever time was taken for the activity itself.
295  if (manager->_how == SLACK_INTERVAL)
296  *manager->_next_activation = time_stamp(manager->_sleep_time);
297 
298  // we do the sleep timing in chunks so that there's not such a huge wait
299  // when the user stops the thread before the sleep interval elapses.
300  // snooze until time for the next activation.
301  while (!manager->_stop_thread) {
302  int time_diff = int(manager->_next_activation->value()
303  - time_stamp().value());
304  if (time_diff < 0) time_diff = 0; // time keeps on slipping.
305  // make sure we take our time if we're slack intervalled.
306  if (manager->_how == SLACK_INTERVAL) {
307  if (time_diff < MINIMUM_SLEEP_PERIOD)
308  time_diff = MINIMUM_SLEEP_PERIOD;
309  }
310  if (time_diff > MAXIMUM_SLEEP_PERIOD)
311  time_diff = MAXIMUM_SLEEP_PERIOD;
312  if (!manager->_stop_thread)
313  time_control::sleep_ms(time_diff);
314  if (time_stamp() >= *manager->_next_activation)
315  break;
316  }
317  }
319  manager->exempt_stop();
320 #ifdef COUNT_THREADS
321  _current_threads().decrement();
322 #endif
323 //#ifndef _MSC_VER
324  pthread_exit(NULL_POINTER);
325  return NULL_POINTER;
326 //#else
327  //_endthread();
328 //#endif
329 }
330 
331 } //namespace.
332 
Provides a dynamically resizable ASCII character string.
Definition: astring.h:35
auto_synchronizer simplifies concurrent code by automatically unlocking.
Definition: mutex.h:113
Provides a platform-independent object for adding threads to a program.
Definition: ethread.h:36
bool start(void *thread_data)
causes the thread to start, if it has not already been started.
Definition: ethread.cpp:146
void cancel()
stops the thread but does not wait until it has terminated.
Definition: ethread.h:84
virtual void perform_activity(void *thread_data)=0
< invoked just after after start(), when the OS thread is created.
bool thread_started() const
returns true if the thread has been started.
Definition: ethread.h:122
void exempt_stop()
this special form of stop() does not wait for the thread to exit.
Definition: ethread.cpp:216
bool thread_finished() const
returns true if the thread has exited.
Definition: ethread.h:125
void stop()
tells the thread to shutdown and waits for the shutdown to occur.
Definition: ethread.cpp:197
ethread()
creates a single-shot thread object.
Definition: ethread.cpp:87
void reschedule(int delay=0)
causes a periodic thread to activate after "delay" milliseconds from now.
Definition: ethread.cpp:141
virtual ~ethread()
Definition: ethread.cpp:126
Represents a point in time relative to the operating system startup time.
Definition: time_stamp.h:38
void reset()
sets the stamp time back to now.
Definition: time_stamp.cpp:59
#define NULL_POINTER
The value representing a pointer to nothing.
Definition: definitions.h:32
#define DEFINE_CLASS_NAME(objname)
Defines the name of a class by providing a couple standard methods.
Definition: enhance_cpp.h:45
#define FUNCDEF(func_in)
FUNCDEF sets the name of a function (and plugs it into the callstack).
Definition: enhance_cpp.h:57
#define LOG(s)
Definition: ethread.cpp:44
The guards collection helps in testing preconditions and reporting errors.
Definition: array.h:30
void WHACK(contents *&ptr)
deletion with clearing of the pointer.
Definition: functions.h:121
A logger that sends to the console screen using the standard output device.
const int SNOOZE_FOR_RETRY
Definition: ethread.cpp:58
const int MINIMUM_SLEEP_PERIOD
Definition: ethread.cpp:52
const int MAXIMUM_SLEEP_PERIOD
Definition: ethread.cpp:55
const int MAXIMUM_CREATE_ATTEMPTS
Definition: ethread.cpp:48
A dynamic container class that holds any kind of object via pointers.
Definition: amorph.h:55
#include <time.h>
Definition: earth_time.cpp:37
#define SAFE_STATIC(type, func_name, parms)
Statically defines a singleton object whose scope is the program's lifetime.
Aids in achievement of platform independence.