/*] Copyright (c) 2009-2011, Charles McGarvey [***************************** **] All rights reserved. * * Distributable under the terms and conditions of the 2-clause BSD license; * see the file COPYING for a complete text of the license. * *****************************************************************************/ #ifndef _MOOF_TIMER_HH_ #define _MOOF_TIMER_HH_ #include #include #include #include #include /** * \file timer.hh * Functions for measuring time in a friendly unit. */ namespace moof { // forward declarations class runloop; /** * A timer source is an object that keeps track of increasing time in units * of seconds. A timer source does not necessarily have to increment at * the same rate or in the same way as the real time. The time source must * begin at zero and always remain the same or increase each time the time * is queried. */ class timer_source { public: /** * Deconstruct a timer source. */ virtual ~timer_source() {} /** * Get the number seconds since the timer source was created or reset. */ virtual scalar ticks() const = 0; /** * Reset the timer such that the current time will become zero. */ virtual void reset() = 0; /** * Scale the time. After calling this, the timer source should either * increment faster or slower, depending on the scale factor. */ virtual void scale(scalar factor) = 0; }; /** * A class to represent a timer for scheduled events. The timer events * will be executed on or sometime after their schedculed time. The * runloop associated with the current running view will make an attempt to * fire any expired timers as close to their scheduled times as possible. */ class timer : public boost::noncopyable { public: /** * A type for the state of a timer. */ enum mode { invalid = 0, /// Timer is not scheduled. relative = 1, /// Timer is scheduled by a relative time. absolute = 2, /// Timer is scheduled by an absolute time. repeat = 3 /// Timer is scheduled by a periodic time. }; /** * Function protocol for a timer event handler. A function matching * this protocol will be called when the timer expires. The function * takes two parameters: the timer object that just expired, and the * absolute time of the time source which caused the timer to expire. */ typedef boost::function function; /** * Construct an invalid (uninitialized) timer. */ timer() : mode_(invalid), absolute_(SCALAR(0.0)), runloop_(0) {} /** * Construct a scheduled timer. * \param function The event handler. * \param seconds The number of seconds; the meaning of this depends on * the mode of the timer. * \param mode The timer mode. If the mode is relative (default), the * seconds parameter is the number of seconds from the current time to * wait before expiring the timer. If the mode is absolute, the * seconds parameter is the number of seconds from some arbitrary, * fixed time in the past. If the mode is repeat, the seconds * parameter is the number of seconds from now to wait before expiring * the timer, at which time the timer will be rescheduled to expired * again at that many seconds from the expiration time. A repeating * timer can be invalidated manually using invalidate(). */ timer(const function& function, scalar seconds, mode mode = relative, timer_source& source = default_source()) : runloop_(0) { init(function, seconds, mode, source); } /** * Deconstruct a timer. This will automagically invalidate the timer, * so it will not expire or fire an event. */ ~timer(); /** * Initialize a timer with a scheduled time. If the timer is already * scheduled, its prior schedule will be invalidated and replaced by * this new schedule. * \param function The event handler. * \param seconds The number of seconds; the meaning of this depends on * the mode of the timer. * \param mode The timer mode. If the mode is relative (default), the * seconds parameter is the number of seconds from the current time to * wait before expiring the timer. If the mode is absolute, the * seconds parameter is the number of seconds from some arbitrary, * fixed time in the past. If the mode is repeat, the seconds * parameter is the number of seconds from now to wait before expiring * the timer, at which time the timer will be rescheduled to expired * again at that many seconds from the expiration time. A repeating * timer can be invalidated manually using invalidate(). */ void init(const function& function, scalar seconds, enum mode mode = relative, timer_source& source = default_source()); /** * Manually invalidated the timer, removing any schedule such that the * timer will not expired and no event will be fired. */ void invalidate(); enum mode mode() const { return mode_; } /** * Manually fire the timer event. Usually, the timer event will be * fired when the timer expires, but this can be used to fire it * prematurely. If the timer is scheduled, it will be invalidated. If * the timer is already invalid (but is initialized with an event * handler), the event will be fired and the timer will remain invalid. * \param t The absolute time passed to the timer event function. */ void fire(scalar t); void fire() { fire(source_->ticks()); } /** * Fire the timer event if it is expired. * \param t The absolute time used as a reference to determine if the * timer is expired; defaults to the current time. * \return The absolute time of the next expiration (if repeating), or * 0.0 otherwise. */ bool fire_if_expired(scalar t) { if (is_expired(t)) { fire(t); return true; } return false; } bool fire_if_expired() { return fire_if_expired(source_->ticks()); } /** * Get the absolute time of the next expiration of this timer. * \return Seconds. */ scalar expiration() const { return absolute_; } /** * Get the number of seconds remaining before the timer is scheduled to * expired. If the timer is invalid, the retured value will be * negative. * \param t The absolute time used as a reference to determine the * amount of time left; defaults to the current time. * \return Seconds. */ scalar remaining(scalar t) const { return expiration() - t; } scalar remaining() const { return remaining(source_->ticks()); } /** * Get whether or not the timer is expired. A timer on a repeating * schedule will never be expired since it will always have a scheduled * expiration time in the future. If the timer is expired but not * invalid, the timer event has not yet fired; the timer will be * invalidated when it does fire. * \param t The absolute time used as a reference to determine if the * timer is expired; defaults to the current time. * \return True if the timer is expired, false otherwise. */ bool is_expired(scalar t) const { return remaining(t) < SCALAR(0.0); } bool is_expired() const { return is_expired(source_->ticks()); } static timer_source& default_source(); /** * Get the number of real seconds since the default timer was first * created or last reset. * \return Seconds. */ static scalar ticks(); /** * Put the thread to sleep for a certain period of time. If absolute * is true, then it will sleep until the default timer reaches the * specified number of seconds. If the mode is relative, the thread * will sleep for that many seconds. Unlike system sleep functions, * this one automatically resumes sleep if sleep was interrupted by a * signal. Therefore, calling this function is guaranteed to sleep for * at least the requested amount of time. * \param seconds Number of seconds. * \param mode The timer mode. */ static void sleep(scalar seconds, enum mode mode = relative); private: void added_to_runloop(runloop& runloop); void detach_from_runloop(); function function_; enum mode mode_; scalar absolute_; scalar interval_; timer_source* source_; runloop* runloop_; }; class game_time : public timer_source { public: game_time(scalar timestep = SCALAR(1.0)) : scale_(timestep), scalefactor_(SCALAR(1.0)) { reset(timestep); } scalar ticks() const { return reference_ + scalar(ticks_) * scale_; } void reset() { reference_ = SCALAR(0.0); ticks_ = 0; } void reset(scalar timestep) { reset(); timestep_ = timestep; scale_ = timestep_ * scalefactor_; } void scale(scalar factor) { reference_ = ticks(); ticks_ = 0; scale_ = timestep_ * factor; scalefactor_ = factor; } scalar step(unsigned step = 1) { ticks_ += step; return scale_; } private: scalar reference_; unsigned ticks_; scalar timestep_; scalar scale_; scalar scalefactor_; }; } // namespace moof #endif // _MOOF_TIMER_HH_