From 4f0b5344edf01a243d4852bc01884a2bc70575a9 Mon Sep 17 00:00:00 2001 From: Jordan Dominion Date: Sun, 26 Nov 2023 18:25:35 -0500 Subject: [PATCH] Fix yield scheduling --- .../DMProject/Tests/Sleeping/YieldOrder.dm | 3 --- OpenDreamRuntime/Procs/DMOpcodeHandlers.cs | 14 +++----------- OpenDreamRuntime/Procs/ProcScheduler.Delays.cs | 14 +++++++++----- OpenDreamRuntime/Procs/ProcScheduler.cs | 4 +++- 4 files changed, 15 insertions(+), 20 deletions(-) diff --git a/Content.Tests/DMProject/Tests/Sleeping/YieldOrder.dm b/Content.Tests/DMProject/Tests/Sleeping/YieldOrder.dm index 4ff554ee8f..99513df4a1 100644 --- a/Content.Tests/DMProject/Tests/Sleeping/YieldOrder.dm +++ b/Content.Tests/DMProject/Tests/Sleeping/YieldOrder.dm @@ -49,9 +49,6 @@ world.log << "Inline:" TestSequence(MODE_INLINE) - // test fails after this point - return - world.log << "Background:" TestSequence(MODE_BACKGROUND) diff --git a/OpenDreamRuntime/Procs/DMOpcodeHandlers.cs b/OpenDreamRuntime/Procs/DMOpcodeHandlers.cs index d3d0ff4675..0d600af68a 100644 --- a/OpenDreamRuntime/Procs/DMOpcodeHandlers.cs +++ b/OpenDreamRuntime/Procs/DMOpcodeHandlers.cs @@ -1703,20 +1703,12 @@ public static ProcStatus Spawn(DMProcState state) { // and have state.Spawn return a ProcState instead DreamThread newContext = state.Spawn(); - //Negative delays mean the spawned code runs immediately - if (delayMilliseconds < 0) { + async void Wait() { + await state.ProcScheduler.CreateDelay(delay); newContext.Resume(); - // TODO: Does the rest of the proc get scheduled? - // Does the value of the delay mean anything? - } else { - async void Wait() { - await state.ProcScheduler.CreateDelay(delay); - newContext.Resume(); - } - - Wait(); } + Wait(); state.Jump(jumpTo); return ProcStatus.Continue; } diff --git a/OpenDreamRuntime/Procs/ProcScheduler.Delays.cs b/OpenDreamRuntime/Procs/ProcScheduler.Delays.cs index fd2033230d..71339b2010 100644 --- a/OpenDreamRuntime/Procs/ProcScheduler.Delays.cs +++ b/OpenDreamRuntime/Procs/ProcScheduler.Delays.cs @@ -40,12 +40,16 @@ public Task CreateDelay(float deciseconds) { /// The amount of ticks to sleep. /// public Task CreateDelayTicks(int ticks) { + // When the delay is <= zero, we should run again in the current tick. + // Now, BYOND apparently does have a difference between 0 and -1. See https://github.com/OpenDreamProject/OpenDream/issues/1262#issuecomment-1563663041 + // They both delay execution and allow other sleeping procs in the current tick to run immediately. + // We achieve this by putting the proc on the _deferredTasks lists, so it can be immediately executed again. + if (ticks < 0 && !HasProcsQueued) { + // special case, only yields when there is more work to do + return Task.CompletedTask; + } + if (ticks <= 0) { - // When the delay is <= zero, we should run again in the current tick. - // Now, BYOND apparently does have a difference between 0 and -1, but we're not quite sure what it is yet. - // This is "good enough" for now. - // They both delay execution and allow other sleeping procs in the current tick to run immediately. - // We achieve this by putting the proc on the _deferredTasks lists, so it can be immediately executed again. var defTcs = new TaskCompletionSource(); _deferredTasks.Enqueue(defTcs); diff --git a/OpenDreamRuntime/Procs/ProcScheduler.cs b/OpenDreamRuntime/Procs/ProcScheduler.cs index f4ae1566d5..998a291dad 100644 --- a/OpenDreamRuntime/Procs/ProcScheduler.cs +++ b/OpenDreamRuntime/Procs/ProcScheduler.cs @@ -26,6 +26,8 @@ public sealed partial class ProcScheduler { private readonly Queue _scheduled = new(); private AsyncNativeProc.State? _current; + bool HasProcsQueued => _scheduled.Count > 0 || _deferredTasks.Count > 0; + public Task Schedule(AsyncNativeProc.State state, Func> taskFunc) { async Task Foo() { state.Result = await taskFunc(state); @@ -51,7 +53,7 @@ public void Process() { // If a proc calls sleep(1) or such, it gets put into _deferredTasks. // When we drain the _deferredTasks lists, it'll indirectly schedule things into _scheduled again. // This should all happen synchronously (see above). - while (_scheduled.Count > 0 || _deferredTasks.Count > 0) { + while (HasProcsQueued) { while (_scheduled.TryDequeue(out _current)) { _current.SafeResume(); }