Merge pull request #162 from vibe-d/issue161-multiple-joiners

Fix Task.join() for multiple callers. Fixes #161.
This commit is contained in:
Leonid Kramer 2019-06-20 09:30:49 +02:00 committed by GitHub
commit a200328a0d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 44 additions and 2 deletions

View file

@ -362,7 +362,7 @@ final package class TaskFiber : Fiber {
import std.algorithm.mutation : swap; import std.algorithm.mutation : swap;
import std.concurrency : Tid, thisTid; import std.concurrency : Tid, thisTid;
import std.encoding : sanitize; import std.encoding : sanitize;
import vibe.core.core : isEventLoopRunning, recycleFiber, taskScheduler, yield; import vibe.core.core : isEventLoopRunning, recycleFiber, taskScheduler, yield, yieldLock;
version (VibeDebugCatchAll) alias UncaughtException = Throwable; version (VibeDebugCatchAll) alias UncaughtException = Throwable;
else alias UncaughtException = Exception; else alias UncaughtException = Exception;
@ -413,6 +413,17 @@ final package class TaskFiber : Fiber {
this.tidInfo.ident = Tid.init; // clear message box this.tidInfo.ident = Tid.init; // clear message box
debug (VibeTaskLog) logTrace("Notifying joining tasks."); debug (VibeTaskLog) logTrace("Notifying joining tasks.");
// Issue #161: This fiber won't be resumed before the next task
// is assigned, because it is already marked as de-initialized.
// Since ManualEvent.emit() will need to switch tasks, this
// would mean that only the first waiter is notified before
// this fiber gets a new task assigned.
// Using a yield lock forces all corresponding tasks to be
// enqueued into the schedule queue and resumed in sequence
// at the end of the scope.
auto l = yieldLock();
m_onExit.emit(); m_onExit.emit();
// make sure that the task does not get left behind in the yielder queue if terminated during yield() // make sure that the task does not get left behind in the yielder queue if terminated during yield()
@ -723,7 +734,7 @@ package struct TaskScheduler {
auto t = Task.getThis(); auto t = Task.getThis();
if (t == Task.init) return; // not really a task -> no-op if (t == Task.init) return; // not really a task -> no-op
auto tf = () @trusted { return t.taskFiber; } (); auto tf = () @trusted { return t.taskFiber; } ();
debug (VibeTaskLog) logTrace("Yielding (interrupt=%s)", tf.m_interrupt); debug (VibeTaskLog) logTrace("Yielding (interrupt=%s)", () @trusted { return (cast(shared)tf).getTaskStatus().interrupt; } ());
tf.handleInterrupt(); tf.handleInterrupt();
if (tf.m_queue !is null) return; // already scheduled to be resumed if (tf.m_queue !is null) return; // already scheduled to be resumed
m_taskQueue.insertBack(tf); m_taskQueue.insertBack(tf);

View file

@ -0,0 +1,31 @@
/+ dub.sdl:
name "tests"
dependency "vibe-core" path=".."
debugVersions "VibeTaskLog" "VibeAsyncLog"
+/
module tests;
import vibe.core.core;
import vibe.core.log;
import vibe.core.sync;
import core.time;
import core.stdc.stdlib : exit;
void main()
{
setTimer(5.seconds, { logError("Test has hung."); exit(1); });
Task t;
runTask({
t = runTask({ sleep(100.msecs); });
t.join();
});
yield();
assert(t && t.running);
t.join();
}