From 52f9da8cac69c03f917bba8796f94f7c08b67ec7 Mon Sep 17 00:00:00 2001 From: Tony Garnock-Jones Date: Sun, 11 Nov 2018 15:21:44 +0000 Subject: [PATCH] Proper treatment of actor containment; proper treatment of adhoc assertion --- packages/core/src/dataspace.js | 16 +-- packages/core/src/ground.js | 22 ++++- packages/core/src/relay.js | 99 ++++++++++++------- .../syntax-playground/src/assertionspeed.js | 2 +- .../syntax-playground/src/messagespeed.js | 2 +- packages/syntax-playground/src/nested2.js | 50 ++++++++++ 6 files changed, 138 insertions(+), 53 deletions(-) create mode 100644 packages/syntax-playground/src/nested2.js diff --git a/packages/core/src/dataspace.js b/packages/core/src/dataspace.js index 04635fb..0d6bd83 100644 --- a/packages/core/src/dataspace.js +++ b/packages/core/src/dataspace.js @@ -38,9 +38,8 @@ const PRIORITY = Object.freeze({ _count: 6 }); -function Dataspace(container, bootProc) { +function Dataspace(bootProc) { this.nextId = 0; - this.container = container; this.index = new Skeleton.Index(); this.dataflow = new Dataflow.Graph(); this.runnable = Immutable.List(); @@ -104,7 +103,7 @@ Dataspace.wrapExternal = function (f) { let ac = savedFacet.actor; return function () { let actuals = arguments; - ac.dataspace.container.start(); + ac.dataspace.start(); ac.pushScript(function () { Dataspace.withCurrentFacet(savedFacet, function () { f.apply(this, actuals); @@ -114,16 +113,7 @@ Dataspace.wrapExternal = function (f) { }; Dataspace.backgroundTask = function (k) { - let ground = Dataspace._currentFacet.actor.dataspace.container; - let active = true; - ground.backgroundTaskCount++; - function finish() { - if (active) { - ground.backgroundTaskCount--; - active = false; - } - } - return k ? k(finish) : finish; + Dataspace._currentFacet.actor.dataspace.ground().backgroundTask(k); }; Dataspace.referenceField = function (obj, prop) { diff --git a/packages/core/src/ground.js b/packages/core/src/ground.js index f5ae8c0..277264d 100644 --- a/packages/core/src/ground.js +++ b/packages/core/src/ground.js @@ -4,22 +4,36 @@ const Immutable = require('immutable'); const Dataspace = require('./dataspace.js').Dataspace; function Ground(bootProc) { + Dataspace.call(this, bootProc); this.stepperId = null; this.stepping = false; this.startingFuel = 1000; - this.dataspace = new Dataspace(this, bootProc); this.stopHandlers = []; this.backgroundTaskCount = 0; if (typeof window !== 'undefined') { window._ground = this; } } +Ground.prototype = new Dataspace(null); Ground._resolved = Promise.resolve(); Ground.laterCall = function (thunk) { Ground._resolved.then(thunk); }; +Ground.prototype.backgroundTask = function (k) { + const ground = this; + let active = true; + ground.backgroundTaskCount++; + function finish() { + if (active) { + ground.backgroundTaskCount--; + active = false; + } + } + return k ? k(finish) : finish; +}; + Ground.prototype.start = function () { if (!this.stepperId) { this.stepperId = Ground.laterCall(() => { @@ -30,12 +44,16 @@ Ground.prototype.start = function () { return this; // allows chaining start() immediately after construction }; +Ground.prototype.ground = function () { + return this; +}; + Ground.prototype._step = function () { this.stepping = true; try { let stillBusy = false; for (var fuel = this.startingFuel; fuel > 0; fuel--) { - stillBusy = this.dataspace.runScripts(); + stillBusy = this.runScripts(); if (!stillBusy) break; } if (stillBusy) { diff --git a/packages/core/src/relay.js b/packages/core/src/relay.js index 73abe49..23d6368 100644 --- a/packages/core/src/relay.js +++ b/packages/core/src/relay.js @@ -27,17 +27,15 @@ const Observe = Assertions.Observe; const Inbound = Assertions.Inbound; const Outbound = Assertions.Outbound; +const Bag = require('./bag.js'); + const $QuitDataspace = new $Special("quit-dataspace"); -// TODO: container --> metaContainer == ground -// TODO: parent links -// so there's a path up the tree at all times, and also an easy way to get to ground - -function NestedDataspace(outerFacet, container, bootProc) { - Dataspace.call(this, container, bootProc); +function NestedDataspace(outerFacet, bootProc) { + Dataspace.call(this, bootProc); this.outerFacet = outerFacet; } -NestedDataspace.prototype = new Dataspace(null, null); +NestedDataspace.prototype = new Dataspace(null); NestedDataspace.prototype.sendMessage = function (m) { Dataspace.prototype.sendMessage.call(this, m); @@ -46,36 +44,60 @@ NestedDataspace.prototype.sendMessage = function (m) { } }; -NestedDataspace.prototype.endpointHook = function (facet, ep) { - Dataspace.prototype.endpointHook.call(this, facet, ep); - if (Observe.isClassOf(ep.assertion) && Inbound.isClassOf(ep.assertion[0])) { - this.installInboundRelay(facet, ep); - } else if (Outbound.isClassOf(ep.assertion)) { - this.installOutboundRelay(facet, ep); +NestedDataspace.prototype.endpointHook = function (facet, innerEp) { + const innerDs = this; + Dataspace.prototype.endpointHook.call(this, facet, innerEp); + if (Observe.isClassOf(innerEp.assertion) && Inbound.isClassOf(innerEp.assertion[0])) { + // We know that innerEp.assertion is an Observe(Inbound(...)). + // Also, if innerEp.handler exists, it will be consonant with innerEp.assertion. + // Beware of completely-constant patterns, which cause skeleton to be null! + this.hookEndpointLifecycle(innerEp, this.outerFacet.addEndpoint(() => { + const h = innerEp.handler; + return [Observe(innerEp.assertion[0][0]), + h && (h.skeleton === null + ? { + skeleton: null, + constPaths: h.constPaths, + constVals: h.constVals.map((v) => v[0]), + capturePaths: h.capturePaths.map((p) => p.shift()), + callback: function (evt, captures) { + h.callback.call(this, evt, captures); + innerDs.start(); + } + } + : { + skeleton: h.skeleton[1], + constPaths: h.constPaths.map((p) => p.shift()), + constVals: h.constVals, + capturePaths: h.capturePaths.map((p) => p.shift()), + callback: function (evt, captures) { + h.callback.call(this, evt, captures); + innerDs.start(); + } + })]; + }, false)); } }; -NestedDataspace.prototype.installInboundRelay = function (facet, innerEp) { - // We know that innerEp.assertion is an Observe(Inbound(...)). - // Also, if innerEp.handler exists, it will be consonant with innerEp.assertion. - this.hookEndpointLifecycle(innerEp, this.outerFacet.addEndpoint(() => { - return [Observe(innerEp.assertion[0][0]), - innerEp.handler && { - skeleton: innerEp.handler.skeleton[1], - constPaths: innerEp.handler.constPaths.map((p) => p.shift()), - constVals: innerEp.handler.constVals, - capturePaths: innerEp.handler.capturePaths.map((p) => p.shift()), - callback: innerEp.handler.callback - }]; - }, false)); -}; - -NestedDataspace.prototype.installOutboundRelay = function (facet, innerEp) { - // We know that innerEp.assertion is an Outbound(...). - // We may also then conclude that there is no point in installing a handler. - this.hookEndpointLifecycle(innerEp, this.outerFacet.addEndpoint(() => { - return [innerEp.assertion[0], null]; - }, false)); +NestedDataspace.prototype.adjustIndex = function (a, count) { + const net = Dataspace.prototype.adjustIndex.call(this, a, count); + if (Outbound.isClassOf(a)) { + switch (net) { + case Bag.ABSENT_TO_PRESENT: + this.outerFacet.actor.pushScript(() => { + this.outerFacet.actor.adhocAssert(a[0]); + }); + this.outerFacet.actor.dataspace.start(); + break; + case Bag.PRESENT_TO_ABSENT: + this.outerFacet.actor.pushScript(() => { + this.outerFacet.actor.adhocRetract(a[0]); + }); + this.outerFacet.actor.dataspace.start(); + break; + } + } + return net; }; NestedDataspace.prototype.hookEndpointLifecycle = function (innerEp, outerEp) { @@ -105,6 +127,11 @@ NestedDataspace.prototype.start = function () { } }); }); + return this; +}; + +NestedDataspace.prototype.ground = function () { + return this.outerFacet.actor.dataspace.ground(); }; function inNestedDataspace(bootProc) { @@ -112,8 +139,8 @@ function inNestedDataspace(bootProc) { const outerFacet = Dataspace.currentFacet(); outerFacet.addDataflow(function () {}); // ^ eww! Dummy endpoint to keep the root facet of the relay alive. - const innerDs = new NestedDataspace(outerFacet, outerFacet.actor.dataspace.container, bootProc); - outerFacet.actor.scheduleScript(() => innerDs.start()); + const innerDs = new NestedDataspace(outerFacet, bootProc); + innerDs.start(); }; } diff --git a/packages/syntax-playground/src/assertionspeed.js b/packages/syntax-playground/src/assertionspeed.js index 9b5b01c..6c3e770 100644 --- a/packages/syntax-playground/src/assertionspeed.js +++ b/packages/syntax-playground/src/assertionspeed.js @@ -45,7 +45,7 @@ spawn named 'client' { console.time('box-and-client-' + N.toString()); spawn { - Dataspace.currentFacet().actor.dataspace.container.addStopHandler(() => { + Dataspace.currentFacet().actor.dataspace.ground().addStopHandler(() => { console.timeEnd('box-and-client-' + N.toString()); }); } diff --git a/packages/syntax-playground/src/messagespeed.js b/packages/syntax-playground/src/messagespeed.js index a8be71f..b526e23 100644 --- a/packages/syntax-playground/src/messagespeed.js +++ b/packages/syntax-playground/src/messagespeed.js @@ -36,7 +36,7 @@ spawn { console.time('msgspeed-' + N.toString()); spawn { - Dataspace.currentFacet().actor.dataspace.container.addStopHandler(() => { + Dataspace.currentFacet().actor.dataspace.ground().addStopHandler(() => { console.timeEnd('msgspeed-' + N.toString()); }); } diff --git a/packages/syntax-playground/src/nested2.js b/packages/syntax-playground/src/nested2.js new file mode 100644 index 0000000..747d1b3 --- /dev/null +++ b/packages/syntax-playground/src/nested2.js @@ -0,0 +1,50 @@ +//--------------------------------------------------------------------------- +// @syndicate-lang/syntax-test, a demo of Syndicate extensions to JS. +// Copyright (C) 2016-2018 Tony Garnock-Jones +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +//--------------------------------------------------------------------------- + +import { Dataspace, inNestedDataspace, Outbound, Inbound } from '@syndicate-lang/core'; +import { $QuitDataspace } from '@syndicate-lang/core'; + +assertion type Greeting(text); + +spawn named 'A' assert Greeting('Hi from outer space!'); +spawn named 'B' on asserted Greeting($t) console.log('Outer dataspace:', t); + +spawn on retracted Greeting($t) console.log('Vanished:', t); + +spawn dataspace named 'C' { + spawn named 'D' assert Outbound(Greeting('Hi from middle!')); + spawn named 'E' on asserted Inbound(Greeting($t)) console.log('Middle dataspace:', t); + + spawn dataspace named 'F' { + spawn named 'G' { + assert Outbound(Outbound(Greeting('Inner!'))); + on start { + Dataspace.currentFacet().actor.adhocAssert(Outbound(Outbound(Greeting('Adhoc')))); + } + } + spawn named 'H' on asserted Inbound(Inbound(Greeting($t))) console.log('Inner dataspace:', t); + spawn named 'I' on asserted Inbound(Inbound(Greeting('Inner!'))) { + console.log('I: Terminating F'); + ^ $QuitDataspace + }; + } + + spawn named 'J' :asserting Outbound(Greeting('Hello from J')) { + on start throw new Error('Deliberate exception'); + } +}