syndicate-2017/js/src/timer-driver.js

111 lines
3.0 KiB
JavaScript

// Timer event driver
var Patch = require("./patch.js");
var DemandMatcher = require('./demand-matcher.js').DemandMatcher;
var Struct = require('./struct.js');
var Dataspace_ = require("./dataspace.js");
var Dataspace = Dataspace_.Dataspace;
var __ = Dataspace_.__;
var _$ = Dataspace_._$;
var periodicTick = Struct.makeConstructor('periodicTick', ['intervalMS']); // message
var timeLaterThan = Struct.makeConstructor('timeLaterThan', ['deadlineMS']); // assertion
function spawnTimerDriver() {
Dataspace.spawn(
new DemandMatcher([Patch.observe(periodicTick(_$('intervalMS')))],
[Patch.advertise(periodicTick(_$('intervalMS')))],
function (c) {
Dataspace.spawn(new Tick(c.intervalMS));
}));
Dataspace.spawn(
new DemandMatcher([Patch.observe(timeLaterThan(_$('deadlineMS')))],
[Patch.advertise(timeLaterThan(_$('deadlineMS')))],
function (c) {
Dataspace.spawn(new Alarm(c.deadlineMS));
}));
}
function Tick(intervalMS) {
this.intervalMS = intervalMS;
this.handle = null;
}
Tick.prototype.boot = function () {
var self = this;
this.handle = setInterval(Dataspace.wrap(function () {
Dataspace.send(periodicTick(self.intervalMS));
}), this.intervalMS);
return Patch.sub(Patch.observe(periodicTick(this.intervalMS))) // monitor interest
.andThen(Patch.pub(periodicTick(this.intervalMS))) // signal we exist to DemandMatcher
;
};
Tick.prototype.trapexit = function () {
this.cancelTimer();
};
Tick.prototype.cancelTimer = function () {
if (this.handle !== null) {
clearInterval(this.handle);
this.handle = null;
}
};
Tick.prototype.handleEvent = function (e) {
if (e.type === 'stateChange' && e.patch.hasRemoved()) {
Dataspace.exit();
}
};
function Alarm(deadlineMS) {
this.deadlineMS = deadlineMS;
this.handle = null;
}
Alarm.prototype.boot = function () {
this.checkTimeout();
return Patch.sub(Patch.observe(timeLaterThan(this.deadlineMS))) // monitor interest
.andThen(Patch.pub(timeLaterThan(this.deadlineMS))) // signal we exist to DemandMatcher
;
};
Alarm.prototype.checkTimeout = function () {
var now = +(new Date());
var delta = this.deadlineMS - now;
if (delta <= 0) {
Dataspace.stateChange(Patch.assert(timeLaterThan(this.deadlineMS)));
this.cancelTimer();
} else if (this.handle === null) {
var self = this;
this.handle = setTimeout(Dataspace.wrap(function () { self.checkTimeout(); }), delta);
}
};
Alarm.prototype.cancelTimer = function () {
if (this.handle !== null) {
clearTimeout(this.handle);
this.handle = null;
}
};
Alarm.prototype.trapexit = function () {
this.cancelTimer();
};
Alarm.prototype.handleEvent = function (e) {
if (e.type === 'stateChange' && e.patch.hasRemoved()) {
Dataspace.exit();
}
};
///////////////////////////////////////////////////////////////////////////
module.exports.spawnTimerDriver = spawnTimerDriver;
module.exports.periodicTick = periodicTick;
module.exports.timeLaterThan = timeLaterThan;
module.exports.Tick = Tick;
module.exports.Alarm = Alarm;