/// <summary> /// Run until next yield, program termination, or completion of scheduled tasklets. /// </summary> public async Task Tick() { // Queue flip because unblocked tasks might unblock further tasks. // TODO: Clear and flip pre-allocated lists instead of constructing a new one each time. var oldUnblocked = unblocked; unblocked = new List <ScheduledTaskRecord>(); oldUnblocked.AddRange(yielded); yielded.Clear(); foreach (var continued in oldUnblocked) { currentTask = null; currentTask = continued; active.Add(currentTask); var theContinuation = currentTask.Continuation; await theContinuation.Continue(); currentTask = null; } lastScheduled = null; oldUnblocked.Clear(); var oldActiveFrames = active; active = new List <ScheduledTaskRecord>(); foreach (var scheduled in oldActiveFrames) { lastScheduled = scheduled; // Currently, we won't bump into these exception escape clauses since we'll bomb out from the checks run after every // active task above, but we're keeping them here for later when we try to make the scheduler more resiliant against // rogue scripts and keep running. // // We need to check the call stack because we have started scheduling functions. Those return from themselves and // fully nuke their call stacks unlike root programs. if (lastScheduled.Frame.EscapedDotNetException != null) { // We want to rethrow while retaining the original stack trace. // https://stackoverflow.com/questions/57383/how-to-rethrow-innerexception-without-losing-stack-trace-in-c scheduled.SubmitterReceipt.NotifyEscapedException(ExceptionDispatchInfo.Capture(lastScheduled.Frame.EscapedDotNetException)); } else if (interpreter.ExceptionEscaped(lastScheduled.Frame)) { scheduled.SubmitterReceipt.NotifyEscapedException(ExceptionDispatchInfo.Capture(new EscapedPyException(lastScheduled.Frame.CurrentException))); } else if (lastScheduled.Frame.callStack.Count == 0) { // Executed a return statement and that nuked the whole stack. This code is done! scheduled.SubmitterReceipt.NotifyCompleted(); } else if (lastScheduled.Frame.BlockStack.Count == 0 && lastScheduled.Frame.Cursor >= lastScheduled.Frame.CodeBytes.Bytes.Length) { scheduled.SubmitterReceipt.NotifyCompleted(); } else { active.Add(lastScheduled); } } ++TickCount; }