Ground network; minor refactorings and bugfixes; smoketest example
This commit is contained in:
parent
e0f76b991a
commit
9f69cffbe7
|
@ -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>
|
|
@ -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();
|
||||||
|
});
|
|
@ -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;
|
|
@ -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;
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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;
|
|
||||||
|
|
|
@ -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);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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'));
|
||||||
|
|
|
@ -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'));
|
||||||
|
|
Loading…
Reference in New Issue