Simplify event handling

This commit is contained in:
Emery Hemingway 2021-07-07 11:47:17 +02:00
parent 86b2dfbdab
commit 99fb0a9cff
3 changed files with 49 additions and 58 deletions

View File

@ -26,9 +26,6 @@ export asyncdispatch.`callback=`
proc `==`*(x, y: FieldId): bool {.borrow.}
proc newLit(p: pointer): NimNode = ident"nil"
## Hack to make `newLit` work on `Presevere`.
proc getCurrentFacet*(): Facet {.error.}
## Return the current `Facet` for this context.
@ -46,8 +43,8 @@ template sendMessage*(msg: untyped): untyped =
mixin getCurrentFacet
send(getCurrentFacet(), toPreserve(msg))
proc callbackForEvent(event: EventKind; pattern, handler: NimNode): NimNode =
## Generate a procedure that checks an event kind, unpacks `pattern` match to fit the
proc wrapHandler(handler: NimNode): NimNode =
## Generate a procedure that unpacks a `pattern` match to fit the
## parameters of `handler`, and calls the body of `handler`.
# TODO: compile time analysis of pattern to count number of captures
handler.expectKind nnkDo
@ -55,8 +52,7 @@ proc callbackForEvent(event: EventKind; pattern, handler: NimNode): NimNode =
formalArgs = handler[3]
cbFacetSym = genSym(nskParam, "facet")
scriptFacetSym = genSym(nskParam, "facet")
eventSym = genSym(nskParam, "event")
recSym = genSym(nskParam, "record")
recSym = genSym(nskParam, "bindings")
var
letSection = newNimNode(nnkLetSection, handler)
captureCount: int
@ -101,31 +97,25 @@ proc callbackForEvent(event: EventKind; pattern, handler: NimNode): NimNode =
params = [
newEmptyNode(),
newIdentDefs(cbFacetSym, ident"Facet"),
newIdentDefs(eventSym, ident"EventKind"),
newIdentDefs(recSym, newNimNode(nnkBracketExpr).add(ident"seq",
ident"Preserve")),
],
body = newStmtList(
newIfStmt((cond: infix(eventSym, "==", newLit(event)), body:
body =
newStmtList(
script,
newCall("scheduleScript", cbFacetSym, script[0])
)))))
))
# TODO: this proc just checks the event type and then schedules a script,
# should the event check be done in skeletons instead?
proc onEvent(event: EventKind; pattern, handler: NimNode): NimNode =
proc onEvent(event: EventKind; pattern, doHandler: NimNode): NimNode =
let
handler = callbackForEvent(event, pattern, handler)
handler = wrapHandler(doHandler)
handlerSym = handler[0]
result = quote do:
`handler`
mixin getCurrentFacet
discard getCurrentFacet().addEndpoint do (facet: Facet) -> EndpointSpec:
let a = `pattern`
result.assertion = some(toPreserve(observe(a)))
result.analysis = some(analyzeAssertion(a))
result.analysis.get.callback = wrap(facet, `handlerSym`)
onEvent(getCurrentFacet(), `pattern`, EventKind(`event`), `handlerSym`)
macro onAsserted*(pattern: Preserve; handler: untyped) =
onEvent(addedEvent, pattern, handler)

View File

