diff --git a/js/compiler/demo-santa.js b/js/compiler/demo-santa.js new file mode 100644 index 0000000..52deae3 --- /dev/null +++ b/js/compiler/demo-santa.js @@ -0,0 +1,138 @@ +// bin/syndicatec compiler/demo-santa.js | node +// +// Santa Claus Problem +// https://www.schoolofhaskell.com/school/advanced-haskell/beautiful-concurrency/4-the-santa-claus-problem +// https://arxiv.org/pdf/1810.09613.pdf + +var Immutable = require('immutable'); +var Syndicate = require('./src/main.js'); + +assertion type elfProblem(id); +assertion type reindeerReturned(id); + +assertion type problemResolved(id); +assertion type deliverToys(); + +function sleep(milliseconds, continuation) { + react { + var deadline = +(new Date()) + milliseconds; + stop on asserted Syndicate.Timer.timeLaterThan(deadline) { + continuation(); + } + } +} + +var elfCount = 0; +function elf() { + var elfId = "elf" + ++elfCount; + spawn* named elfId { + function workIndustriously() { + console.log(elfId, "is working."); + sleep(Math.random() * 1000.0, haveAProblem); + } + + function haveAProblem() { + console.log(elfId, "has a problem!"); + react { + assert elfProblem(elfId); + stop on asserted problemResolved(elfId) { + console.log(elfId, "'s problem is resolved."); + workIndustriously(); + } + } + } + + workIndustriously(); + } +} + +var reindeerCount = 0; +function reindeer() { + var reindeerId = "reindeer" + ++reindeerCount; + spawn* named reindeerId { + function holiday() { + console.log(reindeerId, "is on holiday."); + sleep(Math.random() * 9000.0, returnToWork); + } + + function returnToWork() { + console.log(reindeerId, "has returned from holiday and is ready to deliver toys!"); + react { + assert reindeerReturned(reindeerId); + stop on asserted deliverToys() { + console.log(reindeerId, "delivers toys with the other reindeer."); + react { + stop on retracted deliverToys() { + console.log(reindeerId, "has been dismissed by Santa."); + holiday(); + } + } + } + } + } + + holiday(); + } +} + +ground dataspace { + Syndicate.Timer.spawnTimerDriver(); + + var nElves = 20; + var elfGroupSize = 3; + var nReindeer = 9; + + for (var i = 0; i < nElves; i++) { elf(); } + for (var i = 0; i < nReindeer; i++) { reindeer(); } + + spawn* named "Santa" { + function waitForWork() { + console.log("Santa is waiting for something to do."); + react { + field this.puzzledElves = Immutable.List(); + on asserted elfProblem($id) { + this.puzzledElves = this.puzzledElves.push(id); + } + + field this.readyReindeer = Immutable.List(); + on asserted reindeerReturned($id) { + this.readyReindeer = this.readyReindeer.push(id); + } + + stop on (this.puzzledElves.size >= elfGroupSize) { + talkToElves(this.puzzledElves.take(elfGroupSize)); + } + stop on (this.readyReindeer.size == nReindeer) { + harnessReindeer(); + } + } + } + + function talkToElves(elves) { + if (elves.isEmpty()) { + waitForWork(); + } else { + var elf = elves.first(); + console.log("Santa resolves the problem of", elf); + react { + assert problemResolved(elf); + stop on retracted elfProblem(elf) { + talkToElves(elves.shift()); + } + } + } + } + + function harnessReindeer() { + console.log("Santa does the delivery run!"); + react { + assert deliverToys(); + stop on retracted reindeerReturned(_) { + waitForWork(); + } + } + } + + waitForWork() + } +}