]> Dogcows Code - chaz/yoink/blobdiff - src/moof/timer.hh
further implementing runloop support
[chaz/yoink] / src / moof / timer.hh
index d7a411bc48e82089891795db5602d2cfb42e4326..9f3e9846096d7e09600a053ea929c66e7af91d19 100644 (file)
  * Functions for measuring time in a friendly unit.
  */
 
-#include <map>
-
 #include <boost/bind.hpp>
 #include <boost/function.hpp>
+#include <boost/noncopyable.hpp>
 
+#include <moof/hash.hh>
 #include <moof/math.hh>
 
 
 namespace moof {
 
 
-class timer
+/**
+ * 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;
+};
+
+
+class runloop;
+
+
+/**
+ * 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.
+        */
        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);
+       }
+
+
+       /**
+        * Deconstruct a timer.  This will automagically invalidate the timer,
+        * so it will not expire or fire an event.
+        */
+       ~timer();
 
-       timer(const function& function, scalar seconds, mode mode = normal)
+
+       /**
+        * 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);
 
-       bool is_valid() const;
-       void invalidate();
+       /**
+        * 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.
+        */
+       scalar fire_if_expired(scalar t)
+       {
+               if (is_expired(t)) fire(t);
+               return absolute_;
+       }
+       scalar 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_;
+       }
 
-       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());
+       }
+
+
+       /**
+        * 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());
+       }
 
-       scalar seconds_remaining() const;
-       bool is_expired() const;
-       bool is_repeating() const;
 
+       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();
@@ -78,40 +263,78 @@ public:
 
        /**
         * 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_;
+};
+
+
+
+
+class game_time : public timer_source
+{
+public:
 
+       game_time(scalar timestep) :
+               timestep_(timestep),
+               scale_(timestep)
+       {
+               reset();
+       }
 
-       static scalar next_expiration()
+       scalar ticks() const
        {
-               return gNextFire;
+               return reference_ + scalar(ticks_) * scale_;
        }
 
-       static void fire_expired_timers();
-       static void fire_expired_timers(scalar t);
+       void reset()
+       {
+               reference_ = SCALAR(0.0);
+               ticks_ = 0;
+       }
 
+       void scale(scalar factor)
+       {
+               reference_ = ticks();
+               ticks_ = 1;
+               scale_ = timestep_ * factor;
+       }
 
-private:
 
-       static unsigned new_identifier();
-       static scalar find_next_expiration();
+       unsigned step(unsigned step = 1)
+       {
+               ticks_ += step;
+               return ticks_;
+       }
+
 
-       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_;
 };
 
 
This page took 0.023636 seconds and 4 git commands to generate.