Add thread management and stepping to runtime

This commit is contained in:
Tim Mickel 2016-04-26 16:50:49 -04:00
parent dbfb3356c6
commit 92dab97b84
5 changed files with 610 additions and 376 deletions

View file

@ -1,5 +1,6 @@
var EventEmitter = require('events');
var Sequencer = require('./sequencer');
var Thread = require('./thread');
var util = require('util');
/**
@ -32,7 +33,7 @@ function Runtime () {
this.threads = [];
/** @type {!Sequencer} */
this.sequencer = new Sequencer();
this.sequencer = new Sequencer(this);
}
/**
@ -40,6 +41,11 @@ function Runtime () {
*/
util.inherits(Runtime, EventEmitter);
/**
* How rapidly we try to step threads, in ms.
*/
Runtime.THREAD_STEP_INTERVAL = 1000 / 60;
/**
* Block management: create blocks and stacks from a `create` event
* @param {!Object} block Blockly create event to be processed
@ -152,6 +158,49 @@ Runtime.prototype.deleteBlock = function (e) {
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
/**
* Create a thread and push it to the list of threads.
* @param {!string} id ID of block that starts the stack
*/
Runtime.prototype._pushThread = function (id) {
if (this.stacks.indexOf(id) < -1) return;
var thread = new Thread(id);
this.threads.push(thread);
};
/**
* Remove a thread from the list of threads.
* @param {!string} id ID of block that starts the stack
*/
Runtime.prototype._removeThread = function (id) {
var i = this.threads.indexOf(id);
if (i > -1) this.threads.splice(i, 1);
};
/**
* Repeatedly run `sequencer.stepThreads` and filter out
* inactive threads after each iteration.
*/
Runtime.prototype._step = function () {
var inactiveThreads = this.sequencer.stepThreads(this.threads);
for (var i = 0; i < inactiveThreads.length; i++) {
this._removeThread(inactiveThreads[i]);
}
};
/**
* Set up timers to repeatedly step in a browser
*/
Runtime.prototype.start = function () {
if (!window.setInterval) return;
window.setInterval(function() {
this._step();
}.bind(this), Runtime.THREAD_STEP_INTERVAL);
};
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
/**
* Helper to remove a stack from `this.stacks`
* @param {?string} id ID of block that starts the stack

View file

@ -1,11 +1,17 @@
var Timer = require('../util/Timer');
function Sequencer () {
function Sequencer (runtime) {
/**
* A utility timer for timing thread sequencing.
* @type {!Timer}
*/
this.timer = new Timer();
/**
* Reference to the runtime owning this sequencer.
* @type {!Runtime}
*/
this.runtime = runtime;
}
/**
@ -18,11 +24,13 @@ Sequencer.WORK_TIME = 1000 / 60;
/**
* Step through all threads in `this.threads`, running them in order.
* @return {Array.<Thread>} New threads after this round of execution.
* @return {Array.<Thread>} All threads which have finished in this iteration.
*/
Sequencer.prototype.stepThreads = function (threads) {
// Start counting toward WORK_TIME
this.timer.start();
// List of threads which have been killed by this step.
var inactiveThreads = [];
// While there are still threads to run and we are within WORK_TIME,
// continue executing threads.
while (threads.length > 0 &&
@ -35,12 +43,14 @@ Sequencer.prototype.stepThreads = function (threads) {
this.stepThread(activeThread);
if (activeThread.nextBlock !== null) {
newThreads.push(activeThread);
} else {
inactiveThreads.push(activeThread);
}
}
// Effectively filters out threads that have stopped.
threads = newThreads;
}
return threads;
return inactiveThreads;
};
/**
@ -48,7 +58,10 @@ Sequencer.prototype.stepThreads = function (threads) {
* @param {!Thread} thread Thread object to step
*/
Sequencer.prototype.stepThread = function (thread) {
// @todo
// @todo Actually run the blocks
// Currently goes to the next block in the sequence.
var nextBlock = this.runtime._getNextBlock(thread.nextBlock);
thread.nextBlock = nextBlock;
};
module.exports = Sequencer;

View file

@ -1,9 +1,14 @@
function Thread () {
/**
* 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) {
/**
* Next block that the thread will execute.
* @type {string}
*/
this.nextBlock = null;
this.nextBlock = firstBlock;
/**
* Stack for the thread. When the sequencer enters a control structure,
* the block is pushed onto the stack so we know where to exit.

894
vm.js

File diff suppressed because it is too large Load diff

11
vm.min.js vendored

File diff suppressed because one or more lines are too long