X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=otk%2Ftimer.cc;h=0a0083146afee234012a7e30796f33d4bd60b393;hb=ad44e8a708b6836c9d84be7205457cfa82800a65;hp=3f1afea069d7a4f36e1c2694f8b68ae50dc72e90;hpb=d0b55be2c7afb5b25b42804c9eab4995e64dff52;p=chaz%2Fopenbox diff --git a/otk/timer.cc b/otk/timer.cc index 3f1afea0..0a008314 100644 --- a/otk/timer.cc +++ b/otk/timer.cc @@ -1,125 +1,145 @@ -// -*- mode: C++; indent-tabs-mode: nil; -*- +// -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*- -#ifdef HAVE_CONFIG_H -# include "../config.h" -#endif // HAVE_CONFIG_H +#include "config.h" #include "timer.hh" -#include "timerqueuemanager.hh" +#include "display.hh" -namespace otk { - -static timeval normalizeTimeval(const timeval &tm) -{ - timeval ret = tm; - - while (ret.tv_usec < 0) { - if (ret.tv_sec > 0) { - --ret.tv_sec; - ret.tv_usec += 1000000; - } else { - ret.tv_usec = 0; - } - } +extern "C" { +#ifdef HAVE_SYS_SELECT_H +# include +#endif // HAVE_SYS_SELECT_H - if (ret.tv_usec >= 1000000) { - ret.tv_sec += ret.tv_usec / 1000000; - ret.tv_usec %= 1000000; - } - - if (ret.tv_sec < 0) ret.tv_sec = 0; - - return ret; +#ifdef HAVE_SYS_TIME_H +# include +#endif } +namespace otk { -OBTimer::OBTimer(OBTimerQueueManager *m, OBTimeoutHandler h, OBTimeoutData d) -{ - manager = m; - handler = h; - data = d; - - recur = timing = false; -} - +timeval Timer::_nearest_timeout, Timer::_now; +Timer::TimerQ Timer::_q; -OBTimer::~OBTimer(void) +void Timer::timevalAdd(timeval &a, long msec) { - if (timing) stop(); + a.tv_sec += msec / 1000; + a.tv_usec += (msec % 1000) * 1000; + a.tv_sec += a.tv_usec / 1000000; + a.tv_usec %= 1000000; } - -void OBTimer::setTimeout(long t) +bool Timer::nearestTimeout(struct timeval &tm) { - _timeout.tv_sec = t / 1000; - _timeout.tv_usec = t % 1000; - _timeout.tv_usec *= 1000; -} - + if (_q.empty()) + return false; + tm.tv_sec = _nearest_timeout.tv_sec - _now.tv_sec; + tm.tv_usec = _nearest_timeout.tv_usec - _now.tv_usec; + + while (tm.tv_usec < 0) { + tm.tv_usec += 1000000; + tm.tv_sec--; + } + tm.tv_sec += tm.tv_usec / 1000000; + tm.tv_usec %= 1000000; + if (tm.tv_sec < 0) + tm.tv_sec = 0; -void OBTimer::setTimeout(const timeval &t) -{ - _timeout.tv_sec = t.tv_sec; - _timeout.tv_usec = t.tv_usec; + return true; } - -void OBTimer::start(void) +void Timer::dispatchTimers(bool wait) { - gettimeofday(&_start, 0); + fd_set selset; + int fd; + timeval next; + Timer *curr; + + gettimeofday(&_now, NULL); + _nearest_timeout = _now; + _nearest_timeout.tv_sec += 10000; + + while (!_q.empty()) { + curr = _q.top(); + /* since we overload the destructor to keep from removing from the middle + of the priority queue, set _del_me, we have to do our real delete in + here. + */ + if (curr->_del_me) { + _q.pop(); + realDelete(curr); + continue; + } - if (! timing) { - timing = true; - manager->addTimer(this); + // the queue is sorted, so if this timer shouldn't fire, none are ready + _nearest_timeout = curr->_timeout; + if (!timercmp(&_now, &_nearest_timeout, >)) + break; + + /* we set the last fired time to delay msec after the previous firing, then + re-insert. timers maintain their order and may trigger more than once + if they've waited more than one delay's worth of time. + */ + _q.pop(); + timevalAdd(curr->_last, curr->_delay); + curr->_action(curr->_data); + timevalAdd(curr->_timeout, curr->_delay); + _q.push(curr); + + /* if at least one timer fires, then don't wait on X events, as there may + already be some in the queue from the timer callbacks. + */ + wait = false; } -} - -void OBTimer::stop(void) -{ - if (timing) { - timing = false; - - manager->removeTimer(this); + if (wait) { + // wait for the nearest trigger, or for X to do something interesting + fd = ConnectionNumber(**display); + FD_ZERO(&selset); + FD_SET(fd, &selset); + if (nearestTimeout(next)) { + select(fd + 1, &selset, NULL, NULL, &next); + } else + select(fd + 1, &selset, NULL, NULL, NULL); } } - -void OBTimer::fireTimeout(void) +Timer::Timer(long delay, Timer::TimeoutHandler action, void *data) + : _delay(delay), + _action(action), + _data(data), + _del_me(false), + _last(_now), + _timeout(_now) { - if (handler) - handler(data); + timevalAdd(_timeout, delay); + _q.push(this); } - -timeval OBTimer::timeRemaining(const timeval &tm) const +void Timer::operator delete(void *self) { - timeval ret = endpoint(); - - ret.tv_sec -= tm.tv_sec; - ret.tv_usec -= tm.tv_usec; - - return normalizeTimeval(ret); + Timer *t; + t = (Timer *)self; + t->_del_me = true; } - -timeval OBTimer::endpoint(void) const +void Timer::realDelete(Timer *me) { - timeval ret; - - ret.tv_sec = _start.tv_sec + _timeout.tv_sec; - ret.tv_usec = _start.tv_usec + _timeout.tv_usec; - - return normalizeTimeval(ret); + ::delete me; } - -bool OBTimer::shouldFire(const timeval &tm) const +void Timer::initialize(void) { - timeval end = endpoint(); + gettimeofday(&_now, NULL); + _nearest_timeout.tv_sec = 100000; + _nearest_timeout.tv_usec = 0; +} - return ! ((tm.tv_sec < end.tv_sec) || - (tm.tv_sec == end.tv_sec && tm.tv_usec < end.tv_usec)); +void Timer::destroy(void) +{ + while(!_q.empty()) { + realDelete(_q.top()); + _q.pop(); + } } }