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