00001 #ifndef TIMER_DRIVER_IMPLEMENTATION_FILE
00002 #define TIMER_DRIVER_IMPLEMENTATION_FILE
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018 #include "timer_driver.h"
00019
00020 #include <basis/function.h>
00021 #include <basis/log_base.h>
00022 #include <basis/mutex.h>
00023 #include <basis/portable.h>
00024 #include <basis/utility.h>
00025 #include <data_struct/amorph.cpp>
00026 #include <data_struct/static_memory_gremlin.h>
00027 #include <mechanisms/ithread.h>
00028 #include <mechanisms/time_stamp.h>
00029
00030 #include <signal.h>
00031 #include <stdio.h>
00032 #ifdef __UNIX__
00033 #include <sys/time.h>
00034 #endif
00035
00036
00037
00038
00039 #undef LOG
00040 #define LOG(tpr) printf( (utility::timestamp(true, true) + " timer_driver::" + func + tpr).s() )
00041
00042 const int INITIAL_TIMER_GRANULARITY = 14;
00043
00044
00045
00046 const int MAX_TIMER_PREDICTION = 140;
00047
00048
00049
00050
00051 const int PAUSE_TIME = 200;
00052
00053
00054
00055 const int LONG_TIME = 1 * HOUR_ms;
00056
00057
00059
00060 SAFE_STATIC(timer_driver, timer_driver::global_timer_driver, )
00061
00062
00063
00064 timed_object::~timed_object() {}
00065
00067
00068 #ifdef __UNIX__
00069 const int OUR_SIGNAL = SIGUSR2;
00070
00071 class signalling_thread : public ithread
00072 {
00073 public:
00074 signalling_thread(int initial_interval) : ithread(initial_interval) {}
00075
00076 void perform_activity(void *formal(ptr)) {
00077 raise(OUR_SIGNAL);
00078 }
00079
00080 private:
00081 };
00082 #endif
00083
00084 #ifdef __UNIX__
00085 void timer_driver_private_handler(int signal_seen)
00086 #elif defined(__WIN32__)
00087 void __stdcall timer_driver_private_handler(window_handle hwnd, u_int msg,
00088 UINT_PTR id, u_long time)
00089 #else
00090 #error No timer method known for this OS.
00091 #endif
00092 {
00093 #undef static_class_name
00094 #define static_class_name() "timer_driver"
00095 FUNCDEF("timer_driver_private_handler");
00096 #ifdef __UNIX__
00097 int seen = signal_seen;
00098 if (seen != OUR_SIGNAL) {
00099 #elif defined(__WIN32__)
00100 u_int *seen = (u_int *)id;
00101 if (seen != program_wide_timer().real_timer_id()) {
00102 #else
00103 if (true) {
00104 #endif
00105 #ifdef DEBUG_TIMER_DRIVER
00106 LOG(isprintf("unknown signal/message %x caught.", (void *)seen));
00107 #endif
00108 return;
00109 }
00110 program_wide_timer().handle_system_timer();
00111 #undef static_class_name
00112 }
00113
00115
00116 class driven_object_record
00117 {
00118 public:
00119 int _duration;
00120 timed_object *_to_invoke;
00121 time_stamp _next_hit;
00122 bool _okay_to_invoke;
00123 bool _handling_timer;
00124
00125 driven_object_record(int duration, timed_object *to_invoke)
00126 : _duration(duration), _to_invoke(to_invoke), _next_hit(duration),
00127 _okay_to_invoke(true), _handling_timer(false) {}
00128 };
00129
00130 class driven_objects_list
00131 : public amorph<driven_object_record>, public virtual object_base
00132 {
00133 public:
00134 IMPLEMENT_CLASS_NAME("driven_objects_list");
00135
00136 int find_obj(timed_object *obj) {
00137 for (int i = 0; i < elements(); i++) {
00138 if (borrow(i) && (borrow(i)->_to_invoke == obj))
00139 return i;
00140 }
00141 return common::NOT_FOUND;
00142 }
00143 };
00144
00146
00147 timer_driver::timer_driver()
00148 : _timers(new driven_objects_list),
00149 _lock(new mutex),
00150 #ifdef __UNIX__
00151 _prompter(new signalling_thread(INITIAL_TIMER_GRANULARITY)),
00152 #endif
00153 #ifdef __WIN32__
00154 _real_timer_id(NIL),
00155 #endif
00156 _in_timer(false)
00157 {
00158 hookup_OS_timer(INITIAL_TIMER_GRANULARITY);
00159
00160 #ifdef __UNIX__
00161
00162 signal(OUR_SIGNAL, &timer_driver_private_handler);
00163 _prompter->start(NIL);
00164 #endif
00165 }
00166
00167 timer_driver::~timer_driver()
00168 {
00169 FUNCDEF("destructor");
00170 #ifdef __UNIX__
00171 _prompter->stop();
00172
00173 struct sigaction action;
00174 action.sa_handler = SIG_DFL;
00175 action.sa_sigaction = NIL;
00176 sigemptyset(&action.sa_mask);
00177 action.sa_flags = 0;
00178 action.sa_restorer = NIL;
00179 int ret = sigaction(OUR_SIGNAL, &action, NIL);
00180 if (ret) {
00182 }
00183 #endif
00184 unhook_OS_timer();
00185
00186
00187 while (true) {
00188 _lock->lock();
00189 if (_in_timer) {
00190 _lock->unlock();
00191 #ifdef DEBUG_TIMER_DRIVER
00192 LOG("waiting to acquire timer_driver lock.");
00193 #endif
00194 portable::sleep_ms(PAUSE_TIME);
00195 } else {
00196 break;
00197 }
00198 }
00199
00200 _timers->reset();
00201 _lock->unlock();
00202
00203 WHACK(_timers);
00204 WHACK(_lock);
00205 #ifdef __UNIX__
00206 WHACK(_prompter);
00207 #endif
00208
00209 #ifdef DEBUG_TIMER_DRIVER
00210 LOG("timer_driver is closing down.");
00211 #endif
00212 }
00213
00214 #ifdef __WIN32__
00215 u_int *timer_driver::real_timer_id() { return _real_timer_id; }
00216 #endif
00217
00218 bool timer_driver::zap_timer(timed_object *to_remove)
00219 {
00220 FUNCDEF("zap_timer");
00221 #ifdef DEBUG_TIMER_DRIVER
00222 if (_in_timer) {
00223 LOG("hmmm: zapping timer while handling previous timer...!");
00224 }
00225 #endif
00226 auto_synchronizer l(*_lock);
00227 int indy = _timers->find_obj(to_remove);
00228 if (negative(indy)) return false;
00229 #ifdef DEBUG_TIMER_DRIVER
00230 LOG(isprintf("zapping timer %x.", to_remove));
00231 #endif
00232 driven_object_record *reco = _timers->borrow(indy);
00233 reco->_okay_to_invoke = false;
00234 if (reco->_handling_timer) {
00235
00236 #ifdef DEBUG_TIMER_DRIVER
00237 LOG(isprintf("Logic Error: timer %x being zapped WHILE BEING HANDLED!",
00238 to_remove));
00239 #endif
00240 }
00241 return true;
00242 }
00243
00244 bool timer_driver::set_timer(int duration, timed_object *to_invoke)
00245 {
00246 FUNCDEF("set_timer");
00247 #ifdef DEBUG_TIMER_DRIVER
00248 if (_in_timer) {
00249 LOG("hmmm: setting timer while handling previous timer...!");
00250 }
00251 #endif
00252 #ifdef DEBUG_TIMER_DRIVER
00253 LOG(isprintf("setting timer %x to %d ms.", to_invoke, duration));
00254 #endif
00255 auto_synchronizer l(*_lock);
00256
00257 int indy = _timers->find_obj(to_invoke);
00258 if (negative(indy)) {
00259
00260 _timers->append(new driven_object_record(duration, to_invoke));
00261 } else {
00262
00263 driven_object_record *reco = _timers->borrow(indy);
00264 reco->_duration = duration;
00265 reco->_okay_to_invoke = true;
00266 }
00267 return true;
00268 }
00269
00270 void timer_driver::handle_system_timer()
00271 {
00272 FUNCDEF("handle_system_timer");
00273 if (_in_timer) {
00274 #ifdef DEBUG_TIMER_DRIVER
00275 LOG("terrible error: invoked system timer while handling previous timer.");
00276 #endif
00277 return;
00278 }
00279 unhook_OS_timer();
00280
00281 #ifdef DEBUG_TIMER_DRIVER
00282 LOG("into handling OS timer...");
00283 #endif
00284
00285 array<driven_object_record *> to_invoke_now;
00286
00287 {
00288
00289
00290 auto_synchronizer l(*_lock);
00291 _in_timer = true;
00292
00293
00294
00295 for (int i = 0; i < _timers->elements(); i++) {
00296 driven_object_record *funky = _timers->borrow(i);
00297 if (!funky) {
00298 const char *msg = "error in timer list logic!";
00299 #ifdef DEBUG_TIMER_DRIVER
00300 LOG(msg);
00301 #endif
00302 #ifdef CATCH_ERRORS
00303 continuable_error(static_class_name(), func, msg);
00304 #endif
00305 _timers->zap(i, i);
00306 i--;
00307 continue;
00308 }
00309 if (funky->_next_hit <= time_stamp()) {
00310
00311 to_invoke_now += funky;
00312 }
00313 }
00314 }
00315
00316 #ifdef DEBUG_TIMER_DRIVER
00317 istring pointer_dump;
00318 for (int i = 0; i < to_invoke_now.length(); i++) {
00319 driven_object_record *funky = to_invoke_now[i];
00320 pointer_dump += isprintf("%x ", funky->_to_invoke);
00321 }
00322 if (pointer_dump.t())
00323 LOG(istring("activating ") + pointer_dump);
00324 #endif
00325
00326
00327 for (int i = 0; i < to_invoke_now.length(); i++) {
00328 driven_object_record *funky = to_invoke_now[i];
00329 {
00330 auto_synchronizer l(*_lock);
00331 if (!funky->_okay_to_invoke) continue;
00332 funky->_handling_timer = true;
00333 }
00334
00335 funky->_to_invoke->handle_timer_callback();
00336 {
00337 auto_synchronizer l(*_lock);
00338 funky->_handling_timer = false;
00339 }
00340
00341 funky->_next_hit.reset(funky->_duration);
00342 }
00343
00344
00345 int next_timer_duration = MAX_TIMER_PREDICTION;
00346 time_stamp now;
00347 for (int i = 0; i < _timers->elements(); i++) {
00348 driven_object_record *funky = _timers->borrow(i);
00349 int funky_time = int(funky->_next_hit.value() - now.value());
00350
00351
00352 if (funky_time < INITIAL_TIMER_GRANULARITY)
00353 funky_time = INITIAL_TIMER_GRANULARITY;
00354 if (funky_time < next_timer_duration)
00355 next_timer_duration = funky_time;
00356 }
00357
00358 {
00359
00360 auto_synchronizer l(*_lock);
00361 _in_timer = false;
00362 for (int i = 0; i < _timers->elements(); i++) {
00363 driven_object_record *funky = _timers->borrow(i);
00364 if (!funky->_okay_to_invoke) {
00365
00366 _timers->zap(i, i);
00367 i--;
00368 }
00369 }
00370 }
00371
00372 #ifdef DEBUG_TIMER_DRIVER
00373 LOG("done handling OS timer.");
00374 #endif
00375
00376
00377 reset_OS_timer(next_timer_duration);
00378 }
00379
00380
00381
00382
00383 void timer_driver::hookup_OS_timer(int duration)
00384 {
00385 FUNCDEF("hookup_OS_timer");
00386 if (negative(duration)) {
00387 #ifdef DEBUG_TIMER_DRIVER
00388 LOG("seeing negative duration for timer!");
00389 #endif
00390 duration = 1;
00391 } else if (!duration) {
00392 #ifdef DEBUG_TIMER_DRIVER
00393 LOG("patching zero duration for timer.");
00394 #endif
00395 duration = 1;
00396 }
00397 #ifdef DEBUG_TIMER_DRIVER
00398 LOG(isprintf("hooking next OS timer in %d ms.", duration));
00399 #endif
00400 #ifdef __UNIX__
00401
00402 _prompter->reschedule(duration);
00403 #elif defined(__WIN32__)
00404 int max_tries_left = 100;
00405 while (max_tries_left-- >= 0) {
00406 _real_timer_id = (u_int *)SetTimer(NIL, 0, duration,
00407 timer_driver_private_handler);
00408 if (!_real_timer_id) {
00409
00410 LOG("could not set the interval timer.");
00411 portable::sleep_ms(50);
00412 continue;
00413 } else
00414 break;
00415 }
00416 #endif
00417 }
00418
00419 void timer_driver::unhook_OS_timer()
00420 {
00421 FUNCDEF("unhook_OS_timer");
00422 #ifdef __UNIX__
00423
00424 _prompter->reschedule(LONG_TIME);
00425 #elif defined(__WIN32__)
00426 if (_real_timer_id) KillTimer(NIL, (UINT_PTR)_real_timer_id);
00427 #endif
00428 #ifdef DEBUG_TIMER_DRIVER
00429 LOG("unhooked OS timer.");
00430 #endif
00431 }
00432
00433 void timer_driver::reset_OS_timer(int next_hit)
00434 {
00435 FUNCDEF("reset_OS_timer");
00436 unhook_OS_timer();
00437 hookup_OS_timer(next_hit);
00438 }
00439
00440
00441 #endif //TIMER_DRIVER_IMPLEMENTATION_FILE
00442