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',
|
||||
'isObserve', 'isAtMeta', 'isAdvertise',
|
||||
'assert', 'retract', 'sub', 'unsub', 'pub', 'unpub',
|
||||
'patchSeq',
|
||||
'prettyPatch'],
|
||||
'patchSeq'],
|
||||
module.exports,
|
||||
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.Spy = require("./spy.js").Spy;
|
||||
// module.exports.WakeDetector = require("./wake-detector.js").WakeDetector;
|
||||
|
|
|
@ -87,7 +87,7 @@ function computeAffectedPids(routingTable, delta) {
|
|||
|
||||
Mux.prototype.routeMessage = function (body) {
|
||||
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 {
|
||||
// Some other stream has declared body
|
||||
return Immutable.Set();
|
||||
|
|
|
@ -206,9 +206,9 @@ Patch.prototype.projectObjects = function (compiledProjection) {
|
|||
Route.projectObjects(this.removed, compiledProjection)];
|
||||
};
|
||||
|
||||
function prettyPatch(p) {
|
||||
return ("<<<<<<<< Removed:\n" + Route.prettyTrie(p.removed) +
|
||||
"======== Added:\n" + Route.prettyTrie(p.added) +
|
||||
Patch.prototype.pretty = function () {
|
||||
return ("<<<<<<<< Removed:\n" + Route.prettyTrie(this.removed) +
|
||||
"======== Added:\n" + Route.prettyTrie(this.added) +
|
||||
">>>>>>>>\n");
|
||||
}
|
||||
|
||||
|
@ -240,4 +240,3 @@ module.exports.unpub = unpub;
|
|||
module.exports.patchSeq = patchSeq;
|
||||
module.exports.computePatch = computePatch;
|
||||
module.exports.biasedIntersection = biasedIntersection;
|
||||
module.exports.prettyPatch = prettyPatch;
|
||||
|
|
|
@ -101,37 +101,43 @@ Network.exit = function (exn) {
|
|||
Network.current().kill(Network.activePid(), exn);
|
||||
};
|
||||
|
||||
Network.terminateNetwork = function () {
|
||||
Network.exitNetwork = function () {
|
||||
Network.enqueueAction(terminateNetwork());
|
||||
};
|
||||
|
||||
Network.inertBehavior = {
|
||||
handleEvent: function (e) {}
|
||||
};
|
||||
|
||||
// Instance methods
|
||||
|
||||
Network.prototype.asChild = function (pid, f, omitLivenessCheck) {
|
||||
var self = this;
|
||||
var p = this.processTable.get(pid, null);
|
||||
if (!omitLivenessCheck && (p === null)) {
|
||||
console.warn("Network.asChild eliding invocation of dead process", pid);
|
||||
return;
|
||||
}
|
||||
|
||||
return Network.withWorldStack(Network.stack.push({ network: this, activePid: pid }),
|
||||
function () {
|
||||
try {
|
||||
return f(p);
|
||||
} catch (e) {
|
||||
this.kill(pid, e);
|
||||
}
|
||||
});
|
||||
return Network.withNetworkStack(
|
||||
Network.stack.push({ network: this, activePid: pid }),
|
||||
function () {
|
||||
try {
|
||||
return f(p);
|
||||
} catch (e) {
|
||||
self.kill(pid, e);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
Network.prototype.kill = function (pid, exn) {
|
||||
if (exn && exn.stack) {
|
||||
console.log("Process exited", pid, exn, exn.stack);
|
||||
console.log("Process exiting", pid, exn, exn.stack);
|
||||
} else {
|
||||
console.log("Process exited", pid, exn);
|
||||
console.log("Process exiting", pid, exn);
|
||||
}
|
||||
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.behavior.trapexit) {
|
||||
this.asChild(pid, function () { return p.behavior.trapexit(exn); }, true);
|
||||
|
@ -172,13 +178,14 @@ Network.prototype.enqueueAction = function (pid, action) {
|
|||
};
|
||||
|
||||
Network.prototype.dispatchActions = function () {
|
||||
var self = this;
|
||||
var actionQueue = this.pendingActions;
|
||||
this.pendingActions = Immutable.List();
|
||||
var alive = true;
|
||||
actionQueue.forEach(function (entry) {
|
||||
var pid = entry[0];
|
||||
var action = entry[1];
|
||||
if (!this.interpretAction(pid, action)) {
|
||||
if (!self.interpretAction(pid, action)) {
|
||||
alive = false;
|
||||
return false;
|
||||
}
|
||||
|
@ -191,19 +198,22 @@ Network.prototype.markRunnable = function (pid) {
|
|||
};
|
||||
|
||||
Network.prototype.runRunnablePids = function () {
|
||||
var self = this;
|
||||
var pidSet = this.runnablePids;
|
||||
this.runnablePids = Immutable.Set();
|
||||
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
|
||||
&& p.behavior.step();
|
||||
});
|
||||
if (childBusy) this.markRunnable(pid);
|
||||
if (childBusy) self.markRunnable(pid);
|
||||
});
|
||||
return true;
|
||||
};
|
||||
|
||||
Network.prototype.interpretAction = function (pid, action) {
|
||||
var self = this;
|
||||
|
||||
switch (action.type) {
|
||||
case 'stateChange':
|
||||
var oldMux = this.mux.shallowCopy();
|
||||
|
@ -218,7 +228,7 @@ Network.prototype.interpretAction = function (pid, action) {
|
|||
Network.send(action.message[1]);
|
||||
} else {
|
||||
this.mux.routeMessage(action.message).forEach(function (pid) {
|
||||
this.deliverEvent(pid, action);
|
||||
self.deliverEvent(pid, action);
|
||||
});
|
||||
}
|
||||
return true;
|
||||
|
@ -240,6 +250,8 @@ Network.prototype.interpretAction = function (pid, action) {
|
|||
case 'terminate':
|
||||
var oldMux = this.mux.shallowCopy();
|
||||
this.deliverPatches(oldMux, this.mux.removeStream(pid));
|
||||
console.log("Process exit complete", pid);
|
||||
this.processTable = this.processTable.remove(pid);
|
||||
return true;
|
||||
|
||||
case 'terminateNetwork':
|
||||
|
@ -254,15 +266,16 @@ Network.prototype.interpretAction = function (pid, action) {
|
|||
};
|
||||
|
||||
Network.prototype.deliverPatches = function (oldMux, updateStreamResult) {
|
||||
var self = this;
|
||||
var events = Mux.computeEvents(oldMux, this.mux, updateStreamResult);
|
||||
events.eventMap.forEach(function (patch, pid) {
|
||||
this.deliverEvent(pid, stateChange(patch));
|
||||
self.deliverEvent(pid, stateChange(patch));
|
||||
});
|
||||
events.metaEvents.forEach(Network.stateChange);
|
||||
};
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ function checkPrettyTrie(m, expected) {
|
|||
}
|
||||
|
||||
function checkPrettyPatch(p, expectedAdded, expectedRemoved) {
|
||||
expect(Patch.prettyPatch(p)).to.equal(
|
||||
expect(p.pretty()).to.equal(
|
||||
('<<<<<<<< Removed:\n' + expectedRemoved.join('\n') +
|
||||
'======== Added:\n' + expectedAdded.join('\n') +
|
||||
'>>>>>>>>\n'));
|
||||
|
|
|
@ -8,7 +8,7 @@ var __ = Route.__;
|
|||
var _$ = Route._$;
|
||||
|
||||
function checkPrettyPatch(p, expectedAdded, expectedRemoved) {
|
||||
expect(Patch.prettyPatch(p)).to.equal(
|
||||
expect(p.pretty()).to.equal(
|
||||
('<<<<<<<< Removed:\n' + expectedRemoved.join('\n') +
|
||||
'======== Added:\n' + expectedAdded.join('\n') +
|
||||
'>>>>>>>>\n'));
|
||||
|
|
Loading…
Reference in New Issue