@ -1,6 +1,6 @@
# SPDX-License-Identifier: ISC
import ./bags, ./dataflow, ./events, ./skeletons
import ./assertions, ./bags, ./dataflow, ./events, ./skeletons
import preserves
import std/[asyncdispatch, deques, hashes, macros, options, sets, tables]
@ -332,12 +332,6 @@ proc wrap(facet; script: Script[void]): Task[void] =
proc task() = facet.invokeScript(script)
task
proc wrap*(facet; cb: proc(facet: Facet; event: EventKind; bindings: seq[Value]) {.gcsafe.}): HandlerCallback =
proc wrapper(event: EventKind; bindings: seq[Value]) =
facet.invokeScript do (facet: Facet):
cb(facet, event, bindings)
wrapper
proc scheduleScript*(facet; prio: Priority; script: Script[void]) =
facet.actor.scheduleTask(prio, facet.wrap(script))
@ -573,3 +567,34 @@ template declareField*(facet: Facet; F: untyped; T: typedesc; initial: T): untyp
proc getPreserve(f: DistinctField): Preserve {.used.} =
facet.actor.dataspace.dataflow.recordObservation(f.id)
facet.fields[fieldOff]
type EventHandler* = proc (facet: Facet; bindings: seq[Value]) {.gcsafe.}
proc wrap*(facet; onEvent: EventKind; cb: EventHandler): HandlerCallback =
proc wrapper(event: EventKind; bindings: seq[Value]) =
facet.invokeScript do (facet: Facet):
if event == onEvent:
facet.scheduleScript do (facet: Facet):
cb(facet, bindings)
wrapper
proc onEvent*(facet; pattern: Preserve; event: EventKind; cb: EventHandler) =
discard facet.addEndpoint do (facet: Facet) -> EndpointSpec:
result.assertion = some(observe(pattern).toPreserve)
result.analysis = some(analyzeAssertion(pattern))
result.analysis.get.callback = wrap(facet, event, cb)
proc onAsserted*(facet; pattern: Preserve; cb: EventHandler) =
onEvent(facet, pattern, addedEvent, cb)
proc onRetracted*(facet; pattern: Preserve; cb: EventHandler) =
onEvent(facet, pattern, removedEvent, cb)
proc onMessage*(facet; pattern: Preserve; cb: EventHandler) =
onEvent(facet, pattern, messageEvent, cb)
template stopIf*(facet: Facet; cond: untyped; continuation: Script[void]): untyped =
## Stop the current facet if `cond` is true and
## invoke `body` after the facet has stopped.
discard facet.addDataflow do (facet: Facet):
if cond: facet.stop(continuation)

View File

@ -27,43 +27,19 @@ proc boot(facet: Facet) =
facet.stop do (facet: Facet):
echo "terminated box root facet"
discard facet.addEndpoint do (facet: Facet) -> EndpointSpec:
const a = SetBox.init(`?*`)
result.analysis = some analyzeAssertion(a)
proc cb(facet: Facet; evt: EventKind; vs: seq[Value]) =
if evt == messageEvent:
facet.scheduleScript do (facet: Facet):
value.set(vs[0])
echo "box updated value ", vs[0]
result.analysis.get.callback = facet.wrap cb
const o = observe(SetBox.init(`?*`)).toPreserve
result.assertion = some o
facet.onMessage(SetBox.init(`?*`)) do (facet: Facet; vs: seq[Value]):
value.set(vs[0])
echo "box updated value ", vs[0]
facet.spawn("client") do (facet: Facet):
discard facet.addEndpoint do (facet: Facet) -> EndpointSpec:
const a = BoxState.init(`?*`)
result.analysis = some analyzeAssertion(a)
proc cb(facet: Facet; evt: EventKind; vs: seq[Value]) =
if evt == addedEvent:
facet.scheduleScript do (facet: Facet):
let v = SetBox.init(vs[0].int.succ.toPreserve)
# echo "client sending ", v
facet.send(v)
result.analysis.get.callback = facet.wrap cb
const o = observe(BoxState.init(`?*`)).toPreserve
result.assertion = some o
facet.onAsserted(BoxState.init(`?*`)) do (facet: Facet; vs: seq[Value]):
let v = SetBox.init(vs[0].int.succ.toPreserve)
# echo "client sending ", v
facet.send(v)
discard facet.addEndpoint do (facet: Facet) -> EndpointSpec:
const a = BoxState.init(`?_`)
result.analysis = some analyzeAssertion(a)
proc cb(facet: Facet; evt: EventKind; vs: seq[Value]) =
if evt == removedEvent:
facet.scheduleScript do (facet: Facet):
echo "box gone"
result.analysis.get.callback = facet.wrap cb
const o = observe(BoxState.init(`?_`)).toPreserve
result.assertion = some o
facet.onRetracted(BoxState.init(`?_`)) do (facet: Facet; vs: seq[Value]):
echo "box gone"
facet.actor.dataspace.ground.addStopHandler do (_: Dataspace):
echo "stopping box-and-client"