Ground network; minor refactorings and bugfixes; smoketest example

This commit is contained in:
Tony Garnock-Jones 2016-02-02 21:02:55 -05:00
parent e0f76b991a
commit 9f69cffbe7
9 changed files with 160 additions and 28 deletions

View File

@ -0,0 +1,13 @@
<!doctype html>
<html>
<head>
<title>Syndicate: Smoketest</title>
<meta charset="utf-8">
<script src="../../third-party/jquery-2.2.0.min.js"></script>
<script src="../../dist/syndicate.js"></script>
<script src="index.js"></script>
</head>
<body>
<h1>Smoketest</h1>
</body>
</html>

View File

@ -0,0 +1,31 @@
var G;
$(document).ready(function () {
var Network = Syndicate.Network;
var sub = Syndicate.sub;
var __ = Syndicate.__;
var _$ = Syndicate._$;
G = new Syndicate.Ground(function () {
console.log('starting ground boot');
Network.spawn({
counter: 0,
boot: function () {},
handleEvent: function (e) {},
step: function () {
Network.send(["beep", this.counter++]);
return this.counter <= 10;
}
});
Network.spawn({
boot: function () { return sub(["beep", __]); },
handleEvent: function (e) {
if (e.type === 'message') {
console.log("beep!", e.message[1]);
}
}
});
});
G.startStepping();
});

77
js/src/ground.js Normal file
View File

@ -0,0 +1,77 @@
var Immutable = require('immutable');
var Syndicate = require('./syndicate.js');
var Network = Syndicate.Network;
function Ground(bootFn) {
var self = this;
this.stepperId = null;
this.baseStack = Immutable.List.of({ network: this, activePid: -1 });
Network.withNetworkStack(this.baseStack, function () {
self.network = new Network(bootFn);
});
}
Ground.prototype.step = function () {
var self = this;
return Network.withNetworkStack(this.baseStack, function () {
return self.network.step();
});
};
Ground.prototype.checkPid = function (pid) {
if (pid !== -1) console.error('Weird pid in Ground markPidRunnable', pid);
};
Ground.prototype.markPidRunnable = function (pid) {
this.checkPid(pid);
this.startStepping();
};
Ground.prototype.startStepping = function () {
var self = this;
if (this.stepperId) return;
if (this.step()) {
this.stepperId = setTimeout(function () {
self.stepperId = null;
self.startStepping();
}, 0);
}
};
Ground.prototype.stopStepping = function () {
if (this.stepperId) {
clearTimeout(this.stepperId);
this.stepperId = null;
}
};
Ground.prototype.kill = function (pid, exn) {
this.checkPid(pid);
console.log("Ground network terminated");
this.stopStepping();
};
Ground.prototype.enqueueAction = function (pid, action) {
this.checkPid(pid);
switch (action.type) {
case 'stateChange':
if (action.patch.isNonEmpty()) {
console.error('You have subscribed to a nonexistent event source.',
action.patch.pretty());
}
break;
case 'message':
console.error('You have sent a message into the outer void.', action);
break;
default:
console.error('Internal error: unexpected action at ground level', action);
break;
}
};
///////////////////////////////////////////////////////////////////////////
module.exports.Ground = Ground;

View File

@ -26,12 +26,11 @@ copyKeys(['emptyPatch',
'observe', 'atMeta', 'advertise', 'observe', 'atMeta', 'advertise',
'isObserve', 'isAtMeta', 'isAdvertise', 'isObserve', 'isAtMeta', 'isAdvertise',
'assert', 'retract', 'sub', 'unsub', 'pub', 'unpub', 'assert', 'retract', 'sub', 'unsub', 'pub', 'unpub',
'patchSeq', 'patchSeq'],
'prettyPatch'],
module.exports, module.exports,
module.exports.Patch); module.exports.Patch);
// module.exports.Ground = require("./ground.js").Ground; module.exports.Ground = require("./ground.js").Ground;
// module.exports.Actor = require("./actor.js").Actor; // module.exports.Actor = require("./actor.js").Actor;
// module.exports.Spy = require("./spy.js").Spy; // module.exports.Spy = require("./spy.js").Spy;
// module.exports.WakeDetector = require("./wake-detector.js").WakeDetector; // module.exports.WakeDetector = require("./wake-detector.js").WakeDetector;

View File

