// 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 = 10; 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); } stop on (this.puzzledElves.size >= elfGroupSize) { talkToElves(this.puzzledElves.take(elfGroupSize)); } field this.readyReindeer = Immutable.List(); on asserted reindeerReturned($id) { this.readyReindeer = this.readyReindeer.push(id); } 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() } }