]> Dogcows Code - chaz/yoink/blobdiff - src/moof/timer.cc
the massive refactoring effort
[chaz/yoink] / src / moof / timer.cc
diff --git a/src/moof/timer.cc b/src/moof/timer.cc
new file mode 100644 (file)
index 0000000..2736256
--- /dev/null
@@ -0,0 +1,232 @@
+
+/*]  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 <cerrno>
+#include <ctime>
+#include <limits>
+
+#include <SDL/SDL.h>
+
+#include "log.hh"
+#include "timer.hh"
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+
+namespace moof {
+
+
+scalar timer::gNextFire = std::numeric_limits<scalar>::max();
+std::map<unsigned,timer*> timer::gTimers;
+
+
+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();
+               gTimers.insert(std::pair<unsigned,timer*>(id_, this));
+
+               if (absolute_ < gNextFire) gNextFire = absolute_;
+       }
+}
+
+
+bool timer::is_valid() const
+{
+       return mode_ != invalid;
+}
+
+void timer::invalidate()
+{
+       if (mode_ != invalid)
+       {
+               gTimers.erase(id_);
+               mode_ = invalid;
+
+               if (is_equal(absolute_, gNextFire)) gNextFire = 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, gNextFire)) gNextFire = find_next_expiration();
+       }
+       else
+       {
+               invalidate();
+       }
+}
+
+
+scalar timer::find_next_expiration()
+{
+       std::map<unsigned,timer*>::iterator it;
+       scalar nextFire = std::numeric_limits<scalar>::max();
+
+       for (it = gTimers.begin(); it != gTimers.end(); ++it)
+       {
+               scalar absolute = (*it).second->absolute_;
+               if (absolute < nextFire) nextFire = absolute;
+       }
+
+       return nextFire;
+}
+
+
+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)
+{
+       std::map<unsigned,timer*>::iterator it;
+
+       if (gNextFire > t) return;
+
+       for (it = gTimers.begin(); it != gTimers.end(); ++it)
+       {
+               timer* timer = (*it).second;
+               if (timer->is_expired()) timer->fire();
+       }
+}
+
+
+#if HAVE_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 // ! HAVE_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 // HAVE_CLOCK_GETTIME
+
+
+} // namespace moof
+
This page took 0.020719 seconds and 4 git commands to generate.