Example #1
0
        private void transferRecord(FrameContext frame, List <ScheduledTaskRecord> fromRecords, List <ScheduledTaskRecord> toRecords, ISubscheduledContinuation continuation)
        {
            var recordIdx = findTaskRecordIndex(frame, fromRecords);
            ScheduledTaskRecord record = fromRecords[recordIdx];

            record.Continuation = continuation;
            fromRecords.RemoveAt(recordIdx);
            toRecords.Add(record);
        }
Example #2
0
        public Scheduler()
        {
            active      = new List <ScheduledTaskRecord>();
            blocked     = new List <ScheduledTaskRecord>();
            unblocked   = new List <ScheduledTaskRecord>();
            yielded     = new List <ScheduledTaskRecord>();
            currentTask = null;

            TickCount = 0;
        }
Example #3
0
        /// <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;
        }