From dbb82675402f21236163d8e148c0e2d632fc3beb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20Ludwig?= Date: Sat, 27 Oct 2018 12:33:10 +0200 Subject: [PATCH] Introduce TimerCallback2. This allows getting notified also if the timer has been stopped, so that it is guaranteed to be called, except if cancelWait is called. Necessary for fixing vibe-d/vibe-core#86 --- source/eventcore/driver.d | 8 +++++++- source/eventcore/drivers/timer.d | 16 +++++++++++----- tests/0-timer.d | 15 +++++++++++++++ 3 files changed, 33 insertions(+), 6 deletions(-) diff --git a/source/eventcore/driver.d b/source/eventcore/driver.d index 58fac0f..66481e8 100644 --- a/source/eventcore/driver.d +++ b/source/eventcore/driver.d @@ -564,7 +564,12 @@ interface EventDriverTimers { void stop(TimerID timer); bool isPending(TimerID timer); bool isPeriodic(TimerID timer); - void wait(TimerID timer, TimerCallback callback); + final void wait(TimerID timer, TimerCallback callback) { + wait(timer, (tm, fired) { + if (fired) callback(tm); + }); + } + void wait(TimerID timer, TimerCallback2 callback); void cancelWait(TimerID timer); /** Increments the reference count of the given resource. @@ -668,6 +673,7 @@ alias FileIOCallback = void delegate(FileFD, IOStatus, size_t); alias EventCallback = void delegate(EventID); alias SignalCallback = void delegate(SignalListenID, SignalStatus, int); alias TimerCallback = void delegate(TimerID); +alias TimerCallback2 = void delegate(TimerID, bool fired); alias FileChangesCallback = void delegate(WatcherID, in ref FileChange change); @system alias DataInitializer = void function(void*) @nogc; diff --git a/source/eventcore/drivers/timer.d b/source/eventcore/drivers/timer.d index 0f47f46..dc090c3 100644 --- a/source/eventcore/drivers/timer.d +++ b/source/eventcore/drivers/timer.d @@ -79,7 +79,7 @@ final class LoopTimeoutTimerDriver : EventDriverTimers { foreach (tm; processed_timers) { auto cb = tm.callback; tm.callback = null; - if (cb) cb(tm.id); + if (cb) cb(tm.id, true); } return processed_timers.length > 0; @@ -91,8 +91,8 @@ final class LoopTimeoutTimerDriver : EventDriverTimers { TimerSlot* tm; try tm = ms_allocator.make!TimerSlot; catch (Exception e) return TimerID.invalid; - GC.addRange(tm, TimerSlot.sizeof, typeid(TimerSlot)); assert(tm !is null); + GC.addRange(tm, TimerSlot.sizeof, typeid(TimerSlot)); tm.id = id; tm.refCount = 1; tm.timeout = long.max; @@ -113,8 +113,13 @@ final class LoopTimeoutTimerDriver : EventDriverTimers { final override void stop(TimerID timer) @trusted { + import std.algorithm.mutation : swap; + auto tm = m_timers[timer]; if (!tm.pending) return; + TimerCallback2 cb; + swap(cb, tm.callback); + if (cb) cb(timer, false); tm.pending = false; m_timerQueue.remove(tm); } @@ -129,11 +134,12 @@ final class LoopTimeoutTimerDriver : EventDriverTimers { return m_timers[descriptor].repeatDuration > 0; } - final override void wait(TimerID timer, TimerCallback callback) + final override void wait(TimerID timer, TimerCallback2 callback) { - assert(!m_timers[timer].callback, "Calling wait() no a timer that is already waiting."); + assert(!m_timers[timer].callback, "Calling wait() on a timer that is already waiting."); m_timers[timer].callback = callback; } + alias wait = EventDriverTimers.wait; final override void cancelWait(TimerID timer) { @@ -214,7 +220,7 @@ struct TimerSlot { bool pending; long timeout; // stdtime long repeatDuration; - TimerCallback callback; // TODO: use a list with small-value optimization + TimerCallback2 callback; // TODO: use a list with small-value optimization DataInitializer userDataDestructor; ubyte[16*size_t.sizeof] userData; diff --git a/tests/0-timer.d b/tests/0-timer.d index a91d9fc..cfc420d 100644 --- a/tests/0-timer.d +++ b/tests/0-timer.d @@ -57,6 +57,21 @@ void main() eventDriver.timers.set(tm, 1200.msecs, 0.msecs); + // test if stop() produces a callback with fire==false + bool got_event = false; + auto tm2 = eventDriver.timers.create(); + eventDriver.timers.wait(tm2, (t, fired) { assert(!fired); got_event = true; }); + eventDriver.timers.set(tm2, 100.msecs, 0.msecs); + eventDriver.timers.stop(tm2); + assert(got_event); + + + // test that cancelWait() does not produce a callback + eventDriver.timers.wait(tm2, (t, fired) { assert(false); }); + eventDriver.timers.set(tm2, 100.msecs, 0.msecs); + eventDriver.timers.cancelWait(tm2); + eventDriver.timers.stop(tm2); + ExitReason er; do er = eventDriver.core.processEvents(Duration.max); while (er == ExitReason.idle);