2016-06-17 14:36:36 -04:00
|
|
|
var YieldTimers = require('../util/yieldtimers.js');
|
|
|
|
|
2016-04-26 16:50:49 -04:00
|
|
|
/**
|
|
|
|
* A thread is a running stack context and all the metadata needed.
|
|
|
|
* @param {?string} firstBlock First block to execute in the thread.
|
|
|
|
* @constructor
|
|
|
|
*/
|
|
|
|
function Thread (firstBlock) {
|
2016-04-29 17:31:04 -04:00
|
|
|
/**
|
2016-05-02 13:05:48 -04:00
|
|
|
* ID of top block of the thread
|
|
|
|
* @type {!string}
|
2016-04-29 17:31:04 -04:00
|
|
|
*/
|
|
|
|
this.topBlock = firstBlock;
|
2016-06-09 17:08:30 -04:00
|
|
|
|
2016-04-26 15:00:45 -04:00
|
|
|
/**
|
|
|
|
* Stack for the thread. When the sequencer enters a control structure,
|
|
|
|
* the block is pushed onto the stack so we know where to exit.
|
|
|
|
* @type {Array.<string>}
|
|
|
|
*/
|
|
|
|
this.stack = [];
|
2016-05-02 18:09:02 -04:00
|
|
|
|
2016-05-03 13:28:24 -04:00
|
|
|
/**
|
|
|
|
* Stack frames for the thread. Store metadata for the executing blocks.
|
|
|
|
* @type {Array.<Object>}
|
|
|
|
*/
|
|
|
|
this.stackFrames = [];
|
|
|
|
|
2016-05-02 18:09:02 -04:00
|
|
|
/**
|
|
|
|
* Status of the thread, one of three states (below)
|
|
|
|
* @type {number}
|
|
|
|
*/
|
|
|
|
this.status = 0; /* Thread.STATUS_RUNNING */
|
|
|
|
|
|
|
|
/**
|
2016-06-17 14:36:36 -04:00
|
|
|
* Execution-synced timeouts.
|
2016-05-02 18:09:02 -04:00
|
|
|
* @type {number}
|
|
|
|
*/
|
2016-06-17 14:36:36 -04:00
|
|
|
this.timeoutIds = [];
|
2016-04-18 17:20:30 -04:00
|
|
|
}
|
|
|
|
|
2016-05-02 18:09:02 -04:00
|
|
|
/**
|
|
|
|
* Thread status for initialized or running thread.
|
|
|
|
* Threads are in this state when the primitive is called for the first time.
|
|
|
|
* @const
|
|
|
|
*/
|
|
|
|
Thread.STATUS_RUNNING = 0;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Thread status for a yielded thread.
|
|
|
|
* Threads are in this state when a primitive has yielded.
|
|
|
|
* @const
|
|
|
|
*/
|
|
|
|
Thread.STATUS_YIELD = 1;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Thread status for a finished/done thread.
|
|
|
|
* Thread is moved to this state when the interpreter
|
|
|
|
* can proceed with execution.
|
|
|
|
* @const
|
|
|
|
*/
|
|
|
|
Thread.STATUS_DONE = 2;
|
|
|
|
|
2016-06-09 14:22:58 -04:00
|
|
|
/**
|
|
|
|
* Push stack and update stack frames appropriately.
|
|
|
|
* @param {string} blockId Block ID to push to stack.
|
|
|
|
*/
|
|
|
|
Thread.prototype.pushStack = function (blockId) {
|
|
|
|
this.stack.push(blockId);
|
|
|
|
// Push an empty stack frame, if we need one.
|
|
|
|
// Might not, if we just popped the stack.
|
|
|
|
if (this.stack.length > this.stackFrames.length) {
|
2016-06-17 15:10:12 -04:00
|
|
|
this.stackFrames.push({
|
|
|
|
reported: {}, // Collects reported input values.
|
2016-06-17 15:53:58 -04:00
|
|
|
waitingReporter: null, // Name of waiting reporter.
|
2016-06-17 15:10:12 -04:00
|
|
|
executionContext: {} // A context passed to block implementations.
|
|
|
|
});
|
2016-06-09 14:22:58 -04:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Pop last block on the stack and its stack frame.
|
2016-06-09 17:08:30 -04:00
|
|
|
* @return {string} Block ID popped from the stack.
|
2016-06-09 14:22:58 -04:00
|
|
|
*/
|
|
|
|
Thread.prototype.popStack = function () {
|
|
|
|
this.stackFrames.pop();
|
|
|
|
return this.stack.pop();
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
2016-06-09 17:08:30 -04:00
|
|
|
* Get top stack item.
|
|
|
|
* @return {?string} Block ID on top of stack.
|
|
|
|
*/
|
|
|
|
Thread.prototype.peekStack = function () {
|
|
|
|
return this.stack[this.stack.length - 1];
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get top stack frame.
|
2016-06-09 14:22:58 -04:00
|
|
|
* @return {?Object} Last stack frame stored on this thread.
|
|
|
|
*/
|
2016-06-09 17:08:30 -04:00
|
|
|
Thread.prototype.peekStackFrame = function () {
|
2016-06-09 14:22:58 -04:00
|
|
|
return this.stackFrames[this.stackFrames.length - 1];
|
|
|
|
};
|
|
|
|
|
2016-06-20 14:10:19 -04:00
|
|
|
/**
|
|
|
|
* Get stack frame above the current top.
|
|
|
|
* @return {?Object} Second to last stack frame stored on this thread.
|
|
|
|
*/
|
|
|
|
Thread.prototype.peekParentStackFrame = function () {
|
|
|
|
return this.stackFrames[this.stackFrames.length - 2];
|
|
|
|
};
|
|
|
|
|
2016-06-17 15:10:12 -04:00
|
|
|
/**
|
|
|
|
* Push a reported value to the parent of the current stack frame.
|
|
|
|
* @param {!Any} value Reported value to push.
|
|
|
|
*/
|
2016-06-17 15:53:58 -04:00
|
|
|
Thread.prototype.pushReportedValue = function (value) {
|
2016-06-20 14:10:19 -04:00
|
|
|
var parentStackFrame = this.peekParentStackFrame();
|
2016-06-17 15:10:12 -04:00
|
|
|
if (parentStackFrame) {
|
2016-06-17 15:53:58 -04:00
|
|
|
var waitingReporter = parentStackFrame.waitingReporter;
|
|
|
|
parentStackFrame.reported[waitingReporter] = value;
|
|
|
|
parentStackFrame.waitingReporter = null;
|
2016-06-17 15:10:12 -04:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2016-06-09 14:22:58 -04:00
|
|
|
/**
|
|
|
|
* Yields the thread.
|
|
|
|
*/
|
|
|
|
Thread.prototype.yield = function () {
|
|
|
|
this.status = Thread.STATUS_YIELD;
|
|
|
|
};
|
|
|
|
|
2016-06-17 14:36:36 -04:00
|
|
|
/**
|
|
|
|
* Add an execution-synced timeouts for this thread.
|
|
|
|
* See also: util/yieldtimers.js:timeout
|
|
|
|
* @param {!Function} callback To be called when the timer is done.
|
|
|
|
* @param {number} timeDelta Time to wait, in ms.
|
|
|
|
*/
|
|
|
|
Thread.prototype.addTimeout = function (callback, timeDelta) {
|
|
|
|
var timeoutId = YieldTimers.timeout(callback, timeDelta);
|
|
|
|
this.timeoutIds.push(timeoutId);
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Attempt to resolve all execution-synced timeouts on this thread.
|
|
|
|
*/
|
|
|
|
Thread.prototype.resolveTimeouts = function () {
|
|
|
|
var newTimeouts = [];
|
|
|
|
for (var i = 0; i < this.timeoutIds.length; i++) {
|
|
|
|
var resolved = YieldTimers.resolve(this.timeoutIds[i]);
|
|
|
|
if (!resolved) {
|
|
|
|
newTimeouts.push(this.timeoutIds[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
this.timeoutIds = newTimeouts;
|
|
|
|
};
|
|
|
|
|
2016-04-18 17:20:30 -04:00
|
|
|
module.exports = Thread;
|