@ -87,7 +87,7 @@ function computeAffectedPids(routingTable, delta) {
Mux.prototype.routeMessage = function (body) { Mux.prototype.routeMessage = function (body) {
if (Route.matchValue(this.routingTable, body) === null) { if (Route.matchValue(this.routingTable, body) === null) {
return Route.matchValue(m.routingTable, Patch.observe(body)) || Immutable.Set(); return Route.matchValue(this.routingTable, Patch.observe(body)) || Immutable.Set();
} else { } else {
// Some other stream has declared body // Some other stream has declared body
return Immutable.Set(); return Immutable.Set();

View File

@ -206,9 +206,9 @@ Patch.prototype.projectObjects = function (compiledProjection) {
Route.projectObjects(this.removed, compiledProjection)]; Route.projectObjects(this.removed, compiledProjection)];
}; };
function prettyPatch(p) { Patch.prototype.pretty = function () {
return ("<<<<<<<< Removed:\n" + Route.prettyTrie(p.removed) + return ("<<<<<<<< Removed:\n" + Route.prettyTrie(this.removed) +
"======== Added:\n" + Route.prettyTrie(p.added) + "======== Added:\n" + Route.prettyTrie(this.added) +
">>>>>>>>\n"); ">>>>>>>>\n");
} }
@ -240,4 +240,3 @@ module.exports.unpub = unpub;
module.exports.patchSeq = patchSeq; module.exports.patchSeq = patchSeq;
module.exports.computePatch = computePatch; module.exports.computePatch = computePatch;
module.exports.biasedIntersection = biasedIntersection; module.exports.biasedIntersection = biasedIntersection;
module.exports.prettyPatch = prettyPatch;

View File

@ -101,37 +101,43 @@ Network.exit = function (exn) {
Network.current().kill(Network.activePid(), exn); Network.current().kill(Network.activePid(), exn);
}; };
Network.terminateNetwork = function () { Network.exitNetwork = function () {
Network.enqueueAction(terminateNetwork()); Network.enqueueAction(terminateNetwork());
}; };
Network.inertBehavior = {
handleEvent: function (e) {}
};
// Instance methods // Instance methods
Network.prototype.asChild = function (pid, f, omitLivenessCheck) { Network.prototype.asChild = function (pid, f, omitLivenessCheck) {
var self = this;
var p = this.processTable.get(pid, null); var p = this.processTable.get(pid, null);
if (!omitLivenessCheck && (p === null)) { if (!omitLivenessCheck && (p === null)) {
console.warn("Network.asChild eliding invocation of dead process", pid); console.warn("Network.asChild eliding invocation of dead process", pid);
return; return;
} }
return Network.withWorldStack(Network.stack.push({ network: this, activePid: pid }), return Network.withNetworkStack(
function () { Network.stack.push({ network: this, activePid: pid }),
try { function () {
return f(p); try {
} catch (e) { return f(p);
this.kill(pid, e); } catch (e) {
} self.kill(pid, e);
}); }
});
}; };
Network.prototype.kill = function (pid, exn) { Network.prototype.kill = function (pid, exn) {
if (exn && exn.stack) { if (exn && exn.stack) {
console.log("Process exited", pid, exn, exn.stack); console.log("Process exiting", pid, exn, exn.stack);
} else { } else {
console.log("Process exited", pid, exn); console.log("Process exiting", pid, exn);
} }
var p = this.processTable.get(pid); var p = this.processTable.get(pid);
this.processTable = this.processTable.remove(pid); this.processTable = this.processTable.set(pid, { behavior: Network.inertBehavior });
if (p) { if (p) {
if (p.behavior.trapexit) { if (p.behavior.trapexit) {
this.asChild(pid, function () { return p.behavior.trapexit(exn); }, true); this.asChild(pid, function () { return p.behavior.trapexit(exn); }, true);
@ -172,13 +178,14 @@ Network.prototype.enqueueAction = function (pid, action) {
}; };
Network.prototype.dispatchActions = function () { Network.prototype.dispatchActions = function () {
var self = this;
var actionQueue = this.pendingActions; var actionQueue = this.pendingActions;
this.pendingActions = Immutable.List(); this.pendingActions = Immutable.List();
var alive = true; var alive = true;
actionQueue.forEach(function (entry) { actionQueue.forEach(function (entry) {
var pid = entry[0]; var pid = entry[0];
var action = entry[1]; var action = entry[1];
if (!this.interpretAction(pid, action)) { if (!self.interpretAction(pid, action)) {
alive = false; alive = false;
return false; return false;
} }
@ -191,19 +198,22 @@ Network.prototype.markRunnable = function (pid) {
}; };
Network.prototype.runRunnablePids = function () { Network.prototype.runRunnablePids = function () {
var self = this;
var pidSet = this.runnablePids; var pidSet = this.runnablePids;
this.runnablePids = Immutable.Set(); this.runnablePids = Immutable.Set();
pidSet.forEach(function (pid) { pidSet.forEach(function (pid) {
var childBusy = this.asChild(pid, function (p) { var childBusy = self.asChild(pid, function (p) {
return p.behavior.step // exists, haven't called it yet return p.behavior.step // exists, haven't called it yet
&& p.behavior.step(); && p.behavior.step();
}); });
if (childBusy) this.markRunnable(pid); if (childBusy) self.markRunnable(pid);
}); });
return true; return true;
}; };
Network.prototype.interpretAction = function (pid, action) { Network.prototype.interpretAction = function (pid, action) {
var self = this;
switch (action.type) { switch (action.type) {
case 'stateChange': case 'stateChange':
var oldMux = this.mux.shallowCopy(); var oldMux = this.mux.shallowCopy();
@ -218,7 +228,7 @@ Network.prototype.interpretAction = function (pid, action) {
Network.send(action.message[1]); Network.send(action.message[1]);
} else { } else {
this.mux.routeMessage(action.message).forEach(function (pid) { this.mux.routeMessage(action.message).forEach(function (pid) {
this.deliverEvent(pid, action); self.deliverEvent(pid, action);
}); });
} }
return true; return true;
@ -240,6 +250,8 @@ Network.prototype.interpretAction = function (pid, action) {
case 'terminate': case 'terminate':
var oldMux = this.mux.shallowCopy(); var oldMux = this.mux.shallowCopy();
this.deliverPatches(oldMux, this.mux.removeStream(pid)); this.deliverPatches(oldMux, this.mux.removeStream(pid));
console.log("Process exit complete", pid);
this.processTable = this.processTable.remove(pid);
return true; return true;
case 'terminateNetwork': case 'terminateNetwork':
@ -254,15 +266,16 @@ Network.prototype.interpretAction = function (pid, action) {
}; };
Network.prototype.deliverPatches = function (oldMux, updateStreamResult) { Network.prototype.deliverPatches = function (oldMux, updateStreamResult) {
var self = this;
var events = Mux.computeEvents(oldMux, this.mux, updateStreamResult); var events = Mux.computeEvents(oldMux, this.mux, updateStreamResult);
events.eventMap.forEach(function (patch, pid) { events.eventMap.forEach(function (patch, pid) {
this.deliverEvent(pid, stateChange(patch)); self.deliverEvent(pid, stateChange(patch));
}); });
events.metaEvents.forEach(Network.stateChange); events.metaEvents.forEach(Network.stateChange);
}; };
Network.prototype.deliverEvent = function (pid, event) { Network.prototype.deliverEvent = function (pid, event) {
var childBusy = this.asChild(pid, function (p) { return p.handleEvent(event); }); var childBusy = this.asChild(pid, function (p) { return p.behavior.handleEvent(event); });
if (childBusy) this.markRunnable(pid); if (childBusy) this.markRunnable(pid);
}; };

View File

@ -13,7 +13,7 @@ function checkPrettyTrie(m, expected) {
} }
function checkPrettyPatch(p, expectedAdded, expectedRemoved) { function checkPrettyPatch(p, expectedAdded, expectedRemoved) {
expect(Patch.prettyPatch(p)).to.equal( expect(p.pretty()).to.equal(
('<<<<<<<< Removed:\n' + expectedRemoved.join('\n') + ('<<<<<<<< Removed:\n' + expectedRemoved.join('\n') +
'======== Added:\n' + expectedAdded.join('\n') + '======== Added:\n' + expectedAdded.join('\n') +
'>>>>>>>>\n')); '>>>>>>>>\n'));

View File

@ -8,7 +8,7 @@ var __ = Route.__;
var _$ = Route._$; var _$ = Route._$;
function checkPrettyPatch(p, expectedAdded, expectedRemoved) { function checkPrettyPatch(p, expectedAdded, expectedRemoved) {
expect(Patch.prettyPatch(p)).to.equal( expect(p.pretty()).to.equal(
('<<<<<<<< Removed:\n' + expectedRemoved.join('\n') + ('<<<<<<<< Removed:\n' + expectedRemoved.join('\n') +
'======== Added:\n' + expectedAdded.join('\n') + '======== Added:\n' + expectedAdded.join('\n') +
'>>>>>>>>\n')); '>>>>>>>>\n'));