X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=otk%2Ftimer.cc;h=ceee8f2a2c57abc9488a8342c4ed3e0969e98bd8;hb=64ac7d053748494b3a8d0be9f1d55f6f5b9f1a90;hp=b2b85a661fa9b8a19ed7b3770b8c13caf6ee6cfe;hpb=9259ec5732851dd66f7c598d629e3808ac7ab3d8;p=chaz%2Fopenbox diff --git a/otk/timer.cc b/otk/timer.cc index b2b85a66..ceee8f2a 100644 --- a/otk/timer.cc +++ b/otk/timer.cc @@ -1,4 +1,4 @@ -// -*- mode: C++; indent-tabs-mode: nil; -*- +// -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*- #ifdef HAVE_CONFIG_H # include "../config.h" @@ -6,181 +6,138 @@ #include "timer.hh" #include "display.hh" -#include "util.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; - } - } - - 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; -} - - -OBTimer::OBTimer(OBTimerQueueManager *m, OBTimeoutHandler h, OBTimeoutData d) -{ - manager = m; - handler = h; - data = d; - - recur = timing = False; -} - - -OBTimer::~OBTimer(void) -{ - if (timing) stop(); +extern "C" { +#ifdef HAVE_SYS_SELECT_H +# include +#else +# ifdef HAVE_UNISTD_H +# include +# include +# endif // HAVE_UNISTD_H +#endif // HAVE_SYS_SELECT_H } +namespace otk { -void OBTimer::setTimeout(long t) -{ - _timeout.tv_sec = t / 1000; - _timeout.tv_usec = t % 1000; - _timeout.tv_usec *= 1000; -} - +timeval Timer::_nearest_timeout, Timer::_now; +Timer::TimerQ Timer::_q; -void OBTimer::setTimeout(const timeval &t) +void Timer::timevalAdd(timeval &a, long msec) { - _timeout.tv_sec = t.tv_sec; - _timeout.tv_usec = t.tv_usec; + a.tv_sec += msec / 1000; + a.tv_usec += (msec % 1000) * 1000; + a.tv_sec += a.tv_usec / 1000000; + a.tv_usec %= 1000000; } - -void OBTimer::start(void) +bool Timer::nearestTimeout(struct timeval &tm) { - gettimeofday(&_start, 0); - - if (! timing) { - timing = True; - manager->addTimer(this); + 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--; } -} - - -void OBTimer::stop(void) -{ - timing = False; - - manager->removeTimer(this); -} + tm.tv_sec += tm.tv_usec / 1000000; + tm.tv_usec %= 1000000; + if (tm.tv_sec < 0) + tm.tv_sec = 0; - -void OBTimer::halt(void) -{ - timing = False; + return true; } - -void OBTimer::fireTimeout(void) +void Timer::dispatchTimers(bool wait) { - if (handler) - handler(data); -} - + 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; + } -timeval OBTimer::timeRemaining(const timeval &tm) const -{ - timeval ret = endpoint(); + // the queue is sorted, so if this timer shouldn't fire, none are ready + _nearest_timeout = curr->_timeout; + if (!timercmp(&_now, &_nearest_timeout, >)) + break; - ret.tv_sec -= tm.tv_sec; - ret.tv_usec -= tm.tv_usec; + /* 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); + } - return normalizeTimeval(ret); + 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); + } } - -timeval OBTimer::endpoint(void) const +Timer::Timer(long delay, Timer::TimeoutHandler action, void *data) + : _delay(delay), + _action(action), + _data(data), + _del_me(false), + _last(_now), + _timeout(_now) { - timeval ret; - - ret.tv_sec = _start.tv_sec + _timeout.tv_sec; - ret.tv_usec = _start.tv_usec + _timeout.tv_usec; - - return normalizeTimeval(ret); + timevalAdd(_timeout, delay); + _q.push(this); } - -bool OBTimer::shouldFire(const timeval &tm) const +void Timer::operator delete(void *self) { - timeval end = endpoint(); - - return ! ((tm.tv_sec < end.tv_sec) || - (tm.tv_sec == end.tv_sec && tm.tv_usec < end.tv_usec)); + Timer *t; + t = (Timer *)self; + t->_del_me = true; } - -void OBTimerQueueManager::fire() +void Timer::realDelete(Timer *me) { - fd_set rfds; - timeval now, tm, *timeout = (timeval *) 0; - - const int xfd = ConnectionNumber(otk::OBDisplay::display); - - FD_ZERO(&rfds); - FD_SET(xfd, &rfds); // break on any x events - - if (! timerList.empty()) { - const OBTimer* const timer = timerList.top(); - - gettimeofday(&now, 0); - tm = timer->timeRemaining(now); - - timeout = &tm; - } - - select(xfd + 1, &rfds, 0, 0, timeout); - - // check for timer timeout - gettimeofday(&now, 0); - - // there is a small chance for deadlock here: - // *IF* the timer list keeps getting refreshed *AND* the time between - // timer->start() and timer->shouldFire() is within the timer's period - // then the timer will keep firing. This should be VERY near impossible. - while (! timerList.empty()) { - OBTimer *timer = timerList.top(); - if (! timer->shouldFire(now)) - break; - - timerList.pop(); - - timer->fireTimeout(); - timer->halt(); - if (timer->isRecurring()) - timer->start(); - } + ::delete me; } - -void OBTimerQueueManager::addTimer(OBTimer *timer) +void Timer::initialize(void) { - assert(timer); - timerList.push(timer); + gettimeofday(&_now, NULL); + _nearest_timeout.tv_sec = 100000; + _nearest_timeout.tv_usec = 0; } -void OBTimerQueueManager::removeTimer(OBTimer* timer) +void Timer::destroy(void) { - assert(timer); - timerList.release(timer); + while(!_q.empty()) { + realDelete(_q.top()); + _q.pop(); + } } }