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
34using namespace basis;
35using namespace loggers;
36using namespace structures;
37using 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
46namespace processes {
47
49 // the number of retries we allow to try creating a thread, if the first
50 // attempt fails.
51
52const int MINIMUM_SLEEP_PERIOD = 10;
53 // this is the smallest time we'll sleep for if we're slack.
54const int MAXIMUM_SLEEP_PERIOD = 80;
55 // the number of milliseconds we use for breaking up longer sleep periods.
56
57const int SNOOZE_FOR_RETRY = 100;
58 // how long to sleep when a thread creation fails.
59
60#ifdef COUNT_THREADS
61 // singleton thread counter code.
62 class thread_counter : public virtual root_object {
63 public:
64 thread_counter() : _count(0) {}
65 DEFINE_CLASS_NAME("thread_counter");
66 void increment() {
67 auto_synchronizer l(_lock);
68 _count++;
69 }
70 void decrement() {
71 auto_synchronizer l(_lock);
72 _count--;
73 }
74 private:
75 int _count;
76 mutex _lock;
77 };
78
79 SAFE_STATIC(thread_counter, _current_threads, )
80
81//hmmm: this seems to not be used anywhere yet. it needs to be accessible
82// externally if it's going to serve any useful purpose.
83
84#endif
85
87: _thread_ready(false),
88 _thread_active(false),
89 _stop_thread(false),
90 _data(NULL_POINTER),
91//#ifdef _MSC_VER
92// _handle(0),
93//#else
94 _handle(new pthread_t),
95//#endif
96 _sleep_time(0),
97 _periodic(false),
98 _next_activation(new time_stamp),
99 _how(TIGHT_INTERVAL) // unused.
100{
101 FUNCDEF("constructor [one-shot]");
102}
103
105: _thread_ready(false),
106 _thread_active(false),
107 _stop_thread(false),
108 _data(NULL_POINTER),
109//#ifdef _MSC_VER
110// _handle(0),
111//#else
112 _handle(new pthread_t),
113//#endif
114 _sleep_time(sleep_timer),
115 _periodic(true),
116 _next_activation(new time_stamp),
117 _how(how)
118{
119 FUNCDEF("constructor [periodic]");
120 if (sleep_timer < MINIMUM_SLEEP_PERIOD) {
121 _sleep_time = MINIMUM_SLEEP_PERIOD;
122 }
123}
124
126{
127 stop();
128 WHACK(_next_activation);
129//#ifndef _MSC_VER
130 WHACK(_handle);
131//#endif
132}
133
135
137
138// the reschedule operation assumes that assignment to a time stamp
139// object (based on a real numbers) happens indivisibly.
140void ethread::reschedule(int delay)
141{
142 *_next_activation = time_stamp(delay); // start after the delay.
143}
144
145bool ethread::start(void *thread_data)
146{
147 FUNCDEF("start");
148 if (!thread_finished()) return false; // already running.
149 _data = thread_data; // store the thread's data pointer.
150 _stop_thread = false; // don't stop now.
151 _thread_ready = true; // we're starting it now.
152 _next_activation->reset(); // make "now" the next time to activate.
153 bool success = false;
154 int error = 0;
155 int attempts = 0;
156 while (attempts++ < MAXIMUM_CREATE_ATTEMPTS) {
157//#ifndef _MSC_VER
158 pthread_attr_t attribs; // special flags for creation of thread.
159 int aret = pthread_attr_init(&attribs);
160 if (aret) LOG("failed to init attribs.");
161 aret = pthread_attr_setdetachstate(&attribs, PTHREAD_CREATE_DETACHED);
162 if (aret) LOG("failed to set detach state.");
163 int ret = -1;
164 if (_periodic)
165 ret = pthread_create(_handle, &attribs, periodic_thread_driver, (void *)this);
166 else
167 ret = pthread_create(_handle, &attribs, one_shot_thread_driver, (void *)this);
168 if (!ret) success = true;
169 else error = ret;
170/*
171#else
172 if (_periodic)
173 _handle = _beginthread(periodic_thread_driver, 0, (void *)this);
174 else
175 _handle = _beginthread(one_shot_thread_driver, 0, (void *)this);
176 if (_handle != -1) success = true;
177 else error = critical_events::system_error();
178#endif
179*/
180 if (success) break; // got it created.
181 LOG("failed to create thread; trying again...");
183 }
184 if (!success) {
185 // couldn't start it, so reset our state.
186 LOG(astring("failed to create thread, error is ")
188 exempt_stop();
189 return false;
190 }
191 return true;
192}
193
195{
196 cancel(); // tell thread to leave.
197 if (!thread_started()) return; // not running.
198 while (!thread_finished()) {
199/*
200#ifdef _MSC_VER
201 int result = 0;
202 if (!GetExitCodeThread((HANDLE)_handle, (LPDWORD)&result)
203 || (result != STILL_ACTIVE)) {
204 exempt_stop();
205 break;
206 }
207#endif
208*/
209 time_control::sleep_ms(10); // wait for thread to leave.
210 }
211}
212
214{
215 _thread_active = false;
216 _thread_ready = false;
217//#ifdef _MSC_VER
218// _handle = 0;
219//#endif
220}
221
222//#if defined(__UNIX__) || defined(__GNU_WINDOWS__)
223void *ethread::one_shot_thread_driver(void *hidden_pointer)
224//#elif defined(_MSC_VER)
225//void ethread::one_shot_thread_driver(void *hidden_pointer)
226//#else
227//#error unknown thread signature.
228//#endif
229{
230 FUNCDEF("one_shot_thread_driver");
231 ethread *manager = (ethread *)hidden_pointer;
232//#ifndef _MSC_VER
233 if (!manager) return NULL_POINTER;
234//#else
235 //if (!manager) return;
236//#endif
237#ifdef COUNT_THREADS
238 _current_threads().increment();
239#endif
241 manager->_thread_active = true;
242 manager->perform_activity(manager->_data);
244 manager->exempt_stop();
245#ifdef COUNT_THREADS
246 _current_threads().decrement();
247#endif
248//#ifndef _MSC_VER
249 pthread_exit(NULL_POINTER);
250 return NULL_POINTER;
251//#else
252 //_endthread();
253//#endif
254}
255
256//#if defined(__UNIX__) || defined(__GNU_WINDOWS__)
257void *ethread::periodic_thread_driver(void *hidden_pointer)
258//#elif defined(_MSC_VER)
259//void ethread::periodic_thread_driver(void *hidden_pointer)
260//#else
261//#error unknown thread signature.
262//#endif
263{
264 FUNCDEF("periodic_thread_driver");
265 ethread *manager = (ethread *)hidden_pointer;
266//#if defined(__UNIX__) || defined(__GNU_WINDOWS__)
267 if (!manager) return NULL_POINTER;
268//#elif defined(_MSC_VER)
269// if (!manager) return;
270//#endif
271#ifdef COUNT_THREADS
272 _current_threads().increment();
273#endif
275
276 while (!manager->_stop_thread) {
277 // for TIGHT_INTERVAL, we reset the next active time here. this is safe
278 // relative to the reschedule() method, since we're about to do
279 // perform_activity() right now anyway. this brings about a pretty hard
280 // interval; if perform_activity() takes N milliseconds, then there will
281 // only be sleep_time - N (min zero) ms before the next invocation.
282 if (manager->_how == TIGHT_INTERVAL)
283 *manager->_next_activation = time_stamp(manager->_sleep_time);
284
285 manager->_thread_active = true;
286 manager->perform_activity(manager->_data);
287 manager->_thread_active = false;
288
289 // SLACK_INTERVAL means between activations. we reset the next activation
290 // here to ensure we wait the period specified for sleep time, including
291 // whatever time was taken for the activity itself.
292 if (manager->_how == SLACK_INTERVAL)
293 *manager->_next_activation = time_stamp(manager->_sleep_time);
294
295 // we do the sleep timing in chunks so that there's not such a huge wait
296 // when the user stops the thread before the sleep interval elapses.
297 // snooze until time for the next activation.
298 while (!manager->_stop_thread) {
299 int time_diff = int(manager->_next_activation->value()
300 - time_stamp().value());
301 if (time_diff < 0) time_diff = 0; // time keeps on slipping.
302 // make sure we take our time if we're slack intervalled.
303 if (manager->_how == SLACK_INTERVAL) {
304 if (time_diff < MINIMUM_SLEEP_PERIOD)
305 time_diff = MINIMUM_SLEEP_PERIOD;
306 }
307 if (time_diff > MAXIMUM_SLEEP_PERIOD)
308 time_diff = MAXIMUM_SLEEP_PERIOD;
309 if (!manager->_stop_thread)
310 time_control::sleep_ms(time_diff);
311 if (time_stamp() >= *manager->_next_activation)
312 break;
313 }
314 }
316 manager->exempt_stop();
317#ifdef COUNT_THREADS
318 _current_threads().decrement();
319#endif
320//#ifndef _MSC_VER
321 pthread_exit(NULL_POINTER);
322 return NULL_POINTER;
323//#else
324 //_endthread();
325//#endif
326}
327
328} //namespace.
329
#define LOG(s)
Provides a dynamically resizable ASCII character string.
Definition astring.h:35
auto_synchronizer simplifies concurrent code by automatically unlocking.
Definition mutex.h:113
static basis::astring system_error_text(basis::un_int error_to_show)
returns the OS's string form of the "error_to_show".
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:145
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:213
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:194
ethread()
creates a single-shot thread object.
Definition ethread.cpp:86
void reschedule(int delay=0)
causes a periodic thread to activate after "delay" milliseconds from now.
Definition ethread.cpp:140
virtual ~ethread()
Definition ethread.cpp:125
static void sleep_ms(basis::un_int msec)
a system independent name for a forced snooze measured in milliseconds.
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.
#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:42
#define FUNCDEF(func_in)
FUNCDEF sets the name of a function (and plugs it into the callstack).
Definition enhance_cpp.h:54
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:57
const int MINIMUM_SLEEP_PERIOD
Definition ethread.cpp:52
const int MAXIMUM_SLEEP_PERIOD
Definition ethread.cpp:54
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>
#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.