diff --git a/src/syndicate.nim b/src/syndicate.nim index e908283..b26ccba 100644 --- a/src/syndicate.nim +++ b/src/syndicate.nim @@ -11,6 +11,8 @@ export dataspaces.Facet export dataspaces.FieldId export dataspaces.Fields export dataspaces.addEndpoint +export dataspaces.addStartScript +export dataspaces.addStopScript export dataspaces.defineObservableProperty export dataspaces.generateId export dataspaces.hash @@ -178,4 +180,4 @@ template syndicate*(name: string; dataspaceBody: untyped): untyped = proc bootProc(rootFacet: Facet) = proc getCurrentFacet(): Facet {.inject, used.} = rootFacet dataspaceBody - waitFor bootModule(name, bootProc) + asyncCheck bootModule(name, bootProc) diff --git a/src/syndicate/dataspaces.nim b/src/syndicate/dataspaces.nim index d64737b..79fd058 100644 --- a/src/syndicate/dataspaces.nim +++ b/src/syndicate/dataspaces.nim @@ -253,6 +253,8 @@ proc abort(facet; emitPatches: bool) = for child in facet.children.values: child.abort(emitPatches) facet.retractAssertionsAndSubscriptions(emitPatches) + for s in facet.stopScripts: s(facet) + # call stopScripts immediately proc enqueueScriptAction(actor; action: Action) = actor.pendingActions.add(action) @@ -342,10 +344,13 @@ proc scheduleScript*(facet; prio: Priority; script: Script[void]) = proc scheduleScript*(facet; script: Script[void]) = facet.actor.scheduleTask(pNormal, facet.wrap(script)) -proc addStartScript(facet; s: Script[void]) = +proc addStartScript*(facet; s: Script[void]) = facet.ensureFacetSetup("onStart") facet.scheduleScript(pNormal, s) +proc addStopScript*(facet; s: Script[void]) = + facet.stopScripts.add(s) + proc addFacet(actor; parentFacet: Option[Facet]; bootScript: Script[void]; checkInScript = false) = if checkInScript and parentFacet.isSome: assert parentFacet.get.inScript @@ -530,6 +535,8 @@ proc step(g: Ground) = if g.dataspace.runTasks(): asyncdispatch.callSoon: step(g) else: + for actor in g.dataspace.actors.values: + terminate(actor, false) for sh in g.stopHandlers: sh(g.dataspace) reset g.stopHandlers diff --git a/tests/test_dsl.nim b/tests/test_dsl.nim index 70eedc1..66a420f 100644 --- a/tests/test_dsl.nim +++ b/tests/test_dsl.nim @@ -7,15 +7,17 @@ import syndicate const BoxState = RecordClass(label: symbol"box-state", arity: 1) SetBox = RecordClass(label: symbol"set-box", arity: 1) + # TODO: hide RecordClass behind Assertion and Message types syndicate "test_dsl": spawn "box": field(currentValue, int, 0) - assert(BoxState, currentValue) + assert(BoxState, currentValue.get) stopIf currentValue.get == 10: echo "box: terminating" onMessage(SetBox) do (newValue: int): + # The SetBox message is unpacked to `newValue: int` echo "box: taking on new value ", newValue currentValue.set(newValue) @@ -27,3 +29,9 @@ syndicate "test_dsl": send(SetBox, v+1) onRetracted(BoxState) do (_): echo "client: box state disappeared" + onStop: + quit(0) # Quit explicitly rather than let the dispatcher run empty. + +runForever() + # The dataspace is driven by the async dispatcher. + # Without `runForever` this module would exit immediately.