Proper treatment of actor containment; proper treatment of adhoc assertion
This commit is contained in:
parent
72e1653aa6
commit
52f9da8cac
|
@ -38,9 +38,8 @@ const PRIORITY = Object.freeze({
|
||||||
_count: 6
|
_count: 6
|
||||||
});
|
});
|
||||||
|
|
||||||
function Dataspace(container, bootProc) {
|
function Dataspace(bootProc) {
|
||||||
this.nextId = 0;
|
this.nextId = 0;
|
||||||
this.container = container;
|
|
||||||
this.index = new Skeleton.Index();
|
this.index = new Skeleton.Index();
|
||||||
this.dataflow = new Dataflow.Graph();
|
this.dataflow = new Dataflow.Graph();
|
||||||
this.runnable = Immutable.List();
|
this.runnable = Immutable.List();
|
||||||
|
@ -104,7 +103,7 @@ Dataspace.wrapExternal = function (f) {
|
||||||
let ac = savedFacet.actor;
|
let ac = savedFacet.actor;
|
||||||
return function () {
|
return function () {
|
||||||
let actuals = arguments;
|
let actuals = arguments;
|
||||||
ac.dataspace.container.start();
|
ac.dataspace.start();
|
||||||
ac.pushScript(function () {
|
ac.pushScript(function () {
|
||||||
Dataspace.withCurrentFacet(savedFacet, function () {
|
Dataspace.withCurrentFacet(savedFacet, function () {
|
||||||
f.apply(this, actuals);
|
f.apply(this, actuals);
|
||||||
|
@ -114,16 +113,7 @@ Dataspace.wrapExternal = function (f) {
|
||||||
};
|
};
|
||||||
|
|
||||||
Dataspace.backgroundTask = function (k) {
|
Dataspace.backgroundTask = function (k) {
|
||||||
let ground = Dataspace._currentFacet.actor.dataspace.container;
|
Dataspace._currentFacet.actor.dataspace.ground().backgroundTask(k);
|
||||||
let active = true;
|
|
||||||
ground.backgroundTaskCount++;
|
|
||||||
function finish() {
|
|
||||||
if (active) {
|
|
||||||
ground.backgroundTaskCount--;
|
|
||||||
active = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return k ? k(finish) : finish;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Dataspace.referenceField = function (obj, prop) {
|
Dataspace.referenceField = function (obj, prop) {
|
||||||
|
|
|
@ -4,22 +4,36 @@ const Immutable = require('immutable');
|
||||||
const Dataspace = require('./dataspace.js').Dataspace;
|
const Dataspace = require('./dataspace.js').Dataspace;
|
||||||
|
|
||||||
function Ground(bootProc) {
|
function Ground(bootProc) {
|
||||||
|
Dataspace.call(this, bootProc);
|
||||||
this.stepperId = null;
|
this.stepperId = null;
|
||||||
this.stepping = false;
|
this.stepping = false;
|
||||||
this.startingFuel = 1000;
|
this.startingFuel = 1000;
|
||||||
this.dataspace = new Dataspace(this, bootProc);
|
|
||||||
this.stopHandlers = [];
|
this.stopHandlers = [];
|
||||||
this.backgroundTaskCount = 0;
|
this.backgroundTaskCount = 0;
|
||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== 'undefined') {
|
||||||
window._ground = this;
|
window._ground = this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Ground.prototype = new Dataspace(null);
|
||||||
|
|
||||||
Ground._resolved = Promise.resolve();
|
Ground._resolved = Promise.resolve();
|
||||||
Ground.laterCall = function (thunk) {
|
Ground.laterCall = function (thunk) {
|
||||||
Ground._resolved.then(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 () {
|
Ground.prototype.start = function () {
|
||||||
if (!this.stepperId) {
|
if (!this.stepperId) {
|
||||||
this.stepperId = Ground.laterCall(() => {
|
this.stepperId = Ground.laterCall(() => {
|
||||||
|
@ -30,12 +44,16 @@ Ground.prototype.start = function () {
|
||||||
return this; // allows chaining start() immediately after construction
|
return this; // allows chaining start() immediately after construction
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Ground.prototype.ground = function () {
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
Ground.prototype._step = function () {
|
Ground.prototype._step = function () {
|
||||||
this.stepping = true;
|
this.stepping = true;
|
||||||
try {
|
try {
|
||||||
let stillBusy = false;
|
let stillBusy = false;
|
||||||
for (var fuel = this.startingFuel; fuel > 0; fuel--) {
|
for (var fuel = this.startingFuel; fuel > 0; fuel--) {
|
||||||
stillBusy = this.dataspace.runScripts();
|
stillBusy = this.runScripts();
|
||||||
if (!stillBusy) break;
|
if (!stillBusy) break;
|
||||||
}
|
}
|
||||||
if (stillBusy) {
|
if (stillBusy) {
|
||||||
|
|
|
@ -27,17 +27,15 @@ const Observe = Assertions.Observe;
|
||||||
const Inbound = Assertions.Inbound;
|
const Inbound = Assertions.Inbound;
|
||||||
const Outbound = Assertions.Outbound;
|
const Outbound = Assertions.Outbound;
|
||||||
|
|
||||||
|
const Bag = require('./bag.js');
|
||||||
|
|
||||||
const $QuitDataspace = new $Special("quit-dataspace");
|
const $QuitDataspace = new $Special("quit-dataspace");
|
||||||
|
|
||||||
// TODO: container --> metaContainer == ground
|
function NestedDataspace(outerFacet, bootProc) {
|
||||||
// TODO: parent links
|
Dataspace.call(this, bootProc);
|
||||||
// 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);
|
|
||||||
this.outerFacet = outerFacet;
|
this.outerFacet = outerFacet;
|
||||||
}
|
}
|
||||||
NestedDataspace.prototype = new Dataspace(null, null);
|
NestedDataspace.prototype = new Dataspace(null);
|
||||||
|
|
||||||
NestedDataspace.prototype.sendMessage = function (m) {
|
NestedDataspace.prototype.sendMessage = function (m) {
|
||||||
Dataspace.prototype.sendMessage.call(this, m);
|
Dataspace.prototype.sendMessage.call(this, m);
|
||||||
|
@ -46,36 +44,60 @@ NestedDataspace.prototype.sendMessage = function (m) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
NestedDataspace.prototype.endpointHook = function (facet, ep) {
|
NestedDataspace.prototype.endpointHook = function (facet, innerEp) {
|
||||||
Dataspace.prototype.endpointHook.call(this, facet, ep);
|
const innerDs = this;
|
||||||
if (Observe.isClassOf(ep.assertion) && Inbound.isClassOf(ep.assertion[0])) {
|
Dataspace.prototype.endpointHook.call(this, facet, innerEp);
|
||||||
this.installInboundRelay(facet, ep);
|
if (Observe.isClassOf(innerEp.assertion) && Inbound.isClassOf(innerEp.assertion[0])) {
|
||||||
} else if (Outbound.isClassOf(ep.assertion)) {
|
// We know that innerEp.assertion is an Observe(Inbound(...)).
|
||||||
this.installOutboundRelay(facet, ep);
|
// 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) {
|
NestedDataspace.prototype.adjustIndex = function (a, count) {
|
||||||
// We know that innerEp.assertion is an Observe(Inbound(...)).
|
const net = Dataspace.prototype.adjustIndex.call(this, a, count);
|
||||||
// Also, if innerEp.handler exists, it will be consonant with innerEp.assertion.
|
if (Outbound.isClassOf(a)) {
|
||||||
this.hookEndpointLifecycle(innerEp, this.outerFacet.addEndpoint(() => {
|
switch (net) {
|
||||||
return [Observe(innerEp.assertion[0][0]),
|
case Bag.ABSENT_TO_PRESENT:
|
||||||
innerEp.handler && {
|
this.outerFacet.actor.pushScript(() => {
|
||||||
skeleton: innerEp.handler.skeleton[1],
|
this.outerFacet.actor.adhocAssert(a[0]);
|
||||||
constPaths: innerEp.handler.constPaths.map((p) => p.shift()),
|
});
|
||||||
constVals: innerEp.handler.constVals,
|
this.outerFacet.actor.dataspace.start();
|
||||||
capturePaths: innerEp.handler.capturePaths.map((p) => p.shift()),
|
break;
|
||||||
callback: innerEp.handler.callback
|
case Bag.PRESENT_TO_ABSENT:
|
||||||
}];
|
this.outerFacet.actor.pushScript(() => {
|
||||||
}, false));
|
this.outerFacet.actor.adhocRetract(a[0]);
|
||||||
};
|
});
|
||||||
|
this.outerFacet.actor.dataspace.start();
|
||||||
NestedDataspace.prototype.installOutboundRelay = function (facet, innerEp) {
|
break;
|
||||||
// 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 net;
|
||||||
return [innerEp.assertion[0], null];
|
|
||||||
}, false));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
NestedDataspace.prototype.hookEndpointLifecycle = function (innerEp, outerEp) {
|
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) {
|
function inNestedDataspace(bootProc) {
|
||||||
|
@ -112,8 +139,8 @@ function inNestedDataspace(bootProc) {
|
||||||
const outerFacet = Dataspace.currentFacet();
|
const outerFacet = Dataspace.currentFacet();
|
||||||
outerFacet.addDataflow(function () {});
|
outerFacet.addDataflow(function () {});
|
||||||
// ^ eww! Dummy endpoint to keep the root facet of the relay alive.
|
// ^ eww! Dummy endpoint to keep the root facet of the relay alive.
|
||||||
const innerDs = new NestedDataspace(outerFacet, outerFacet.actor.dataspace.container, bootProc);
|
const innerDs = new NestedDataspace(outerFacet, bootProc);
|
||||||
outerFacet.actor.scheduleScript(() => innerDs.start());
|
innerDs.start();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,7 +45,7 @@ spawn named 'client' {
|
||||||
|
|
||||||
console.time('box-and-client-' + N.toString());
|
console.time('box-and-client-' + N.toString());
|
||||||
spawn {
|
spawn {
|
||||||
Dataspace.currentFacet().actor.dataspace.container.addStopHandler(() => {
|
Dataspace.currentFacet().actor.dataspace.ground().addStopHandler(() => {
|
||||||
console.timeEnd('box-and-client-' + N.toString());
|
console.timeEnd('box-and-client-' + N.toString());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@ spawn {
|
||||||
|
|
||||||
console.time('msgspeed-' + N.toString());
|
console.time('msgspeed-' + N.toString());
|
||||||
spawn {
|
spawn {
|
||||||
Dataspace.currentFacet().actor.dataspace.container.addStopHandler(() => {
|
Dataspace.currentFacet().actor.dataspace.ground().addStopHandler(() => {
|
||||||
console.timeEnd('msgspeed-' + N.toString());
|
console.timeEnd('msgspeed-' + N.toString());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
// @syndicate-lang/syntax-test, a demo of Syndicate extensions to JS.
|
||||||
|
// Copyright (C) 2016-2018 Tony Garnock-Jones <tonyg@leastfixedpoint.com>
|
||||||
|
//
|
||||||
|
// 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 <https://www.gnu.org/licenses/>.
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
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');
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue