Add single-frame yield mode

This commit is contained in:
Tim Mickel 2016-07-01 10:44:43 -04:00
parent 5876681bc7
commit de6ba08866
4 changed files with 39 additions and 11 deletions

View file

@ -37,7 +37,13 @@ Scratch3ControlBlocks.prototype.repeat = function(args, util) {
};
Scratch3ControlBlocks.prototype.forever = function(args, util) {
util.startSubstack();
if (!util.stackFrame.executed) {
util.stackFrame.executed = true;
util.startSubstack();
} else {
util.stackFrame.executed = false;
util.yieldFrame();
}
};
Scratch3ControlBlocks.prototype.wait = function(args) {

View file

@ -63,8 +63,14 @@ var execute = function (sequencer, thread) {
var primitiveReportedValue = null;
primitiveReportedValue = blockFunction(argValues, {
yield: thread.yield.bind(thread),
yield: function() {
thread.setStatus(Thread.STATUS_YIELD);
},
yieldFrame: function() {
thread.setStatus(Thread.STATUS_YIELD_FRAME);
},
done: function() {
thread.setStatus(Thread.STATUS_RUNNING);
sequencer.proceedThread(thread);
},
stackFrame: currentStackFrame.executionContext,
@ -84,17 +90,19 @@ var execute = function (sequencer, thread) {
if (isPromise) {
if (thread.status === Thread.STATUS_RUNNING) {
// Primitive returned a promise; automatically yield thread.
thread.status = Thread.STATUS_YIELD;
thread.setStatus(Thread.STATUS_YIELD);
}
// Promise handlers
primitiveReportedValue.then(function(resolvedValue) {
// Promise resolved: the primitive reported a value.
thread.pushReportedValue(resolvedValue);
thread.setStatus(Thread.STATUS_RUNNING);
sequencer.proceedThread(thread);
}, function(rejectionReason) {
// Promise rejected: the primitive had some error.
// Log it and proceed.
console.warn('Primitive rejected promise: ', rejectionReason);
thread.setStatus(Thread.STATUS_RUNNING);
sequencer.proceedThread(thread);
});
} else if (thread.status === Thread.STATUS_RUNNING) {

View file

@ -36,6 +36,13 @@ Sequencer.prototype.stepThreads = function (threads) {
var inactiveThreads = [];
// If all of the threads are yielding, we should yield.
var numYieldingThreads = 0;
// Clear all yield statuses that were for the previous frame.
for (var t = 0; t < threads.length; t++) {
if (threads[t].status === Thread.STATUS_YIELD_FRAME) {
threads[t].setStatus(Thread.STATUS_RUNNING);
}
}
// While there are still threads to run and we are within WORK_TIME,
// continue executing threads.
while (threads.length > 0 &&
@ -51,8 +58,10 @@ Sequencer.prototype.stepThreads = function (threads) {
if (activeThread.status === Thread.STATUS_RUNNING) {
// Normal-mode thread: step.
this.startThread(activeThread);
} else if (activeThread.status === Thread.STATUS_YIELD) {
} else if (activeThread.status === Thread.STATUS_YIELD ||
activeThread.status === Thread.STATUS_YIELD_FRAME) {
// Yielding thread: do nothing for this step.
numYieldingThreads++;
continue;
}
if (activeThread.stack.length === 0 &&
@ -148,8 +157,6 @@ Sequencer.prototype.proceedThread = function (thread) {
var currentBlockId = thread.peekStack();
// Mark the status as done and proceed to the next block.
this.runtime.glowBlock(currentBlockId, false);
// If the block was yielding, move back to running state.
thread.status = Thread.STATUS_RUNNING;
// Pop from the stack - finished this level of execution.
thread.popStack();
// Push next connected block, if there is one.
@ -164,7 +171,7 @@ Sequencer.prototype.proceedThread = function (thread) {
}
// If we still can't find a next block to run, mark the thread as done.
if (thread.peekStack() === null) {
thread.status = Thread.STATUS_DONE;
thread.setStatus(Thread.STATUS_DONE);
}
};

View file

@ -46,12 +46,18 @@ Thread.STATUS_RUNNING = 0;
*/
Thread.STATUS_YIELD = 1;
/**
* Thread status for a single-frame yield.
* @const
*/
Thread.STATUS_YIELD_FRAME = 2;
/**
* Thread status for a finished/done thread.
* Thread is in this state when there are no more blocks to execute.
* @const
*/
Thread.STATUS_DONE = 2;
Thread.STATUS_DONE = 3;
/**
* Push stack and update stack frames appropriately.
@ -118,10 +124,11 @@ Thread.prototype.pushReportedValue = function (value) {
};
/**
* Yields the thread.
* Set thread status.
* @param {number} status Enum representing thread status.
*/
Thread.prototype.yield = function () {
this.status = Thread.STATUS_YIELD;
Thread.prototype.setStatus = function (status) {
this.status = status;
};
module.exports = Thread;