From 78fef30310c06bcccedb6c491f37b7a586600504 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20Ludwig?= Date: Sat, 27 Oct 2018 15:48:21 +0200 Subject: [PATCH] Implement callback based timers without relying on a task. Fixes #86. In the previous implementation, the callback tasks were starving as soon as the last external reference to a non-pending timer was given up. --- dub.sdl | 2 +- source/vibe/core/core.d | 46 +++++++++++++++++++++++++++-------------- 2 files changed, 31 insertions(+), 17 deletions(-) diff --git a/dub.sdl b/dub.sdl index dcefdc2..f0cdd03 100644 --- a/dub.sdl +++ b/dub.sdl @@ -4,7 +4,7 @@ authors "Sönke Ludwig" copyright "Copyright © 2016-2018, rejectedsoftware e.K." license "MIT" -dependency "eventcore" version="~>0.8.32" +dependency "eventcore" version="~>0.8.39" dependency "stdx-allocator" version="~>2.77.0" targetName "vibe_core" diff --git a/source/vibe/core/core.d b/source/vibe/core/core.d index 41202e1..e40db1d 100644 --- a/source/vibe/core/core.d +++ b/source/vibe/core/core.d @@ -742,7 +742,7 @@ unittest { See_also: createTimer */ -Timer setTimer(Duration timeout, void delegate() nothrow @safe callback, bool periodic = false) +Timer setTimer(Duration timeout, Timer.Callback callback, bool periodic = false) @safe nothrow { auto tm = createTimer(callback); tm.rearm(timeout, periodic); @@ -784,16 +784,7 @@ Timer setTimer(Duration timeout, void delegate() callback, bool periodic = false */ Timer createTimer(void delegate() nothrow @safe callback) @safe nothrow { - auto ret = Timer(eventDriver.timers.create()); - if (callback !is null) { - runTask((void delegate() nothrow @safe cb, Timer tm) { - while (!tm.unique || tm.pending) { - tm.wait(); - cb(); - } - }, callback, ret); - } - return ret; + return Timer(eventDriver.timers.create, callback); } @@ -1036,13 +1027,20 @@ struct Timer { debug uint m_magicNumber = 0x4d34f916; } + alias Callback = void delegate() @safe nothrow; + @safe: - private this(TimerID id) + private this(TimerID id, Callback callback) nothrow { assert(id != TimerID.init, "Invalid timer ID."); m_driver = eventDriver; m_id = id; + + if (callback) { + m_driver.timers.userData!Callback(m_id) = callback; + m_driver.timers.wait(m_id, timerCallbackHandler); + } } this(this) @@ -1054,8 +1052,7 @@ struct Timer { ~this() nothrow { debug assert(m_magicNumber == 0x4d34f916, "Timer corrupted."); - if (m_driver) - releaseHandle!"timers"(m_id, () @trusted { return cast(shared)m_driver; } ()); + if (m_driver) releaseHandle!"timers"(m_id, () @trusted { return cast(shared)m_driver; } ()); } /// True if the timer is yet to fire. @@ -1080,16 +1077,33 @@ struct Timer { void stop() nothrow { if (m_driver) m_driver.timers.stop(m_id); } /** Waits until the timer fires. + + Returns: + `true` is returned $(I iff) the timer was fired. */ - void wait() + bool wait() { - asyncAwait!(TimerCallback, + auto cb = m_driver.timers.userData!Callback(m_id); + assert(cb is null, "Cannot wait on a timer that was created with a callback."); + + auto res = asyncAwait!(TimerCallback2, cb => m_driver.timers.wait(m_id, cb), cb => m_driver.timers.cancelWait(m_id) ); + return res[1]; } } +private immutable TimerCallback2 timerCallbackHandler = (TimerID timer, bool fired) { + if (fired) { + auto cb = eventDriver.timers.userData!(Timer.Callback)(timer); + cb(); + } + + if (!eventDriver.timers.isUnique(timer)) + eventDriver.timers.wait(timer, timerCallbackHandler); +}; + /** Returns an object that ensures that no task switches happen during its life time.