1 /*****************************************************************************\
4 * Author : Chris Koeritz *
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 \*****************************************************************************/
17 #include <application/windoze_helper.h>
18 #include <basis/astring.h>
19 #include <basis/functions.h>
20 #include <basis/guards.h>
21 #include <loggers/critical_events.h>
22 #include <loggers/program_wide_logger.h>
23 #include <structures/static_memory_gremlin.h>
24 #include <timely/time_control.h>
28 #elif defined(__UNIX__) || defined(__GNU_WINDOWS__)
31 #error unknown OS for thread support.
34 using namespace basis;
35 using namespace loggers;
36 using namespace structures;
37 using namespace timely;
39 //#define COUNT_THREADS
40 // if this is enabled, then threads will be counted when they are created
44 #define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), s)
48 const int MAXIMUM_CREATE_ATTEMPTS = 20;
49 // the number of retries we allow to try creating a thread, if the first
52 const int MINIMUM_SLEEP_PERIOD = 10;
53 // this is the smallest time we'll sleep for if we're slack.
55 const int MAXIMUM_SLEEP_PERIOD = 200;
56 // the number of milliseconds we use for breaking up longer sleep periods.
58 const int SNOOZE_FOR_RETRY = 100;
59 // how long to sleep when a thread creation fails.
62 // singleton thread counter code.
63 class thread_counter : public virtual root_object {
65 thread_counter() : _count(0) {}
66 DEFINE_CLASS_NAME("thread_counter");
68 auto_synchronizer l(_lock);
72 auto_synchronizer l(_lock);
80 SAFE_STATIC(thread_counter, _current_threads, )
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.
88 : _thread_ready(false),
89 _thread_active(false),
95 _handle(new pthread_t),
99 _next_activation(new time_stamp),
100 _how(TIGHT_INTERVAL) // unused.
102 FUNCDEF("constructor [one-shot]");
105 ethread::ethread(int sleep_timer, timed_thread_types how)
106 : _thread_ready(false),
107 _thread_active(false),
113 _handle(new pthread_t),
115 _sleep_time(sleep_timer),
117 _next_activation(new time_stamp),
120 FUNCDEF("constructor [periodic]");
121 if (sleep_timer < MINIMUM_SLEEP_PERIOD) {
122 _sleep_time = MINIMUM_SLEEP_PERIOD;
129 WHACK(_next_activation);
135 ///void ethread::pre_thread() {}
137 ///void ethread::post_thread() {}
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)
143 *_next_activation = time_stamp(delay); // start after the delay.
146 bool ethread::start(void *thread_data)
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;
157 while (attempts++ < MAXIMUM_CREATE_ATTEMPTS) {
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.");
166 ret = pthread_create(_handle, &attribs, periodic_thread_driver,
169 ret = pthread_create(_handle, &attribs, one_shot_thread_driver,
171 if (!ret) success = true;
175 _handle = _beginthread(periodic_thread_driver, 0, (void *)this);
177 _handle = _beginthread(one_shot_thread_driver, 0, (void *)this);
178 if (_handle != -1) success = true;
179 else error = critical_events::system_error();
181 if (success) break; // got it created.
182 LOG("failed to create thread; trying again...");
183 time_control::sleep_ms(SNOOZE_FOR_RETRY);
186 // couldn't start it, so reset our state.
187 LOG(astring("failed to create thread, error is ")
188 + critical_events::system_error_text(error));
197 cancel(); // tell thread to leave.
198 if (!thread_started()) return; // not running.
199 while (!thread_finished()) {
202 if (!GetExitCodeThread((HANDLE)_handle, (LPDWORD)&result)
203 || (result != STILL_ACTIVE)) {
208 time_control::sleep_ms(10); // wait for thread to leave.
212 void ethread::exempt_stop()
214 _thread_active = false;
215 _thread_ready = false;
221 #if defined(__UNIX__) || defined(__GNU_WINDOWS__)
222 void *ethread::one_shot_thread_driver(void *hidden_pointer)
223 #elif defined(_MSC_VER)
224 void ethread::one_shot_thread_driver(void *hidden_pointer)
226 #error unknown thread signature.
229 FUNCDEF("one_shot_thread_driver");
230 ethread *manager = (ethread *)hidden_pointer;
232 if (!manager) return NULL_POINTER;
234 if (!manager) return;
237 _current_threads().increment();
239 /// manager->pre_thread();
240 manager->_thread_active = true;
241 manager->perform_activity(manager->_data);
242 /// manager->post_thread();
243 manager->exempt_stop();
245 _current_threads().decrement();
248 pthread_exit(NULL_POINTER);
255 #if defined(__UNIX__) || defined(__GNU_WINDOWS__)
256 void *ethread::periodic_thread_driver(void *hidden_pointer)
257 #elif defined(_MSC_VER)
258 void ethread::periodic_thread_driver(void *hidden_pointer)
260 #error unknown thread signature.
263 FUNCDEF("periodic_thread_driver");
264 ethread *manager = (ethread *)hidden_pointer;
265 #if defined(__UNIX__) || defined(__GNU_WINDOWS__)
266 if (!manager) return NULL_POINTER;
267 #elif defined(_MSC_VER)
268 if (!manager) return;
271 _current_threads().increment();
273 /// manager->pre_thread();
275 while (!manager->_stop_thread) {
276 // for TIGHT_INTERVAL, we reset the next active time here. this is safe
277 // relative to the reschedule() method, since we're about to do
278 // perform_activity() right now anyway. this brings about a pretty hard
279 // interval; if perform_activity() takes N milliseconds, then there will
280 // only be sleep_time - N (min zero) ms before the next invocation.
281 if (manager->_how == TIGHT_INTERVAL)
282 *manager->_next_activation = time_stamp(manager->_sleep_time);
284 manager->_thread_active = true;
285 manager->perform_activity(manager->_data);
286 manager->_thread_active = false;
288 // SLACK_INTERVAL means between activations. we reset the next activation
289 // here to ensure we wait the period specified for sleep time, including
290 // whatever time was taken for the activity itself.
291 if (manager->_how == SLACK_INTERVAL)
292 *manager->_next_activation = time_stamp(manager->_sleep_time);
294 // we do the sleep timing in chunks so that there's not such a huge wait
295 // when the user stops the thread before the sleep interval elapses.
296 // snooze until time for the next activation.
297 while (!manager->_stop_thread) {
298 int time_diff = int(manager->_next_activation->value()
299 - time_stamp().value());
300 if (time_diff < 0) time_diff = 0; // time keeps on slipping.
301 // make sure we take our time if we're slack intervalled.
302 if (manager->_how == SLACK_INTERVAL) {
303 if (time_diff < MINIMUM_SLEEP_PERIOD)
304 time_diff = MINIMUM_SLEEP_PERIOD;
306 if (time_diff > MAXIMUM_SLEEP_PERIOD)
307 time_diff = MAXIMUM_SLEEP_PERIOD;
308 if (!manager->_stop_thread)
309 time_control::sleep_ms(time_diff);
310 if (time_stamp() >= *manager->_next_activation)
314 /// manager->post_thread();
315 manager->exempt_stop();
317 _current_threads().decrement();
320 pthread_exit(NULL_POINTER);