-/*] Copyright (c) 2009-2010, Charles McGarvey [**************************
+/*] Copyright (c) 2009-2011, 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.
*
-**************************************************************************/
+*****************************************************************************/
#ifndef _MOOF_TIMER_HH_
#define _MOOF_TIMER_HH_
+#include <boost/bind.hpp>
+#include <boost/function.hpp>
+#include <boost/noncopyable.hpp>
+
+#include <moof/hash.hh>
+#include <moof/math.hh>
+
+
/**
* \file timer.hh
* Functions for measuring time in a friendly unit.
*/
-#include <map>
+namespace moof {
-#include <boost/bind.hpp>
-#include <boost/function.hpp>
-#include <moof/math.hh>
+// 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:
-namespace moof {
+ /**
+ * 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;
+};
-class timer
+
+/**
+ * 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 = -1,
- normal = 0,
- absolute = 1,
- repeat = 2
+ 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<void(timer&,scalar)> function;
-
+ /**
+ * Construct an invalid (uninitialized) timer.
+ */
timer() :
- mode_(invalid) {}
+ 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);
+ }
- timer(const function& function, scalar seconds, mode mode = normal)
+ /**
+ * 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
{
- init(function, seconds, mode);
+ return mode_;
}
- ~timer()
+ /**
+ * 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()
{
- invalidate();
+ fire(source_->ticks());
}
- void init(const function& function, scalar seconds, mode mode = normal);
+ /**
+ * 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());
+ }
- bool is_valid() const;
- void invalidate();
+ /**
+ * Get the absolute time of the next expiration of this timer.
+ * \return Seconds.
+ */
+ scalar expiration() const
+ {
+ return absolute_;
+ }
- void fire();
+ /**
+ * 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());
+ }
- scalar seconds_remaining() const;
- bool is_expired() const;
- bool is_repeating() const;
+ /**
+ * 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 seconds since a fixed, arbitrary point in the
- * past.
+ * 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 seconds after the fixed time in
- * the past. If absolute is false, it will sleep for seconds starting
- * now. 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 the requested amount of time
- * (and maybe longer).
+ * 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, mode mode = normal);
+ 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_;
+};
- static scalar next_expiration()
+
+class game_time : public timer_source
+{
+public:
+
+ game_time(scalar timestep = SCALAR(1.0)) :
+ scale_(timestep),
+ scalefactor_(SCALAR(1.0))
{
- return gNextFire;
+ reset(timestep);
}
- static void fire_expired_timers();
- static void fire_expired_timers(scalar t);
+ 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_;
+ }
-private:
+ void scale(scalar factor)
+ {
+ reference_ = ticks();
+ ticks_ = 0;
+ scale_ = timestep_ * factor;
+ scalefactor_ = factor;
+ }
- static unsigned new_identifier();
- static scalar find_next_expiration();
+ scalar step(unsigned step = 1)
+ {
+ ticks_ += step;
+ return scale_;
+ }
- function function_;
- mode mode_;
- scalar absolute_;
- scalar interval_;
- unsigned id_;
+private:
- static scalar gNextFire;
- static std::map<unsigned,timer*> gTimers;
+ scalar reference_;
+ unsigned ticks_;
+ scalar timestep_;
+ scalar scale_;
+ scalar scalefactor_;
};