Skip to content

Commit

Permalink
Fix yield scheduling
Browse files Browse the repository at this point in the history
  • Loading branch information
Cyberboss committed Nov 27, 2023
1 parent 0e13edb commit 4f0b534
Show file tree
Hide file tree
Showing 4 changed files with 15 additions and 20 deletions.
3 changes: 0 additions & 3 deletions Content.Tests/DMProject/Tests/Sleeping/YieldOrder.dm
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,6 @@
world.log << "Inline:"
TestSequence(MODE_INLINE)

// test fails after this point
return

world.log << "Background:"
TestSequence(MODE_BACKGROUND)

Expand Down
14 changes: 3 additions & 11 deletions OpenDreamRuntime/Procs/DMOpcodeHandlers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
14 changes: 9 additions & 5 deletions OpenDreamRuntime/Procs/ProcScheduler.Delays.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,16 @@ public Task CreateDelay(float deciseconds) {
/// The amount of ticks to sleep.
/// </param>
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);
Expand Down
4 changes: 3 additions & 1 deletion OpenDreamRuntime/Procs/ProcScheduler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ public sealed partial class ProcScheduler {
private readonly Queue<AsyncNativeProc.State> _scheduled = new();
private AsyncNativeProc.State? _current;

bool HasProcsQueued => _scheduled.Count > 0 || _deferredTasks.Count > 0;

public Task Schedule(AsyncNativeProc.State state, Func<AsyncNativeProc.State, Task<DreamValue>> taskFunc) {
async Task Foo() {
state.Result = await taskFunc(state);
Expand All @@ -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();
}
Expand Down

0 comments on commit 4f0b534

Please sign in to comment.