Merge pull request #98 from vibe-d/issue-86-timer-memory-leak

Implement callback based timers without relying on a task. Fixes #86.
This commit is contained in:
Sönke Ludwig 2018-10-27 20:11:58 +02:00 committed by GitHub
commit ec170e954d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 37 additions and 17 deletions

View file

@ -5,12 +5,14 @@
- Simplified worker task logic by avoiding an explicit event loop - [pull #95][issue95] - Simplified worker task logic by avoiding an explicit event loop - [pull #95][issue95]
- Fixed an issue in `WindowsPath`, where an empty path was converted to "/" when cast to another path type - [pull #91][issue91] - Fixed an issue in `WindowsPath`, where an empty path was converted to "/" when cast to another path type - [pull #91][issue91]
- Fixed two hang issues in `TaskPool` causing the worker task processing to possibly hang at shutdown or to temporarily hang during run time - [pull #96][issue96] - Fixed two hang issues in `TaskPool` causing the worker task processing to possibly hang at shutdown or to temporarily hang during run time - [pull #96][issue96]
- Fixed internal timer callback tasks leaking memory - [issue #86][issue86], [pull #98][issue98]
[issue91]: https://github.com/vibe-d/vibe-core/issues/91 [issue91]: https://github.com/vibe-d/vibe-core/issues/91
[issue92]: https://github.com/vibe-d/vibe-core/issues/92 [issue92]: https://github.com/vibe-d/vibe-core/issues/92
[issue95]: https://github.com/vibe-d/vibe-core/issues/95 [issue95]: https://github.com/vibe-d/vibe-core/issues/95
[issue96]: https://github.com/vibe-d/vibe-core/issues/96 [issue96]: https://github.com/vibe-d/vibe-core/issues/96
[issue97]: https://github.com/vibe-d/vibe-core/issues/97 [issue97]: https://github.com/vibe-d/vibe-core/issues/97
[issue98]: https://github.com/vibe-d/vibe-core/issues/98
1.4.3 - 2018-09-03 1.4.3 - 2018-09-03

View file

@ -4,7 +4,7 @@ authors "Sönke Ludwig"
copyright "Copyright © 2016-2018, rejectedsoftware e.K." copyright "Copyright © 2016-2018, rejectedsoftware e.K."
license "MIT" license "MIT"
dependency "eventcore" version="~>0.8.32" dependency "eventcore" version="~>0.8.39"
dependency "stdx-allocator" version="~>2.77.0" dependency "stdx-allocator" version="~>2.77.0"
targetName "vibe_core" targetName "vibe_core"

View file

@ -742,7 +742,7 @@ unittest {
See_also: createTimer 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 { @safe nothrow {
auto tm = createTimer(callback); auto tm = createTimer(callback);
tm.rearm(timeout, periodic); 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) Timer createTimer(void delegate() nothrow @safe callback)
@safe nothrow { @safe nothrow {
auto ret = Timer(eventDriver.timers.create()); return Timer(eventDriver.timers.create, callback);
if (callback !is null) {
runTask((void delegate() nothrow @safe cb, Timer tm) {
while (!tm.unique || tm.pending) {
tm.wait();
cb();
}
}, callback, ret);
}
return ret;
} }
@ -1036,13 +1027,20 @@ struct Timer {
debug uint m_magicNumber = 0x4d34f916; debug uint m_magicNumber = 0x4d34f916;
} }
alias Callback = void delegate() @safe nothrow;
@safe: @safe:
private this(TimerID id) private this(TimerID id, Callback callback)
nothrow { nothrow {
assert(id != TimerID.init, "Invalid timer ID."); assert(id != TimerID.init, "Invalid timer ID.");
m_driver = eventDriver; m_driver = eventDriver;
m_id = id; m_id = id;
if (callback) {
m_driver.timers.userData!Callback(m_id) = callback;
m_driver.timers.wait(m_id, &TimerCallbackHandler.instance.handle);
}
} }
this(this) this(this)
@ -1054,8 +1052,7 @@ struct Timer {
~this() ~this()
nothrow { nothrow {
debug assert(m_magicNumber == 0x4d34f916, "Timer corrupted."); debug assert(m_magicNumber == 0x4d34f916, "Timer corrupted.");
if (m_driver) if (m_driver) releaseHandle!"timers"(m_id, () @trusted { return cast(shared)m_driver; } ());
releaseHandle!"timers"(m_id, () @trusted { return cast(shared)m_driver; } ());
} }
/// True if the timer is yet to fire. /// True if the timer is yet to fire.
@ -1080,13 +1077,34 @@ struct Timer {
void stop() nothrow { if (m_driver) m_driver.timers.stop(m_id); } void stop() nothrow { if (m_driver) m_driver.timers.stop(m_id); }
/** Waits until the timer fires. /** 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.wait(m_id, cb),
cb => m_driver.timers.cancelWait(m_id) cb => m_driver.timers.cancelWait(m_id)
); );
return res[1];
}
}
struct TimerCallbackHandler {
static TimerCallbackHandler instance;
void handle(TimerID timer, bool fired)
@safe nothrow {
if (fired) {
auto cb = eventDriver.timers.userData!(Timer.Callback)(timer);
cb();
}
if (!eventDriver.timers.isUnique(timer))
eventDriver.timers.wait(timer, &handle);
} }
} }