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
This commit is contained in:
parent
42f96060c9
commit
dbb8267540
|
@ -564,7 +564,12 @@ interface EventDriverTimers {
|
||||||
void stop(TimerID timer);
|
void stop(TimerID timer);
|
||||||
bool isPending(TimerID timer);
|
bool isPending(TimerID timer);
|
||||||
bool isPeriodic(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);
|
void cancelWait(TimerID timer);
|
||||||
|
|
||||||
/** Increments the reference count of the given resource.
|
/** 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 EventCallback = void delegate(EventID);
|
||||||
alias SignalCallback = void delegate(SignalListenID, SignalStatus, int);
|
alias SignalCallback = void delegate(SignalListenID, SignalStatus, int);
|
||||||
alias TimerCallback = void delegate(TimerID);
|
alias TimerCallback = void delegate(TimerID);
|
||||||
|
alias TimerCallback2 = void delegate(TimerID, bool fired);
|
||||||
alias FileChangesCallback = void delegate(WatcherID, in ref FileChange change);
|
alias FileChangesCallback = void delegate(WatcherID, in ref FileChange change);
|
||||||
@system alias DataInitializer = void function(void*) @nogc;
|
@system alias DataInitializer = void function(void*) @nogc;
|
||||||
|
|
||||||
|
|
|
@ -79,7 +79,7 @@ final class LoopTimeoutTimerDriver : EventDriverTimers {
|
||||||
foreach (tm; processed_timers) {
|
foreach (tm; processed_timers) {
|
||||||
auto cb = tm.callback;
|
auto cb = tm.callback;
|
||||||
tm.callback = null;
|
tm.callback = null;
|
||||||
if (cb) cb(tm.id);
|
if (cb) cb(tm.id, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return processed_timers.length > 0;
|
return processed_timers.length > 0;
|
||||||
|
@ -91,8 +91,8 @@ final class LoopTimeoutTimerDriver : EventDriverTimers {
|
||||||
TimerSlot* tm;
|
TimerSlot* tm;
|
||||||
try tm = ms_allocator.make!TimerSlot;
|
try tm = ms_allocator.make!TimerSlot;
|
||||||
catch (Exception e) return TimerID.invalid;
|
catch (Exception e) return TimerID.invalid;
|
||||||
GC.addRange(tm, TimerSlot.sizeof, typeid(TimerSlot));
|
|
||||||
assert(tm !is null);
|
assert(tm !is null);
|
||||||
|
GC.addRange(tm, TimerSlot.sizeof, typeid(TimerSlot));
|
||||||
tm.id = id;
|
tm.id = id;
|
||||||
tm.refCount = 1;
|
tm.refCount = 1;
|
||||||
tm.timeout = long.max;
|
tm.timeout = long.max;
|
||||||
|
@ -113,8 +113,13 @@ final class LoopTimeoutTimerDriver : EventDriverTimers {
|
||||||
|
|
||||||
final override void stop(TimerID timer)
|
final override void stop(TimerID timer)
|
||||||
@trusted {
|
@trusted {
|
||||||
|
import std.algorithm.mutation : swap;
|
||||||
|
|
||||||
auto tm = m_timers[timer];
|
auto tm = m_timers[timer];
|
||||||
if (!tm.pending) return;
|
if (!tm.pending) return;
|
||||||
|
TimerCallback2 cb;
|
||||||
|
swap(cb, tm.callback);
|
||||||
|
if (cb) cb(timer, false);
|
||||||
tm.pending = false;
|
tm.pending = false;
|
||||||
m_timerQueue.remove(tm);
|
m_timerQueue.remove(tm);
|
||||||
}
|
}
|
||||||
|
@ -129,11 +134,12 @@ final class LoopTimeoutTimerDriver : EventDriverTimers {
|
||||||
return m_timers[descriptor].repeatDuration > 0;
|
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;
|
m_timers[timer].callback = callback;
|
||||||
}
|
}
|
||||||
|
alias wait = EventDriverTimers.wait;
|
||||||
|
|
||||||
final override void cancelWait(TimerID timer)
|
final override void cancelWait(TimerID timer)
|
||||||
{
|
{
|
||||||
|
@ -214,7 +220,7 @@ struct TimerSlot {
|
||||||
bool pending;
|
bool pending;
|
||||||
long timeout; // stdtime
|
long timeout; // stdtime
|
||||||
long repeatDuration;
|
long repeatDuration;
|
||||||
TimerCallback callback; // TODO: use a list with small-value optimization
|
TimerCallback2 callback; // TODO: use a list with small-value optimization
|
||||||
|
|
||||||
DataInitializer userDataDestructor;
|
DataInitializer userDataDestructor;
|
||||||
ubyte[16*size_t.sizeof] userData;
|
ubyte[16*size_t.sizeof] userData;
|
||||||
|
|
|
@ -57,6 +57,21 @@ void main()
|
||||||
|
|
||||||
eventDriver.timers.set(tm, 1200.msecs, 0.msecs);
|
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;
|
ExitReason er;
|
||||||
do er = eventDriver.core.processEvents(Duration.max);
|
do er = eventDriver.core.processEvents(Duration.max);
|
||||||
while (er == ExitReason.idle);
|
while (er == ExitReason.idle);
|
||||||
|
|
Loading…
Reference in a new issue