/*] Copyright (c) 2009-2010, Charles McGarvey [************************** **] All rights reserved. * * vi:ts=4 sw=4 tw=75 * * Distributable under the terms and conditions of the 2-clause BSD license; * see the file COPYING for a complete text of the license. * **************************************************************************/ #include "config.h" #include #include #include #include #include "debug.hh" #include "timer.hh" namespace moof { scalar timer::next_expiration_ = std::numeric_limits::max(); hash timer::timers_; unsigned timer::new_identifier() { static unsigned id = 1; return id++; } void timer::init(const function& function, scalar seconds, mode mode) { invalidate(); mode_ = mode; if (mode_ != invalid) { function_ = function; if (mode == absolute) { absolute_ = seconds; } else { absolute_ = seconds - ticks(); interval_ = seconds; } id_ = new_identifier(); timers_.insert(std::pair(id_, this)); if (absolute_ < next_expiration_) next_expiration_ = absolute_; } } bool timer::is_valid() const { return mode_ != invalid; } void timer::invalidate() { if (mode_ != invalid) { timers_.erase(id_); mode_ = invalid; if (is_equal(absolute_, next_expiration_)) { next_expiration_ = find_next_expiration(); } } } void timer::fire() { scalar t = ticks(); if (function_) function_(*this, t); if (is_repeating()) { scalar absolute = absolute_; if (is_equal(absolute_, t, 1.0)) absolute_ += interval_; else absolute_ = interval_ + t; if (is_equal(absolute, next_expiration_)) { next_expiration_ = find_next_expiration(); } } else { invalidate(); } } scalar timer::find_next_expiration() { scalar next_fire = std::numeric_limits::max(); hash::iterator it; for (it = timers_.begin(); it != timers_.end(); ++it) { scalar absolute = (*it).second->absolute_; if (absolute < next_fire) next_fire = absolute; } return next_fire; } scalar timer::seconds_remaining() const { return absolute_ - ticks(); } bool timer::is_expired() const { return seconds_remaining() < 0.0; } bool timer::is_repeating() const { return mode_ == repeat; } void timer::fire_expired_timers() { fire_expired_timers(ticks()); } void timer::fire_expired_timers(scalar t) { if (next_expiration_ > t) return; hash::iterator it; for (it = timers_.begin(); it != timers_.end(); ++it) { timer* timer = (*it).second; if (timer->is_expired()) timer->fire(); } } #if USE_CLOCK_GETTIME // Since the monotonic clock will provide us with the time since the // computer started, the number of seconds since that time could easily // become so large that it cannot be accurately stored in a float (even // with as little two days uptime), therefore we need to start from a more // recent reference (when the program starts). Of course this isn't much // of an issue if scalar is a double-precision number. static time_t set_reference() { struct timespec ts; if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) { return 0; } return ts.tv_sec; } static const time_t reference_ = set_reference(); scalar timer::ticks() { struct timespec ts; int result = clock_gettime(CLOCK_MONOTONIC, &ts); ASSERT(result == 0 && "cannot access clock"); return scalar(ts.tv_sec - reference_) + scalar(ts.tv_nsec) / 1000000000.0; } void timer::sleep(scalar seconds, mode mode) { struct timespec ts; int ret; if (mode == absolute) seconds -= ticks(); ts.tv_sec = time_t(seconds); ts.tv_nsec = long((seconds - scalar(ts.tv_sec)) * 1000000000.0); do { ret = nanosleep(&ts, &ts); } while (ret == -1 && errno == EINTR); } #else // ! USE_CLOCK_GETTIME // If we don't have posix timers, we'll have to use a different timing // method. SDL only promises centisecond accuracy, but that's better than // a kick in the pants. scalar timer::ticks() { Uint32 ms = SDL_GetTicks(); return scalar(ms / 1000) + scalar(ms % 1000) / 1000.0; } void timer::sleep(scalar seconds, mode mode) { if (mode == absolute) seconds -= ticks(); SDL_Delay(Uint32(clamp(int(seconds * 1000.0), 0, 1000))); } #endif // USE_CLOCK_GETTIME } // namespace moof