Fix proper interleaving of busy yield loops inside and outside of tasks.

This commit is contained in:
Sönke Ludwig 2019-03-06 12:35:37 +01:00
parent bfcf08def0
commit cae9f28885

View file

@ -662,10 +662,24 @@ void yield()
} else {
// Let yielded tasks execute
assert(TaskFiber.getThis().m_yieldLockCount == 0, "May not yield within an active yieldLock()!");
() @safe nothrow { performIdleProcessing(true); } ();
() @safe nothrow { performIdleProcessingOnce(true); } ();
}
}
unittest {
size_t ti;
auto t = runTask({
for (ti = 0; ti < 10; ti++)
yield();
});
foreach (i; 0 .. 5) yield();
assert(ti > 0 && ti < 10, "Task did not interleave with yield loop outside of task");
t.join();
assert(ti == 10);
}
/**
Suspends the execution of the calling task until `switchToTask` is called
@ -1244,29 +1258,37 @@ package(vibe) void performIdleProcessing(bool force_process_events = false)
@safe nothrow {
bool again = !getExitFlag();
while (again) {
if (force_process_events) {
again = performIdleProcessingOnce(force_process_events);
force_process_events = true;
}
if (s_scheduler.scheduledTaskCount) logDebug("Exiting from idle processing although there are still yielded tasks");
if (s_exitEventLoop) return;
if (!s_ignoreIdleForGC && s_gcTimer) {
s_gcTimer.rearm(s_gcCollectTimeout);
} else s_ignoreIdleForGC = false;
}
private bool performIdleProcessingOnce(bool process_events)
@safe nothrow {
if (process_events) {
auto er = eventDriver.core.processEvents(0.seconds);
if (er.among!(ExitReason.exited, ExitReason.outOfWaiters) && s_scheduler.scheduledTaskCount == 0) {
if (s_eventLoopRunning) {
logDebug("Setting exit flag due to driver signalling exit: %s", er);
s_exitEventLoop = true;
}
return;
return false;
}
}
} else force_process_events = true;
bool again;
if (s_idleHandler)
again = s_idleHandler();
else again = false;
again = (s_scheduler.schedule() == ScheduleStatus.busy || again) && !getExitFlag();
}
if (s_scheduler.scheduledTaskCount) logDebug("Exiting from idle processing although there are still yielded tasks");
if (!s_ignoreIdleForGC && s_gcTimer) {
s_gcTimer.rearm(s_gcCollectTimeout);
} else s_ignoreIdleForGC = false;
return (s_scheduler.schedule() == ScheduleStatus.busy || again) && !getExitFlag();
}