UNPKG

state-trooper

Version:
196 lines (166 loc) • 4.04 kB
"use strict"; var dispatch = require("./dispatch"); var select = require("./select"); var Channel = require("./channels").Channel; var NO_VALUE = {}; var FnHandler = function(blockable, f) { this.f = f; this.blockable = blockable; }; FnHandler.prototype.is_active = function() { return true; }; FnHandler.prototype.is_blockable = function() { return this.blockable; }; FnHandler.prototype.commit = function() { return this.f; }; function put_then_callback(channel, value, callback) { var result = channel._put(value, new FnHandler(true, callback)); if (result && callback) { callback(result.value); } } function take_then_callback(channel, callback) { var result = channel._take(new FnHandler(true, callback)); if (result) { callback(result.value); } } var Process = function(gen, onFinish, creator) { this.gen = gen; this.creatorFunc = creator; this.finished = false; this.onFinish = onFinish; }; var Instruction = function(op, data) { this.op = op; this.data = data; }; var TAKE = "take"; var PUT = "put"; var SLEEP = "sleep"; var ALTS = "alts"; // TODO FIX XXX: This is a (probably) temporary hack to avoid blowing // up the stack, but it means double queueing when the value is not // immediately available Process.prototype._continue = function(response) { var self = this; dispatch.run(function() { self.run(response); }); }; Process.prototype._done = function(value) { if (!this.finished) { this.finished = true; var onFinish = this.onFinish; if (typeof onFinish === "function") { dispatch.run(function() { onFinish(value); }); } } }; Process.prototype.run = function(response) { if (this.finished) { return; } // TODO: Shouldn't we (optionally) stop error propagation here (and // signal the error through a channel or something)? Otherwise the // uncaught exception will crash some runtimes (e.g. Node) var iter = this.gen.next(response); if (iter.done) { this._done(iter.value); return; } var ins = iter.value; var self = this; if (ins instanceof Instruction) { switch (ins.op) { case PUT: var data = ins.data; put_then_callback(data.channel, data.value, function(ok) { self._continue(ok); }); break; case TAKE: var channel = ins.data; take_then_callback(channel, function(value) { self._continue(value); }); break; case SLEEP: var msecs = ins.data; dispatch.queue_delay(function() { self.run(null); }, msecs); break; case ALTS: select.do_alts(ins.data.operations, function(result) { self._continue(result); }, ins.data.options); break; } } else if(ins instanceof Channel) { var channel = ins; take_then_callback(channel, function(value) { self._continue(value); }); } else { this._continue(ins); } }; function take(channel) { return new Instruction(TAKE, channel); } function put(channel, value) { return new Instruction(PUT, { channel: channel, value: value }); } function poll(channel) { if (channel.closed) { return NO_VALUE; } var result = channel._take(new FnHandler(false)); if (result) { return result.value; } else { return NO_VALUE; } } function offer(channel, value) { if (channel.closed) { return false; } var result = channel._put(value, new FnHandler(false)); if (result) { return true; } else { return false; } } function sleep(msecs) { return new Instruction(SLEEP, msecs); } function alts(operations, options) { return new Instruction(ALTS, { operations: operations, options: options }); } exports.put_then_callback = put_then_callback; exports.take_then_callback = take_then_callback; exports.put = put; exports.take = take; exports.offer = offer; exports.poll = poll; exports.sleep = sleep; exports.alts = alts; exports.Instruction = Instruction; exports.Process = Process; exports.NO_VALUE = NO_VALUE;