#endif // HAVE_CONFIG_H
#include "timer.hh"
-#include "timerqueuemanager.hh"
+#include "display.hh"
+
+extern "C" {
+#ifdef HAVE_SYS_SELECT_H
+# include <sys/select.h>
+#else
+# ifdef HAVE_UNISTD_H
+# include <sys/types.h>
+# include <unistd.h>
+# endif // HAVE_UNISTD_H
+#endif // HAVE_SYS_SELECT_H
+}
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;
+
+ /* 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 (! _timing) {
- _timing = true;
- _manager->addTimer(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::stop(void)
+Timer::Timer(long delay, Timer::TimeoutHandler action, void *data)
+ : _delay(delay),
+ _action(action),
+ _data(data),
+ _del_me(false),
+ _last(_now),
+ _timeout(_now)
{
- if (_timing) {
- _timing = false;
-
- _manager->removeTimer(this);
- }
+ timevalAdd(_timeout, delay);
+ _q.push(this);
}
-
-void OBTimer::fire(void)
+void Timer::operator delete(void *self)
{
- if (_handler)
- _handler(_data);
+ Timer *t;
+ t = (Timer *)self;
+ t->_del_me = true;
}
-
-timeval OBTimer::remainingTime(const timeval &tm) const
+void Timer::realDelete(Timer *me)
{
- timeval ret = endTime();
-
- ret.tv_sec -= tm.tv_sec;
- ret.tv_usec -= tm.tv_usec;
-
- return normalizeTimeval(ret);
+ ::delete me;
}
-
-timeval OBTimer::endTime(void) const
+void Timer::initialize(void)
{
- timeval ret;
-
- ret.tv_sec = _start.tv_sec + _timeout.tv_sec;
- ret.tv_usec = _start.tv_usec + _timeout.tv_usec;
-
- return normalizeTimeval(ret);
+ gettimeofday(&_now, NULL);
+ _nearest_timeout.tv_sec = 100000;
+ _nearest_timeout.tv_usec = 0;
}
-
-bool OBTimer::shouldFire(const timeval &tm) const
+void Timer::destroy(void)
{
- timeval end = endTime();
-
- return ! ((tm.tv_sec < end.tv_sec) ||
- (tm.tv_sec == end.tv_sec && tm.tv_usec < end.tv_usec));
+ while(!_q.empty()) {
+ realDelete(_q.top());
+ _q.pop();
+ }
}
}