summaryrefslogtreecommitdiff
path: root/src/common
diff options
context:
space:
mode:
authorBen Longbons <b.r.longbons@gmail.com>2013-04-17 13:22:58 -0700
committerBen Longbons <b.r.longbons@gmail.com>2013-04-27 14:09:22 -0700
commitd18f5bdb682a1d9c6e3a191926bfd46d36e813c1 (patch)
treec987d53ea924b761e5445572a438e0c4bc825d48 /src/common
parentda6b990ca1f553a017003f32a436304c66c62b9e (diff)
downloadtmwa-d18f5bdb682a1d9c6e3a191926bfd46d36e813c1.tar.gz
tmwa-d18f5bdb682a1d9c6e3a191926bfd46d36e813c1.tar.bz2
tmwa-d18f5bdb682a1d9c6e3a191926bfd46d36e813c1.tar.xz
tmwa-d18f5bdb682a1d9c6e3a191926bfd46d36e813c1.zip
Force timers to be managed
Diffstat (limited to 'src/common')
-rw-r--r--src/common/timer.cpp83
-rw-r--r--src/common/timer.hpp25
-rw-r--r--src/common/timer.t.hpp42
3 files changed, 106 insertions, 44 deletions
diff --git a/src/common/timer.cpp b/src/common/timer.cpp
index 219efd9..7b115d9 100644
--- a/src/common/timer.cpp
+++ b/src/common/timer.cpp
@@ -15,12 +15,22 @@
struct TimerData
{
+ /// This will be reset on call, to avoid problems.
+ Timer *owner;
+
/// When it will be triggered
tick_t tick;
/// What will be done
timer_func func;
/// Repeat rate - 0 for oneshot
interval_t interval;
+
+ TimerData(Timer *o, tick_t t, timer_func f, interval_t i)
+ : owner(o)
+ , tick(t)
+ , func(std::move(f))
+ , interval(i)
+ {}
};
struct TimerCompare
@@ -52,6 +62,30 @@ tick_t milli_clock::now(void) noexcept
}
static
+void do_nothing(TimerData *, tick_t)
+{
+}
+
+void Timer::cancel()
+{
+ if (!td)
+ return;
+
+ assert (this == td->owner);
+ td->owner = nullptr;
+ td->func = do_nothing;
+ td->interval = interval_t::zero();
+ td = nullptr;
+}
+
+void Timer::detach()
+{
+ assert (this == td->owner);
+ td->owner = nullptr;
+ td = nullptr;
+}
+
+static
void push_timer_heap(TimerData *td)
{
timer_heap.push(td);
@@ -71,34 +105,39 @@ void pop_timer_heap(void)
timer_heap.pop();
}
-TimerData *add_timer(tick_t tick, timer_func func)
-{
- return add_timer_interval(tick, std::move(func), interval_t::zero());
-}
-
-TimerData *add_timer_interval(tick_t tick, timer_func func, interval_t interval)
+Timer::Timer(tick_t tick, timer_func func, interval_t interval)
+: td(new TimerData(this, tick, std::move(func), interval))
{
assert (interval >= interval_t::zero());
- TimerData *td = new TimerData();
- td->tick = tick;
- td->func = std::move(func);
- td->interval = interval;
push_timer_heap(td);
- return td;
}
-static
-void do_nothing(TimerData *, tick_t)
+Timer::Timer(Timer&& t)
+: td(t.td)
{
+ t.td = nullptr;
+ if (td)
+ {
+ assert (td->owner == &t);
+ td->owner = this;
+ }
}
-void delete_timer(TimerData *td)
+Timer& Timer::operator = (Timer&& t)
{
- assert (td != nullptr);
-
- td->func = do_nothing;
- td->interval = interval_t::zero();
+ std::swap(td, t.td);
+ if (td)
+ {
+ assert (td->owner == &t);
+ td->owner = this;
+ }
+ if (t.td)
+ {
+ assert (t.td->owner == this);
+ t.td->owner = &t;
+ }
+ return *this;
}
interval_t do_timer(tick_t tick)
@@ -118,7 +157,13 @@ interval_t do_timer(tick_t tick)
}
pop_timer_heap();
- // If we are too far past the requested tick, call with the current tick instead to fix reregistering problems
+ // Prevent destroying the object we're in.
+ // Note: this would be surprising in an interval timer,
+ // but all interval timers do an immediate explicit detach().
+ if (td->owner)
+ td->owner->detach();
+ // If we are too far past the requested tick, call with
+ // the current tick instead to fix reregistration problems
if (td->tick + std::chrono::seconds(1) < tick)
td->func(td, tick);
else
diff --git a/src/common/timer.hpp b/src/common/timer.hpp
index 876d519..c581377 100644
--- a/src/common/timer.hpp
+++ b/src/common/timer.hpp
@@ -5,12 +5,6 @@
# include "sanity.hpp"
-# include <chrono>
-# include <functional>
-
-/// (to get additional arguments, use std::bind or a lambda).
-typedef std::function<void (TimerData *, tick_t)> timer_func;
-
// updated automatically when using milli_clock::now()
// which is done only by core.cpp
extern tick_t gettick_cache;
@@ -21,23 +15,8 @@ tick_t gettick(void)
return gettick_cache;
}
-/// Schedule a one-shot timer at the given tick.
-/// The timer will automatically be freed after it is called
-/// (during a do_timer).
-TimerData *add_timer(tick_t t, timer_func f);
-
-/// Schedule a recurring timer initially at the given tick.
-/// The timer will automatically reregister itself, with the same
-/// opaque handle, every interval after the tick.
-/// It will never be freed unless you use delete_timer.
-TimerData *add_timer_interval(tick_t, timer_func, interval_t);
-
-/// Cancel the given timer.
-/// This doesn't actually remove it, it just resets the functor.
-/// and waits for the the tick to arrive in do_timer.
-void delete_timer(TimerData *);
-
-/// Do all timers scheduled before tick, and return the number of milliseconds until the next timer happens
+/// Do all timers scheduled before tick, and return the number of
+/// milliseconds until the next timer happens
interval_t do_timer(tick_t tick);
/// Stat a file, and return its modification time, truncated to seconds.
diff --git a/src/common/timer.t.hpp b/src/common/timer.t.hpp
index 67d7450..ee9b5d2 100644
--- a/src/common/timer.t.hpp
+++ b/src/common/timer.t.hpp
@@ -2,6 +2,9 @@
#define TIMER_T_HPP
# include <chrono>
+# include <functional>
+
+struct TimerData;
/// An implementation of the C++ "clock" concept, exposing
/// durations in milliseconds.
@@ -21,8 +24,43 @@ public:
typedef milli_clock::time_point tick_t;
/// The difference between two points in time.
typedef milli_clock::duration interval_t;
+/// (to get additional arguments, use std::bind or a lambda).
+typedef std::function<void (TimerData *, tick_t)> timer_func;
-/// Opaque type representing an active timer.
-struct TimerData;
+class Timer
+{
+ friend struct TimerData;
+ TimerData *td;
+
+ Timer(const Timer&) = delete;
+ Timer& operator = (const Timer&) = delete;
+public:
+ /// Don't own anything yet.
+ Timer() : td(nullptr) {}
+ /// Schedule a timer for the given tick.
+ /// If you do not wish to keep track of it, call disconnect().
+ /// Otherwise, you may cancel() or replace (operator =) it later.
+ ///
+ /// If the interval argument is given, the timer will reschedule
+ /// itself again forever. Otherwise, it will disconnect() itself
+ /// just BEFORE it is called.
+ Timer(tick_t tick, timer_func func, interval_t interval=interval_t::zero());
+
+ Timer(Timer&& t);
+ Timer& operator = (Timer&& t);
+ ~Timer() { cancel(); }
+
+ /// Cancel the delivery of this timer's function, and make it falsy.
+ /// Implementation note: this doesn't actually remove it, just sets
+ /// the functor to do_nothing, and waits for the tick before removing.
+ void cancel();
+ /// Make it falsy without cancelling the timer,
+ void detach();
+
+ /// Check if there is a timer connected.
+ explicit operator bool() { return td; }
+ /// Check if there is no connected timer.
+ bool operator !() { return !td; }
+};
#endif // TIMER_T_HPP