X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=otk%2Ftimer.cc;h=2d36c7a05e8c476839711e5b9e3537212712568c;hb=96a9196cbb71b7f8d5e3d98a92b2e59bb1b591a8;hp=b2b85a661fa9b8a19ed7b3770b8c13caf6ee6cfe;hpb=9259ec5732851dd66f7c598d629e3808ac7ab3d8;p=chaz%2Fopenbox diff --git a/otk/timer.cc b/otk/timer.cc index b2b85a66..2d36c7a0 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,126 @@ #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; -} +timeval Timer::_nearest_timeout, Timer::_now; +Timer::TimerQ Timer::_q; - -OBTimer::OBTimer(OBTimerQueueManager *m, OBTimeoutHandler h, OBTimeoutData d) +void Timer::timevalAdd(timeval &a, long msec) { - manager = m; - handler = h; - data = d; - - recur = timing = False; + a.tv_sec += msec / 1000; + a.tv_usec += (msec % 1000) * 1000; + a.tv_sec += a.tv_usec / 1000000; + a.tv_usec %= 1000000; } - -OBTimer::~OBTimer(void) +bool Timer::nearestTimeout(struct timeval &tm) { - if (timing) stop(); -} - + 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(long t) -{ - _timeout.tv_sec = t / 1000; - _timeout.tv_usec = t % 1000; - _timeout.tv_usec *= 1000; + return true; } - -void OBTimer::setTimeout(const timeval &t) +void Timer::dispatchTimers(bool wait) { - _timeout.tv_sec = t.tv_sec; - _timeout.tv_usec = t.tv_usec; -} - + 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; + } -void OBTimer::start(void) -{ - gettimeofday(&_start, 0); + // the queue is sorted, so if this timer shouldn't fire, none are ready + _nearest_timeout = curr->_timeout; + if (!timercmp(&_now, &_nearest_timeout, >)) + break; - if (! timing) { - timing = True; - manager->addTimer(this); + /* 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); } -} - -void OBTimer::stop(void) -{ - timing = False; - - manager->removeTimer(this); -} - - -void OBTimer::halt(void) -{ - timing = False; + 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(); - - return ! ((tm.tv_sec < end.tv_sec) || - (tm.tv_sec == end.tv_sec && tm.tv_usec < end.tv_usec)); + gettimeofday(&_now, NULL); + _nearest_timeout.tv_sec = 100000; + _nearest_timeout.tv_usec = 0; } - -void OBTimerQueueManager::fire() +void Timer::destroy(void) { - 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; + while(!_q.empty()) { + realDelete(_q.top()); + _q.pop(); } - - 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(); - } -} - - -void OBTimerQueueManager::addTimer(OBTimer *timer) -{ - assert(timer); - timerList.push(timer); -} - -void OBTimerQueueManager::removeTimer(OBTimer* timer) -{ - assert(timer); - timerList.release(timer); } }