X-Git-Url: https://git.dogcows.com/gitweb?p=chaz%2Fyoink;a=blobdiff_plain;f=src%2Fmoof%2Ftimer.hh;h=9f3e9846096d7e09600a053ea929c66e7af91d19;hp=d7a411bc48e82089891795db5602d2cfb42e4326;hb=af88821a172c4dfd138b91b2a5148ae50b502fa2;hpb=831f04d4bc19a390415ac0bbac4331c7a65509bc diff --git a/src/moof/timer.hh b/src/moof/timer.hh index d7a411b..9f3e984 100644 --- a/src/moof/timer.hh +++ b/src/moof/timer.hh @@ -17,60 +17,245 @@ * Functions for measuring time in a friendly unit. */ -#include - #include #include +#include +#include #include 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 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 gTimers; + scalar reference_; + unsigned ticks_; + scalar timestep_; + scalar scale_; };