CPS refactor
This commit is contained in:
parent
a8a17b4978
commit
9eae178723
|
@ -38,11 +38,11 @@
|
||||||
"packages": [
|
"packages": [
|
||||||
"nimcrypto"
|
"nimcrypto"
|
||||||
],
|
],
|
||||||
"path": "/nix/store/7b491gv9zlayilsh8k2gnyzw6znrh7xq-source",
|
"path": "/nix/store/jwz8pqbv6rsm8w4fjzdb37r0wzjn5hv0-source",
|
||||||
"rev": "70151aa132f3a771996117c23c1fcaa8446a6f35",
|
"rev": "d58da671799c69c0b3208b96c154e13c8b1a9e90",
|
||||||
"sha256": "1ldjz02p70wagqvk6vgcg16kjh7pkm1394qd1pcdmg8z39bm5ag3",
|
"sha256": "12dm0gsy10ppga7zf7hpf4adaqjrd9b740n2w926xyazq1njf6k9",
|
||||||
"srcDir": "",
|
"srcDir": "",
|
||||||
"url": "https://github.com/cheatfate/nimcrypto/archive/70151aa132f3a771996117c23c1fcaa8446a6f35.tar.gz"
|
"url": "https://github.com/cheatfate/nimcrypto/archive/d58da671799c69c0b3208b96c154e13c8b1a9e90.tar.gz"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"method": "fetchzip",
|
"method": "fetchzip",
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||||
# SPDX-License-Identifier: Unlicense
|
# SPDX-License-Identifier: Unlicense
|
||||||
|
|
||||||
import std/[deques, hashes, options, times]
|
import std/[deques, hashes, options, tables, times]
|
||||||
import pkg/cps
|
import pkg/cps
|
||||||
import preserves
|
import preserves
|
||||||
import ../syndicate/protocols/[protocol, sturdy]
|
import ./protocols/[protocol, sturdy]
|
||||||
|
|
||||||
# const traceSyndicate {.booldefine.}: bool = true
|
# const traceSyndicate {.booldefine.}: bool = true
|
||||||
const traceSyndicate* = true
|
const traceSyndicate* = true
|
||||||
|
@ -18,9 +18,13 @@ export protocol.Handle
|
||||||
|
|
||||||
type
|
type
|
||||||
Cont* = ref object of Continuation
|
Cont* = ref object of Continuation
|
||||||
turn: Turn
|
|
||||||
facet: Facet
|
facet: Facet
|
||||||
|
|
||||||
|
PublishProc* = proc (e: Entity; v: Value; h: Handle) {.cps: Cont.}
|
||||||
|
RetractProc* = proc (e: Entity; h: Handle) {.cps: Cont.}
|
||||||
|
MessageProc* = proc (e: Entity; v: Value) {.cps: Cont.}
|
||||||
|
SyncProc* = proc (e: Entity; peer: Cap) {.cps: Cont.}
|
||||||
|
|
||||||
Handler* = proc() {.closure.}
|
Handler* = proc() {.closure.}
|
||||||
|
|
||||||
Work = Deque[Cont]
|
Work = Deque[Cont]
|
||||||
|
@ -28,45 +32,53 @@ type
|
||||||
|
|
||||||
FacetState = enum fFresh, fRunning, fEnded
|
FacetState = enum fFresh, fRunning, fEnded
|
||||||
|
|
||||||
Callback* = proc () {.nimcall.}
|
Callback* = proc () {.closure.}
|
||||||
|
|
||||||
|
OutboundTable = Table[Handle, OutboundAssertion]
|
||||||
|
OutboundAssertion = ref object
|
||||||
|
handle: Handle
|
||||||
|
peer: Cap
|
||||||
|
established: bool
|
||||||
|
|
||||||
Facet* = ref object
|
Facet* = ref object
|
||||||
## https://synit.org/book/glossary.html#facet
|
## https://synit.org/book/glossary.html#facet
|
||||||
actor: Actor
|
actor: Actor
|
||||||
parent: Facet
|
parent: Facet
|
||||||
children: seq[Facet]
|
children: seq[Facet]
|
||||||
|
outbound: OutboundTable
|
||||||
stopHandlers: HandlerDeque
|
stopHandlers: HandlerDeque
|
||||||
stopCallbacks: seq[Callback]
|
stopCallbacks: seq[Callback]
|
||||||
state: FacetState
|
state: FacetState
|
||||||
when traceSyndicate:
|
id: FacetId
|
||||||
id: FacetId
|
|
||||||
|
|
||||||
Turn = ref object
|
FacetProc* = proc (f: Facet) {.closure.}
|
||||||
|
## Type for callbacks to be called within a turn.
|
||||||
|
## The `Facet` parameter is the owning facet.
|
||||||
|
|
||||||
|
Turn {.byref.} = object
|
||||||
## https://synit.org/book/glossary.html#turn
|
## https://synit.org/book/glossary.html#turn
|
||||||
facet: Facet
|
facet: Facet
|
||||||
entity: Entity
|
entity: Entity
|
||||||
event: Option[protocol.Event]
|
|
||||||
work: Work
|
work: Work
|
||||||
|
actions: seq[Cont]
|
||||||
|
event: Option[protocol.Event]
|
||||||
when traceSyndicate:
|
when traceSyndicate:
|
||||||
desc: TurnDescription
|
desc: TurnDescription
|
||||||
|
|
||||||
Entity* = ref object of RootObj
|
Entity* = ref object of RootObj
|
||||||
## https://synit.org/book/glossary.html#entity
|
## https://synit.org/book/glossary.html#entity
|
||||||
facet*: Facet
|
|
||||||
oid*: sturdy.Oid # oid is how Entities are identified over the wire
|
oid*: sturdy.Oid # oid is how Entities are identified over the wire
|
||||||
publishImpl*: PublishProc
|
publishImpl*: PublishProc
|
||||||
retractImpl*: RetractProc
|
retractImpl*: RetractProc
|
||||||
messageImpl*: MessageProc
|
messageImpl*: MessageProc
|
||||||
syncImpl*: SyncProc
|
syncImpl*: SyncProc
|
||||||
when traceSyndicate:
|
|
||||||
id: FacetId
|
|
||||||
|
|
||||||
Cap* {.final, preservesEmbedded.} = ref object of EmbeddedObj
|
Cap* {.final, preservesEmbedded.} = ref object of EmbeddedObj
|
||||||
relay*: Facet
|
relay*: Facet
|
||||||
target*: Entity
|
target*: Entity
|
||||||
attenuation*: seq[sturdy.Caveat]
|
attenuation*: seq[sturdy.Caveat]
|
||||||
|
|
||||||
Actor = ref object
|
Actor* = ref object
|
||||||
## https://synit.org/book/glossary.html#actor
|
## https://synit.org/book/glossary.html#actor
|
||||||
# TODO: run on a seperate thread.
|
# TODO: run on a seperate thread.
|
||||||
# crashHandlers: HandlerDeque
|
# crashHandlers: HandlerDeque
|
||||||
|
@ -74,11 +86,12 @@ type
|
||||||
handleAllocator: Handle
|
handleAllocator: Handle
|
||||||
facetIdAllocator: int
|
facetIdAllocator: int
|
||||||
id: ActorId
|
id: ActorId
|
||||||
|
turn: Turn
|
||||||
when traceSyndicate:
|
when traceSyndicate:
|
||||||
traceStream: FileStream
|
traceStream: FileStream
|
||||||
stopped: bool
|
stopped: bool
|
||||||
|
|
||||||
template syndicate*(prc: typed): untyped =
|
template turnWork*(prc: typed): untyped =
|
||||||
cps(Cont, prc)
|
cps(Cont, prc)
|
||||||
|
|
||||||
proc activeFacet*(c: Cont): Facet {.cpsVoodoo.} =
|
proc activeFacet*(c: Cont): Facet {.cpsVoodoo.} =
|
||||||
|
@ -93,6 +106,9 @@ using
|
||||||
cap: Cap
|
cap: Cap
|
||||||
turn: Turn
|
turn: Turn
|
||||||
|
|
||||||
|
proc `$`*(facet): string = $facet.id
|
||||||
|
proc `$`*(cap): string = "#:…"
|
||||||
|
|
||||||
proc hash*(facet): Hash = facet.unsafeAddr.hash
|
proc hash*(facet): Hash = facet.unsafeAddr.hash
|
||||||
proc hash*(cap): Hash = cap.unsafeAddr.hash
|
proc hash*(cap): Hash = cap.unsafeAddr.hash
|
||||||
|
|
||||||
|
@ -126,13 +142,19 @@ when traceSyndicate:
|
||||||
)
|
)
|
||||||
actor.traceStream.writeLine($entry.toPreserves)
|
actor.traceStream.writeLine($entry.toPreserves)
|
||||||
|
|
||||||
proc trace(actor; turn) =
|
proc traceTurn(actor) =
|
||||||
if not actor.traceStream.isNil:
|
if not actor.traceStream.isNil:
|
||||||
actor.trace(ActorActivation(
|
actor.trace(ActorActivation(
|
||||||
orKind: ActorActivationKind.turn,
|
orKind: ActorActivationKind.turn,
|
||||||
turn: turn.desc,
|
turn: actor.turn.desc,
|
||||||
))
|
))
|
||||||
|
|
||||||
|
proc traceTarget(facet): trace.Target =
|
||||||
|
Target(
|
||||||
|
actor: facet.actor.id,
|
||||||
|
facet: facet.id,
|
||||||
|
)
|
||||||
|
|
||||||
proc traceTarget(cap): trace.Target =
|
proc traceTarget(cap): trace.Target =
|
||||||
let facet = cap.relay
|
let facet = cap.relay
|
||||||
Target(
|
Target(
|
||||||
|
@ -141,53 +163,80 @@ when traceSyndicate:
|
||||||
oid: cap.target.oid.toPreserves,
|
oid: cap.target.oid.toPreserves,
|
||||||
)
|
)
|
||||||
|
|
||||||
proc traceTarget(turn): trace.Target =
|
proc traceEnqueue(actor; e: TargetedTurnEvent) =
|
||||||
let facet = turn.facet
|
actor.turn.desc.actions.add ActionDescription(
|
||||||
Target(
|
orKind: ActionDescriptionKind.enqueue,
|
||||||
actor: facet.actor.id,
|
enqueue: ActionDescriptionEnqueue(event: e),
|
||||||
facet: facet.id,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
proc newExternalTurn(facet): Turn =
|
proc traceDequeue(actor; e: TargetedTurnEvent) =
|
||||||
result = Turn(facet: facet)
|
actor.turn.desc.actions.add ActionDescription(
|
||||||
when traceSyndicate:
|
orKind: ActionDescriptionKind.dequeue,
|
||||||
result.desc = TurnDescription(cause: TurnCause(orKind: TurnCauseKind.external))
|
dequeue: ActionDescriptionDequeue(event: e),
|
||||||
|
)
|
||||||
|
|
||||||
proc pass*(a, b: Cont): Cont =
|
proc pass*(a, b: Cont): Cont =
|
||||||
b.turn = a.turn
|
assert not a.facet.isNil
|
||||||
if b.facet.isNil:
|
b.facet = a.facet
|
||||||
b.facet = a.facet
|
|
||||||
# TODO: whelp a new continuation at facet boundaries?
|
|
||||||
b
|
b
|
||||||
|
|
||||||
proc queue(t: Turn; c: Cont) =
|
proc queueWork(facet; c: Cont) =
|
||||||
c.facet = t.facet
|
c.facet = facet
|
||||||
t.work.addLast(c)
|
facet.actor.turn.work.addLast(c)
|
||||||
|
|
||||||
proc queue(c: Cont): Cont {.cpsMagic.} =
|
proc yieldWork(c: Cont): Cont {.cpsMagic.} =
|
||||||
queue(c.turn, c)
|
## Suspend and enqueue the caller until later in the turn.
|
||||||
|
assert not c.facet.isNil
|
||||||
|
c.facet.queueWork(c)
|
||||||
nil
|
nil
|
||||||
|
|
||||||
proc complete(turn; c: Cont) =
|
proc yieldToActions(c: Cont): Cont {.cpsMagic.} =
|
||||||
|
assert not c.facet.isNil
|
||||||
|
c.facet.actor.turn.actions.add(c)
|
||||||
|
nil
|
||||||
|
|
||||||
|
proc startExternalTurn(facet) =
|
||||||
|
let actor = facet.actor
|
||||||
|
assert actor.turn.work.len == 0
|
||||||
|
assert actor.turn.actions.len == 0
|
||||||
|
actor.turn.facet = facet
|
||||||
|
when traceSyndicate:
|
||||||
|
actor.turn.desc = TurnDescription(cause: TurnCause(orKind: TurnCauseKind.external))
|
||||||
|
|
||||||
|
proc terminate(actor; err: ref Exception) =
|
||||||
|
raise err
|
||||||
|
|
||||||
|
proc terminate(facet; err: ref Exception) =
|
||||||
|
terminate(facet.actor, err)
|
||||||
|
|
||||||
|
proc complete(c: Cont) =
|
||||||
var c = c
|
var c = c
|
||||||
try:
|
try:
|
||||||
while not c.isNil and not c.fn.isNil:
|
while not c.isNil and not c.fn.isNil:
|
||||||
c.turn = turn
|
|
||||||
var y = c.fn
|
var y = c.fn
|
||||||
var x = y(c)
|
var x = y(c)
|
||||||
c = Cont(x)
|
c = Cont(x)
|
||||||
except CatchableError as err:
|
except CatchableError as err:
|
||||||
if not c.dismissed:
|
if not c.dismissed:
|
||||||
writeStackFrames c
|
writeStackFrames c
|
||||||
# terminate(c.facet, err)
|
terminate(c.facet, err)
|
||||||
|
|
||||||
proc run(turn) =
|
proc run(actor) =
|
||||||
let actor = turn.facet.actor
|
|
||||||
assert not actor.stopped
|
assert not actor.stopped
|
||||||
while turn.work.len > 0:
|
var n = 0
|
||||||
complete(turn, turn.work.popFirst())
|
while actor.turn.work.len > 0:
|
||||||
|
actor.turn.work.popFirst().complete()
|
||||||
|
inc n
|
||||||
|
echo n, " items completed from work queue"
|
||||||
|
when traceSyndicate:
|
||||||
|
actor.traceTurn()
|
||||||
|
var i: int
|
||||||
|
while i < actor.turn.actions.len:
|
||||||
|
complete(move actor.turn.actions[i])
|
||||||
|
inc i
|
||||||
|
echo i, " items completed from action queue"
|
||||||
|
turn.actions.setLen(0)
|
||||||
when traceSyndicate:
|
when traceSyndicate:
|
||||||
actor.trace(turn)
|
|
||||||
if actor.stopped:
|
if actor.stopped:
|
||||||
trace(actor, ActorActivation(orkind: ActorActivationKind.stop))
|
trace(actor, ActorActivation(orkind: ActorActivationKind.stop))
|
||||||
|
|
||||||
|
@ -196,11 +245,11 @@ proc start(actor; cont: Cont) =
|
||||||
var act = ActorActivation(orkind: ActorActivationKind.start)
|
var act = ActorActivation(orkind: ActorActivationKind.start)
|
||||||
trace(actor, act)
|
trace(actor, act)
|
||||||
actor.root.state = fRunning
|
actor.root.state = fRunning
|
||||||
let turn = actor.root.newExternalTurn()
|
actor.root.startExternalTurn()
|
||||||
turn.queue(cont)
|
actor.root.queueWork(cont)
|
||||||
run(turn)
|
run(actor)
|
||||||
|
|
||||||
proc stop(turn; actor)
|
proc stop(actor)
|
||||||
|
|
||||||
proc collectPath(result: var seq[FacetId]; facet) =
|
proc collectPath(result: var seq[FacetId]; facet) =
|
||||||
if not facet.parent.isNil:
|
if not facet.parent.isNil:
|
||||||
|
@ -211,51 +260,47 @@ proc runNextStop(c: Cont; facet: Facet): Cont {.cpsMagic.} =
|
||||||
c.fn = facet.stopHandlers.pop()
|
c.fn = facet.stopHandlers.pop()
|
||||||
result = c
|
result = c
|
||||||
|
|
||||||
proc runNextFacetStop() {.syndicate.} =
|
proc runNextFacetStop() {.cps: Cont.} =
|
||||||
activeFacet().runNextStop()
|
activeFacet().runNextStop()
|
||||||
|
|
||||||
proc stop(turn; facet; reason: FacetStopReason) =
|
proc stop(facet; reason: FacetStopReason) =
|
||||||
|
let actor = facet.actor
|
||||||
while facet.stopHandlers.len > 0:
|
while facet.stopHandlers.len > 0:
|
||||||
var c = whelp runNextFacetStop()
|
var c = whelp runNextFacetStop()
|
||||||
c.facet = facet
|
c.facet = facet
|
||||||
complete(turn, c)
|
complete(c)
|
||||||
while facet.stopCallbacks.len > 0:
|
while facet.stopCallbacks.len > 0:
|
||||||
var cb = facet.stopCallbacks.pop()
|
var cb = facet.stopCallbacks.pop()
|
||||||
cb()
|
cb()
|
||||||
while facet.children.len > 0:
|
while facet.children.len > 0:
|
||||||
stop(turn, facet.children.pop(), FacetStopReason.parentStopping)
|
stop(facet.children.pop(), FacetStopReason.parentStopping)
|
||||||
when traceSyndicate:
|
when traceSyndicate:
|
||||||
var act = ActionDescription(orKind: ActionDescriptionKind.facetstop)
|
var act = ActionDescription(orKind: ActionDescriptionKind.facetstop)
|
||||||
collectPath(act.facetstop.path, facet)
|
collectPath(act.facetstop.path, facet)
|
||||||
act.facetStop.reason = reason
|
act.facetStop.reason = reason
|
||||||
turn.desc.actions.add act
|
actor.turn.desc.actions.add act
|
||||||
if facet.parent.isNil:
|
if facet.parent.isNil:
|
||||||
facet.actor.root = nil
|
actor.root = nil
|
||||||
stop(turn, facet.actor)
|
stop(actor)
|
||||||
|
|
||||||
proc stop(turn; actor) =
|
proc stop(actor) =
|
||||||
if not actor.root.isNil:
|
if not actor.root.isNil:
|
||||||
stop(turn, actor.root, FacetStopReason.actorStopping)
|
stop(actor.root, FacetStopReason.actorStopping)
|
||||||
actor.stopped = true
|
actor.stopped = true
|
||||||
|
|
||||||
proc bootActor*(name: string, c: Cont) =
|
proc bootActor(name: string, c: Cont) =
|
||||||
start(newActor(name), c)
|
start(newActor(name), c)
|
||||||
|
|
||||||
proc stopActor(c: Cont; a: Actor): Cont {.cpsMagic.} =
|
# proc stopFacetAction(reason: FacetStopReason) {.syndicate.} =
|
||||||
stop(c.turn, a)
|
# stop(c.facet, reason)
|
||||||
nil
|
|
||||||
|
|
||||||
proc stopFacet(c: Cont; f: Facet): Cont {.cpsMagic.} =
|
proc stopActorAction() {.cps: Cont.} =
|
||||||
stop(c.turn, f, FacetStopReason.explicitAction)
|
activeFacet().actor.stop()
|
||||||
nil
|
|
||||||
|
|
||||||
proc stopFacet*() {.syndicate.} =
|
proc stopActor*(facet) =
|
||||||
queue()
|
let c = whelp stopActorAction()
|
||||||
stop(activeTurn(), activeFacet(), FacetStopReason.explicitAction)
|
c.facet = facet
|
||||||
|
facet.actor.turn.actions.add(c)
|
||||||
proc stopActor*() {.syndicate.} =
|
|
||||||
queue()
|
|
||||||
stop(activeTurn(), activeActor())
|
|
||||||
|
|
||||||
type
|
type
|
||||||
AssertionRef* = ref object
|
AssertionRef* = ref object
|
||||||
|
@ -270,67 +315,238 @@ proc nextHandle(facet: Facet): Handle =
|
||||||
inc(facet.actor.handleAllocator)
|
inc(facet.actor.handleAllocator)
|
||||||
facet.actor.handleAllocator
|
facet.actor.handleAllocator
|
||||||
|
|
||||||
proc publish*(cap: Cap; val: Value): Handle =
|
proc actor(cap): Actor = cap.relay.actor
|
||||||
result = cap.relay.nextHandle()
|
|
||||||
|
type Bindings = Table[Value, Value]
|
||||||
|
|
||||||
|
proc attenuate(r: Cap; a: seq[Caveat]): Cap =
|
||||||
|
if a.len == 0: result = r
|
||||||
|
else: result = Cap(
|
||||||
|
relay: r.relay,
|
||||||
|
target: r.target,
|
||||||
|
attenuation: a & r.attenuation)
|
||||||
|
|
||||||
|
proc match(bindings: var Bindings; p: Pattern; v: Value): bool =
|
||||||
|
case p.orKind
|
||||||
|
of PatternKind.Pdiscard: result = true
|
||||||
|
of PatternKind.Patom:
|
||||||
|
result = case p.patom
|
||||||
|
of PAtom.Boolean: v.isBoolean
|
||||||
|
of PAtom.Double: v.isDouble
|
||||||
|
of PAtom.Signedinteger: v.isInteger
|
||||||
|
of PAtom.String: v.isString
|
||||||
|
of PAtom.Bytestring: v.isByteString
|
||||||
|
of PAtom.Symbol: v.isSymbol
|
||||||
|
of PatternKind.Pembedded:
|
||||||
|
result = v.isEmbedded
|
||||||
|
of PatternKind.Pbind:
|
||||||
|
if match(bindings, p.pbind.pattern, v):
|
||||||
|
bindings[p.pbind.pattern.toPreserves] = v
|
||||||
|
result = true
|
||||||
|
of PatternKind.Pand:
|
||||||
|
for pp in p.pand.patterns:
|
||||||
|
result = match(bindings, pp, v)
|
||||||
|
if not result: break
|
||||||
|
of PatternKind.Pnot:
|
||||||
|
var b: Bindings
|
||||||
|
result = not match(b, p.pnot.pattern, v)
|
||||||
|
of PatternKind.Lit:
|
||||||
|
result = p.lit.value == v
|
||||||
|
of PatternKind.PCompound:
|
||||||
|
case p.pcompound.orKind
|
||||||
|
of PCompoundKind.rec:
|
||||||
|
if v.isRecord and
|
||||||
|
p.pcompound.rec.label == v.label and
|
||||||
|
p.pcompound.rec.fields.len == v.arity:
|
||||||
|
result = true
|
||||||
|
for i, pp in p.pcompound.rec.fields:
|
||||||
|
if not match(bindings, pp, v[i]):
|
||||||
|
result = false
|
||||||
|
break
|
||||||
|
of PCompoundKind.arr:
|
||||||
|
if v.isSequence and p.pcompound.arr.items.len == v.sequence.len:
|
||||||
|
result = true
|
||||||
|
for i, pp in p.pcompound.arr.items:
|
||||||
|
if not match(bindings, pp, v[i]):
|
||||||
|
result = false
|
||||||
|
break
|
||||||
|
of PCompoundKind.dict:
|
||||||
|
if v.isDictionary:
|
||||||
|
result = true
|
||||||
|
for key, pp in p.pcompound.dict.entries:
|
||||||
|
let vv = step(v, key)
|
||||||
|
if vv.isNone or not match(bindings, pp, get vv):
|
||||||
|
result = true
|
||||||
|
break
|
||||||
|
|
||||||
|
proc match(p: Pattern; v: Value): Option[Bindings] =
|
||||||
|
var b: Bindings
|
||||||
|
if match(b, p, v):
|
||||||
|
result = some b
|
||||||
|
|
||||||
|
proc instantiate(t: Template; bindings: Bindings): Value =
|
||||||
|
case t.orKind
|
||||||
|
of TemplateKind.Tattenuate:
|
||||||
|
let v = instantiate(t.tattenuate.template, bindings)
|
||||||
|
let cap = v.unembed(Cap)
|
||||||
|
if cap.isNone:
|
||||||
|
raise newException(ValueError, "Attempt to attenuate non-capability")
|
||||||
|
result = attenuate(get cap, t.tattenuate.attenuation).embed
|
||||||
|
of TemplateKind.TRef:
|
||||||
|
let n = $t.tref.binding.int
|
||||||
|
try: result = bindings[n.toPreserves]
|
||||||
|
except KeyError:
|
||||||
|
raise newException(ValueError, "unbound reference: " & n)
|
||||||
|
of TemplateKind.Lit:
|
||||||
|
result = t.lit.value
|
||||||
|
of TemplateKind.Tcompound:
|
||||||
|
case t.tcompound.orKind
|
||||||
|
of TCompoundKind.rec:
|
||||||
|
result = initRecord(t.tcompound.rec.label, t.tcompound.rec.fields.len)
|
||||||
|
for i, tt in t.tcompound.rec.fields:
|
||||||
|
result[i] = instantiate(tt, bindings)
|
||||||
|
of TCompoundKind.arr:
|
||||||
|
result = initSequence(t.tcompound.arr.items.len)
|
||||||
|
for i, tt in t.tcompound.arr.items:
|
||||||
|
result[i] = instantiate(tt, bindings)
|
||||||
|
of TCompoundKind.dict:
|
||||||
|
result = initDictionary()
|
||||||
|
for key, tt in t.tcompound.dict.entries:
|
||||||
|
result[key] = instantiate(tt, bindings)
|
||||||
|
|
||||||
|
proc rewrite(r: Rewrite; v: Value): Value =
|
||||||
|
let bindings = match(r.pattern, v)
|
||||||
|
if bindings.isSome:
|
||||||
|
result = instantiate(r.template, get bindings)
|
||||||
|
|
||||||
|
proc examineAlternatives(cav: Caveat; v: Value): Value =
|
||||||
|
case cav.orKind
|
||||||
|
of CaveatKind.Rewrite:
|
||||||
|
result = rewrite(cav.rewrite, v)
|
||||||
|
of CaveatKind.Alts:
|
||||||
|
for r in cav.alts.alternatives:
|
||||||
|
result = rewrite(r, v)
|
||||||
|
if not result.isFalse: break
|
||||||
|
of CaveatKind.Reject: discard
|
||||||
|
of CaveatKind.unknown: discard
|
||||||
|
|
||||||
|
proc runRewrites(v: Value; a: openarray[Caveat]): Value =
|
||||||
|
result = v
|
||||||
|
for stage in a:
|
||||||
|
result = examineAlternatives(stage, result)
|
||||||
|
if result.isFalse: break
|
||||||
|
|
||||||
|
proc publish(c: Cont; e: Entity; v: Value; h: Handle): Cont {.cpsMagic.} =
|
||||||
|
pass c, e.publishImpl.call(e, v, h)
|
||||||
|
|
||||||
|
proc retract(c: Cont; e: Entity; h: Handle): Cont {.cpsMagic.} =
|
||||||
|
pass c, e.retractImpl.call(e, h)
|
||||||
|
|
||||||
|
proc message(c: Cont; e: Entity; v: Value): Cont {.cpsMagic.} =
|
||||||
|
pass c, e.messageImpl.call(e, v)
|
||||||
|
|
||||||
|
proc sync(c: Cont; e: Entity; p: Cap): Cont {.cpsMagic.} =
|
||||||
|
pass c, e.syncImpl.call(e, p)
|
||||||
|
|
||||||
|
proc turnPublish(cap: Cap; val: Value; h: Handle) {.turnWork.} =
|
||||||
when traceSyndicate:
|
when traceSyndicate:
|
||||||
var act = ActionDescription(orKind: ActionDescriptionKind.enqueue)
|
var traceEvent = TargetedTurnEvent(
|
||||||
act.enqueue.event = TargetedTurnEvent(
|
|
||||||
target: cap.traceTarget,
|
target: cap.traceTarget,
|
||||||
detail: trace.TurnEvent(orKind: trace.TurnEventKind.assert)
|
detail: trace.TurnEvent(orKind: trace.TurnEventKind.assert)
|
||||||
)
|
)
|
||||||
act.enqueue.event.detail = trace.TurnEvent(orKind: TurnEventKind.assert)
|
traceEvent.detail = trace.TurnEvent(orKind: TurnEventKind.assert)
|
||||||
act.enqueue.event.detail.assert = TurnEventAssert(
|
traceEvent.detail.assert = TurnEventAssert(
|
||||||
assertion: AssertionDescription(orKind: AssertionDescriptionKind.value),
|
assertion: AssertionDescription(orKind: AssertionDescriptionKind.value),
|
||||||
handle: result,
|
handle: h,
|
||||||
)
|
)
|
||||||
act.enqueue.event.detail.assert.assertion.value.value = val
|
traceEvent.detail.assert.assertion.value.value = val
|
||||||
turn.desc.actions.add act
|
cap.actor.traceEnqueue(traceEvent)
|
||||||
cap.target.publish(val, result)
|
cap.relay.outbound[h] = OutboundAssertion(handle: h, peer: cap)
|
||||||
|
yieldToActions()
|
||||||
proc retract*(cap: Cap; h: Handle) =
|
|
||||||
when traceSyndicate:
|
when traceSyndicate:
|
||||||
var act = ActionDescription(orKind: ActionDescriptionKind.enqueue)
|
cap.actor.traceDequeue(traceEvent)
|
||||||
act.enqueue.event = TargetedTurnEvent(
|
cap.target.publish(val, h)
|
||||||
target: turn.traceTarget,
|
cap.relay.outbound[h].established = true
|
||||||
|
|
||||||
|
proc turnRetract(cap: Cap; h: Handle) {.turnWork.} =
|
||||||
|
when traceSyndicate:
|
||||||
|
var traceEvent = TargetedTurnEvent(
|
||||||
|
target: cap.traceTarget,
|
||||||
detail: trace.TurnEvent(orKind: trace.TurnEventKind.retract)
|
detail: trace.TurnEvent(orKind: trace.TurnEventKind.retract)
|
||||||
)
|
)
|
||||||
act.enqueue.event.detail.retract = TurnEventRetract(handle: h)
|
traceEvent.detail.retract.handle = h
|
||||||
turn.desc.actions.add act
|
cap.actor.traceEnqueue(traceEvent)
|
||||||
|
yieldToActions()
|
||||||
|
when traceSyndicate:
|
||||||
|
cap.actor.traceDequeue(traceEvent)
|
||||||
cap.target.retract(h)
|
cap.target.retract(h)
|
||||||
|
|
||||||
proc message*(cap; val: Value) =
|
proc turnMessage(cap: Cap; val: Value) {.turnWork.} =
|
||||||
|
var val = runRewrites(val, cap.attenuation)
|
||||||
when traceSyndicate:
|
when traceSyndicate:
|
||||||
var act = ActionDescription(orKind: ActionDescriptionKind.enqueue)
|
var traceEvent = TargetedTurnEvent(
|
||||||
act.enqueue.event = TargetedTurnEvent(
|
target: cap.traceTarget,
|
||||||
target: turn.traceTarget,
|
|
||||||
detail: trace.TurnEvent(orKind: trace.TurnEventKind.message)
|
detail: trace.TurnEvent(orKind: trace.TurnEventKind.message)
|
||||||
)
|
)
|
||||||
act.enqueue.event.detail.message.body.value.value = val
|
traceEvent.detail.message.body.value.value = val
|
||||||
turn.desc.actions.add act
|
cap.actor.traceEnqueue(traceEvent)
|
||||||
cap.entity.message(val)
|
yieldToActions()
|
||||||
|
|
||||||
proc sync*(peer: Cap) =
|
|
||||||
when traceSyndicate:
|
when traceSyndicate:
|
||||||
var act = ActionDescription(orKind: ActionDescriptionKind.enqueue)
|
cap.actor.traceDequeue(traceEvent)
|
||||||
act.enqueue.event = TargetedTurnEvent(
|
cap.target.message(val)
|
||||||
target: turn.traceTarget,
|
|
||||||
|
proc turnSync(cap: Cap; peer: Cap) {.turnWork.} =
|
||||||
|
when traceSyndicate:
|
||||||
|
var traceEvent = TargetedTurnEvent(
|
||||||
|
target: cap.traceTarget,
|
||||||
detail: trace.TurnEvent(orKind: trace.TurnEventKind.sync)
|
detail: trace.TurnEvent(orKind: trace.TurnEventKind.sync)
|
||||||
)
|
)
|
||||||
act.enqueue.event.detail.sync.peer = peer.traceTarget
|
traceEvent.detail.sync.peer = peer.traceTarget
|
||||||
turn.desc.actions.add act
|
cap.actor.traceEnqueue(traceEvent)
|
||||||
peer.entity.sync()
|
yieldToActions()
|
||||||
|
when traceSyndicate:
|
||||||
|
cap.actor.traceDequeue(traceEvent)
|
||||||
|
cap.target.sync(peer)
|
||||||
|
|
||||||
proc installStopHook*(c: Cont, facet: Facet): Cont {.cpsMagic.} =
|
proc publish*(cap; val: Value): Handle =
|
||||||
|
var val = runRewrites(val, cap.attenuation)
|
||||||
|
# TODO: attenuation to nothing?
|
||||||
|
result = cap.relay.nextHandle()
|
||||||
|
cap.relay.queueWork(whelp turnPublish(cap, val, result))
|
||||||
|
|
||||||
|
proc publish*[T](cap; x: T): Handle =
|
||||||
|
publish(cap, x.toPreserves)
|
||||||
|
|
||||||
|
proc retract*(cap; h: Handle) =
|
||||||
|
cap.relay.queueWork(whelp turnRetract(cap, h))
|
||||||
|
|
||||||
|
proc message*(cap; val: Value) =
|
||||||
|
var val = runRewrites(val, cap.attenuation)
|
||||||
|
cap.relay.queueWork(whelp turnMessage(cap, val))
|
||||||
|
|
||||||
|
proc message*[T](cap; x: T) =
|
||||||
|
message(cap, x.toPreserves)
|
||||||
|
|
||||||
|
proc sync*(cap, peer: Cap) =
|
||||||
|
cap.relay.queueWork(whelp turnSync(cap, peer))
|
||||||
|
|
||||||
|
proc installStopHook(c: Cont, facet: Facet): Cont {.cpsMagic.} =
|
||||||
facet.stopHandlers.add(c.fn)
|
facet.stopHandlers.add(c.fn)
|
||||||
return c
|
return c
|
||||||
|
|
||||||
proc addOnStopHandler*(c: Cont; cb: Callback): Cont {.cpsMagic.} =
|
proc addOnStopHandler(c: Cont; cb: Callback): Cont {.cpsMagic.} =
|
||||||
c.facet.stopCallbacks.add(cb)
|
c.facet.stopCallbacks.add(cb)
|
||||||
result = c
|
result = c
|
||||||
|
|
||||||
type FacetProc* = proc (f: Facet) {.nimcall.}
|
proc onStop*(facet; cb: proc () {.closure.}) =
|
||||||
|
facet.stopCallbacks.add(cb)
|
||||||
|
|
||||||
proc bootActor*(name: string; bootProc: FacetProc): Actor =
|
proc bootActor*(name: string; bootProc: FacetProc): Actor =
|
||||||
result = newActor(name)
|
result = newActor(name)
|
||||||
# TODO: start a turn, trace reason is Eternal,
|
result.root.startExternalTurn()
|
||||||
bootProc(result.root)
|
bootProc(result.root)
|
||||||
|
|
||||||
|
proc runActor*(name: string; bootProc: FacetProc) =
|
||||||
|
let actor = bootActor(name, bootProc)
|
||||||
|
while not actor.stopped:
|
||||||
|
run(actor)
|
||||||
|
|
|
@ -3,10 +3,8 @@
|
||||||
|
|
||||||
import std/[asyncdispatch, monotimes, times, posix, times, epoll]
|
import std/[asyncdispatch, monotimes, times, posix, times, epoll]
|
||||||
import preserves
|
import preserves
|
||||||
import syndicate
|
import ../syndicate, ../protocols/timer
|
||||||
|
from ../protocols/dataspace import Observe
|
||||||
import ../protocols/timer
|
|
||||||
from ../syndicate/protocols/dataspace import Observe
|
|
||||||
|
|
||||||
export timer
|
export timer
|
||||||
|
|
||||||
|
@ -24,12 +22,16 @@ proc eventfd(count: cuint, flags: cint): cint
|
||||||
|
|
||||||
proc now: float64 = getTime().toUnixFloat()
|
proc now: float64 = getTime().toUnixFloat()
|
||||||
|
|
||||||
proc processTimers(ds: Cap) {.turnAction.} =
|
proc spawnTimers*(ds: Cap): Actor {.discardable.} =
|
||||||
let pat = inject(grab Observe(pattern: dropType LaterThan), {0: grabLit()})
|
## Spawn a timer actor.
|
||||||
during(ds, pat) do (seconds: float):
|
bootActor("timers") do (root: Facet):
|
||||||
let period = seconds - now()
|
let pat = inject(grab Observe(pattern: dropType LaterThan), {0: grabLit()})
|
||||||
if period < 0.001 or true:
|
#[
|
||||||
let h = publish(ds, LaterThan(seconds: seconds).toPreserves)
|
during(ds, pat) do (seconds: float):
|
||||||
|
let period = seconds - now()
|
||||||
|
if period < 0.001 or true:
|
||||||
|
let h = publish(ds, LaterThan(seconds: seconds).toPreserves)
|
||||||
|
]#
|
||||||
|
|
||||||
#[
|
#[
|
||||||
else:
|
else:
|
||||||
|
@ -39,14 +41,12 @@ proc processTimers(ds: Cap) {.turnAction.} =
|
||||||
discard publish(turn, ds, LaterThan(seconds: seconds))
|
discard publish(turn, ds, LaterThan(seconds: seconds))
|
||||||
]#
|
]#
|
||||||
|
|
||||||
proc spawnTimers*(ds: Cap) =
|
#[
|
||||||
## Spawn a timer actor.
|
proc after*(ds: Cap; dur: Duration; cb: proc () {.closure.}) =
|
||||||
boot(newActor("timers"), whelp processTimers(ds))
|
|
||||||
|
|
||||||
proc after*(turn: Turn; ds: Cap; dur: Duration; act: TurnAction) =
|
|
||||||
## Execute `act` after some duration of time.
|
## Execute `act` after some duration of time.
|
||||||
let later = now() + dur.inMilliseconds.float64 * 1_000.0
|
let later = now() + dur.inMilliseconds.float64 * 1_000.0
|
||||||
onPublish(ds, grab LaterThan(seconds: later)):
|
onPublish(ds, grab LaterThan(seconds: later)):
|
||||||
act(turn)
|
cb()
|
||||||
|
]#
|
||||||
|
|
||||||
# TODO: periodic timer
|
# TODO: periodic timer
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
import std/[hashes, options, tables]
|
import std/[hashes, options, tables]
|
||||||
import pkg/cps
|
import pkg/cps
|
||||||
import preserves
|
import preserves
|
||||||
import ./actors, ./protocols/dataspace, ./skeletons
|
import ./[actors, patterns, skeletons]
|
||||||
|
|
||||||
from ./protocols/protocol import Handle
|
from ./protocols/protocol import Handle
|
||||||
from ./protocols/dataspace import Observe
|
from ./protocols/dataspace import Observe
|
||||||
|
@ -14,28 +14,35 @@ type
|
||||||
index: Index
|
index: Index
|
||||||
handleMap: Table[Handle, Value]
|
handleMap: Table[Handle, Value]
|
||||||
|
|
||||||
method publish(ds: Dataspace; turn: Turn; a: AssertionRef; h: Handle) {.gcsafe.} =
|
proc dsPublish(e: Entity; v: Value; h: Handle) {.cps: Cont.} =
|
||||||
if add(ds.index, turn, a.value):
|
var ds = Dataspace(e)
|
||||||
var obs = a.value.preservesTo(Observe)
|
if ds.index.add(v):
|
||||||
|
var obs = v.preservesTo(Observe)
|
||||||
if obs.isSome and obs.get.observer of Cap:
|
if obs.isSome and obs.get.observer of Cap:
|
||||||
ds.index.add(turn, obs.get.pattern, Cap(obs.get.observer))
|
ds.index.add(obs.get.pattern, Cap(obs.get.observer))
|
||||||
ds.handleMap[h] = a.value
|
ds.handleMap[h] = v
|
||||||
|
|
||||||
method retract(ds: Dataspace; turn: Turn; h: Handle) {.gcsafe.} =
|
proc dsRetract(e: Entity; h: Handle) {.cps: Cont.} =
|
||||||
let v = ds.handleMap[h]
|
var ds = Dataspace(e)
|
||||||
if remove(ds.index, turn, v):
|
var v = ds.handleMap[h]
|
||||||
|
if ds.index.remove(v):
|
||||||
ds.handleMap.del h
|
ds.handleMap.del h
|
||||||
var obs = v.preservesTo(Observe)
|
var obs = v.preservesTo(Observe)
|
||||||
if obs.isSome and obs.get.observer of Cap:
|
if obs.isSome and obs.get.observer of Cap:
|
||||||
ds.index.remove(turn, obs.get.pattern, Cap(obs.get.observer))
|
ds.index.remove(obs.get.pattern, Cap(obs.get.observer))
|
||||||
|
|
||||||
method message(ds: Dataspace; turn: Turn; a: AssertionRef) {.gcsafe.} =
|
proc dsMessage(e: Entity; v: Value) {.cps: Cont.} =
|
||||||
ds.index.deliverMessage(turn, a.value)
|
var ds = Dataspace(e)
|
||||||
|
ds.index.deliverMessage(v)
|
||||||
|
|
||||||
proc newDataspace*(f: Facet): Cap =
|
proc newDataspace*(f: Facet): Cap =
|
||||||
newCap(f, Dataspace(index: initIndex()))
|
var ds = Dataspace(
|
||||||
|
publishImpl: whelp dsPublish,
|
||||||
|
retractImpl: whelp dsRetract,
|
||||||
|
messageImpl: whelp dsMessage,
|
||||||
|
index: initIndex(),
|
||||||
|
)
|
||||||
|
newCap(f, ds)
|
||||||
|
|
||||||
type BootProc = proc (ds: Cap)
|
proc observe*(cap: Cap; pat: Pattern; e: Entity): Handle =
|
||||||
|
publish(cap, Observe(pattern: pat, observer: newCap(cap.relay, e)))
|
||||||
proc newDataspace*(): Cap {.syndicate.} =
|
|
||||||
activeFacet().newDataspace()
|
|
||||||
|
|
|
@ -3,34 +3,32 @@
|
||||||
|
|
||||||
import std/[hashes, tables]
|
import std/[hashes, tables]
|
||||||
import preserves
|
import preserves
|
||||||
import ./actors, ./patterns, ./protocols/dataspace
|
import ./[actors, patterns]
|
||||||
|
|
||||||
type
|
type
|
||||||
DuringProc* = proc (a: Value; h: Handle)
|
DuringProc* = proc (a: Value; h: Handle): FacetProc {.gcsafe.}
|
||||||
DuringActionKind = enum null, dead, act
|
DuringActionKind = enum null, dead, act
|
||||||
DuringAction = object
|
DuringAction = object
|
||||||
case kind: DuringActionKind
|
case kind: DuringActionKind
|
||||||
of null, dead: discard
|
of null, dead: discard
|
||||||
of act:
|
of act:
|
||||||
action: Cont
|
retractProc: FacetProc
|
||||||
DuringEntity {.final.}= ref object of Entity
|
DuringEntity {.final.}= ref object of Entity
|
||||||
cb: DuringProc
|
publishProc: DuringProc
|
||||||
assertionMap: Table[Handle, DuringAction]
|
assertionMap: Table[Handle, DuringAction]
|
||||||
|
|
||||||
method publish(de: DuringEntity; turn: Turn; a: AssertionRef; h: Handle) =
|
proc duringPublish(e: Entity; v: Value; h: Handle) {.cps: Cont.} =
|
||||||
let action = de.cb(turn, a.value, h)
|
var de = DuringEntity(e)
|
||||||
# assert(not action.isNil "should have put in a no-op action")
|
let handler = de.handler(de.facet, a.value, h)
|
||||||
let g = de.assertionMap.getOrDefault h
|
let g = de.assertionMap.getOrDefault h
|
||||||
case g.kind
|
case g.kind
|
||||||
of null:
|
of null, dead:
|
||||||
de.assertionMap[h] = DuringAction(kind: act, action: action)
|
de.assertionMap[h] = DuringAction(kind: act, action: handler)
|
||||||
of dead:
|
|
||||||
de.assertionMap.del h
|
|
||||||
freshen(turn, action)
|
|
||||||
of act:
|
of act:
|
||||||
raiseAssert("during: duplicate handle in publish: " & $h)
|
raiseAssert("during: duplicate handle in publish: " & $h)
|
||||||
|
|
||||||
method retract(de: DuringEntity; turn: Turn; h: Handle) =
|
proc duringRetract(e: Entity; h: Handle) {.cps: Cont.} =
|
||||||
|
var de = DuringEntity(e)
|
||||||
let g = de.assertionMap.getOrDefault h
|
let g = de.assertionMap.getOrDefault h
|
||||||
case g.kind
|
case g.kind
|
||||||
of null:
|
of null:
|
||||||
|
@ -40,25 +38,6 @@ method retract(de: DuringEntity; turn: Turn; h: Handle) =
|
||||||
of act:
|
of act:
|
||||||
de.assertionMap.del h
|
de.assertionMap.del h
|
||||||
if not g.action.isNil:
|
if not g.action.isNil:
|
||||||
turn.queue(g.action)
|
g.action(de.facet)
|
||||||
|
|
||||||
proc during*(cb: DuringProc): DuringEntity = DuringEntity(cb: cb)
|
proc during*(cb: DuringProc): DuringEntity = DuringEntity(cb: cb)
|
||||||
|
|
||||||
proc observe*(turn: Turn; ds: Cap; pat: Pattern; e: Entity): Handle =
|
|
||||||
publish(turn, ds, Observe(pattern: pat, observer: newCap(turn, e)).toPreserves)
|
|
||||||
|
|
||||||
proc assertHandle(turn: Turn): Handle =
|
|
||||||
doAssert(
|
|
||||||
turn.event.isSome and turn.event.get.orKind == EventKind.Assert,
|
|
||||||
"operation not valid during this turn")
|
|
||||||
turn.event.get.assert.handle
|
|
||||||
|
|
||||||
proc awaitRetraction(cont: Cont): Cont {.cpsMagic.} =
|
|
||||||
{.error: "this cannot work".}
|
|
||||||
let h = cont.turn.assertHandle
|
|
||||||
while true:
|
|
||||||
let turn = cont.turn
|
|
||||||
if turn.retracts(h):
|
|
||||||
return c
|
|
||||||
else:
|
|
||||||
turn.facet.actor.qeueue(c)
|
|
||||||
|
|
|
@ -65,9 +65,9 @@ func isEmpty(cont: Continuation): bool =
|
||||||
cont.cache.len == 0 and cont.leafMap.len == 0
|
cont.cache.len == 0 and cont.leafMap.len == 0
|
||||||
|
|
||||||
type
|
type
|
||||||
ContinuationProc = proc (c: Continuation; v: Value) {.gcsafe.}
|
ContinuationProc = proc (c: Continuation; v: Value) {.closure.}
|
||||||
LeafProc = proc (l: Leaf; v: Value) {.gcsafe.}
|
LeafProc = proc (l: Leaf; v: Value) {.closure.}
|
||||||
ObserverProc = proc (turn: Turn; group: ObserverGroup; vs: seq[Value]) {.gcsafe.}
|
ObserverProc = proc (group: ObserverGroup; vs: seq[Value]) {.closure.}
|
||||||
|
|
||||||
proc getLeaves(cont: Continuation; constPaths: Paths): LeafMap =
|
proc getLeaves(cont: Continuation; constPaths: Paths): LeafMap =
|
||||||
result = cont.leafMap.getOrDefault(constPaths)
|
result = cont.leafMap.getOrDefault(constPaths)
|
||||||
|
@ -114,10 +114,10 @@ proc top(stack: TermStack): Value =
|
||||||
assert stack.len > 0
|
assert stack.len > 0
|
||||||
stack[stack.high]
|
stack[stack.high]
|
||||||
|
|
||||||
proc modify(node: Node; turn: Turn; outerValue: Value; event: EventKind;
|
proc modify(node: Node; outerValue: Value; event: EventKind;
|
||||||
modCont: ContinuationProc; modLeaf: LeafProc; modObs: ObserverProc) =
|
modCont: ContinuationProc; modLeaf: LeafProc; modObs: ObserverProc) =
|
||||||
|
|
||||||
proc walk(cont: Continuation; turn: Turn) =
|
proc walk(cont: Continuation) =
|
||||||
modCont(cont, outerValue)
|
modCont(cont, outerValue)
|
||||||
for constPaths, constValMap in cont.leafMap.pairs:
|
for constPaths, constValMap in cont.leafMap.pairs:
|
||||||
let constVals = projectPaths(outerValue, constPaths)
|
let constVals = projectPaths(outerValue, constPaths)
|
||||||
|
@ -129,7 +129,7 @@ proc modify(node: Node; turn: Turn; outerValue: Value; event: EventKind;
|
||||||
for capturePaths, observerGroup in leaf.observerGroups.pairs:
|
for capturePaths, observerGroup in leaf.observerGroups.pairs:
|
||||||
let captures = projectPaths(outerValue, capturePaths)
|
let captures = projectPaths(outerValue, capturePaths)
|
||||||
if captures.isSome:
|
if captures.isSome:
|
||||||
modObs(turn, observerGroup, get captures)
|
modObs(observerGroup, get captures)
|
||||||
of removedEvent:
|
of removedEvent:
|
||||||
let leaf = constValMap.getOrDefault(get constVals)
|
let leaf = constValMap.getOrDefault(get constVals)
|
||||||
if not leaf.isNil:
|
if not leaf.isNil:
|
||||||
|
@ -137,13 +137,13 @@ proc modify(node: Node; turn: Turn; outerValue: Value; event: EventKind;
|
||||||
for capturePaths, observerGroup in leaf.observerGroups.pairs:
|
for capturePaths, observerGroup in leaf.observerGroups.pairs:
|
||||||
let captures = projectPaths(outerValue, capturePaths)
|
let captures = projectPaths(outerValue, capturePaths)
|
||||||
if captures.isSome:
|
if captures.isSome:
|
||||||
modObs(turn, observerGroup, get captures)
|
modObs(observerGroup, get captures)
|
||||||
if leaf.isEmpty:
|
if leaf.isEmpty:
|
||||||
constValMap.del(get constVals)
|
constValMap.del(get constVals)
|
||||||
|
|
||||||
|
|
||||||
proc walk(node: Node; turn: Turn; termStack: TermStack) =
|
proc walk(node: Node; termStack: TermStack) =
|
||||||
walk(node.continuation, turn)
|
walk(node.continuation)
|
||||||
for selector, table in node.edges:
|
for selector, table in node.edges:
|
||||||
let
|
let
|
||||||
nextStack = pop(termStack, selector.popCount)
|
nextStack = pop(termStack, selector.popCount)
|
||||||
|
@ -153,11 +153,11 @@ proc modify(node: Node; turn: Turn; outerValue: Value; event: EventKind;
|
||||||
if nextClass.kind != classNone:
|
if nextClass.kind != classNone:
|
||||||
let nextNode = table.getOrDefault(nextClass)
|
let nextNode = table.getOrDefault(nextClass)
|
||||||
if not nextNode.isNil:
|
if not nextNode.isNil:
|
||||||
walk(nextNode, turn, push(nextStack, get nextValue))
|
walk(nextNode, push(nextStack, get nextValue))
|
||||||
if event == removedEvent and nextNode.isEmpty:
|
if event == removedEvent and nextNode.isEmpty:
|
||||||
table.del(nextClass)
|
table.del(nextClass)
|
||||||
|
|
||||||
walk(node, turn, @[@[outerValue].toPreserves])
|
walk(node, @[@[outerValue].toPreserves])
|
||||||
|
|
||||||
proc getOrNew[A, B, C](t: var Table[A, TableRef[B, C]], k: A): TableRef[B, C] =
|
proc getOrNew[A, B, C](t: var Table[A, TableRef[B, C]], k: A): TableRef[B, C] =
|
||||||
result = t.getOrDefault(k)
|
result = t.getOrDefault(k)
|
||||||
|
@ -227,7 +227,7 @@ proc getEndpoints(leaf: Leaf; capturePaths: Paths): ObserverGroup =
|
||||||
if captures.isSome:
|
if captures.isSome:
|
||||||
discard result.cachedCaptures.change(get captures, +1)
|
discard result.cachedCaptures.change(get captures, +1)
|
||||||
|
|
||||||
proc add*(index: var Index; turn: Turn; pattern: Pattern; observer: Cap) =
|
proc add*(index: var Index; pattern: Pattern; observer: Cap) =
|
||||||
let
|
let
|
||||||
cont = index.root.extend(pattern)
|
cont = index.root.extend(pattern)
|
||||||
analysis = analyse pattern
|
analysis = analyse pattern
|
||||||
|
@ -237,10 +237,10 @@ proc add*(index: var Index; turn: Turn; pattern: Pattern; observer: Cap) =
|
||||||
# TODO if endpoints.cachedCaptures.len > 0:
|
# TODO if endpoints.cachedCaptures.len > 0:
|
||||||
var captureMap = newTable[seq[Value], Handle]()
|
var captureMap = newTable[seq[Value], Handle]()
|
||||||
for capture in endpoints.cachedCaptures.items:
|
for capture in endpoints.cachedCaptures.items:
|
||||||
captureMap[capture] = publish(turn, observer, capture.toPreserves)
|
captureMap[capture] = publish(observer, capture.toPreserves)
|
||||||
endpoints.observers[observer] = captureMap
|
endpoints.observers[observer] = captureMap
|
||||||
|
|
||||||
proc remove*(index: var Index; turn: Turn; pattern: Pattern; observer: Cap) =
|
proc remove*(index: var Index; pattern: Pattern; observer: Cap) =
|
||||||
let
|
let
|
||||||
cont = index.root.extend(pattern)
|
cont = index.root.extend(pattern)
|
||||||
analysis = analyse pattern
|
analysis = analyse pattern
|
||||||
|
@ -252,7 +252,7 @@ proc remove*(index: var Index; turn: Turn; pattern: Pattern; observer: Cap) =
|
||||||
if not endpoints.isNil:
|
if not endpoints.isNil:
|
||||||
var captureMap: TableRef[seq[Value], Handle]
|
var captureMap: TableRef[seq[Value], Handle]
|
||||||
if endpoints.observers.pop(observer, captureMap):
|
if endpoints.observers.pop(observer, captureMap):
|
||||||
for handle in captureMap.values: retract(turn, handle)
|
for handle in captureMap.values: retract(observer, handle)
|
||||||
if endpoints.observers.len == 0:
|
if endpoints.observers.len == 0:
|
||||||
leaf.observerGroups.del(analysis.capturePaths)
|
leaf.observerGroups.del(analysis.capturePaths)
|
||||||
if leaf.observerGroups.len == 0:
|
if leaf.observerGroups.len == 0:
|
||||||
|
@ -260,7 +260,7 @@ proc remove*(index: var Index; turn: Turn; pattern: Pattern; observer: Cap) =
|
||||||
if constValMap.len == 0:
|
if constValMap.len == 0:
|
||||||
cont.leafMap.del(analysis.constPaths)
|
cont.leafMap.del(analysis.constPaths)
|
||||||
|
|
||||||
proc adjustAssertion(index: var Index; turn: Turn; outerValue: Value; delta: int): bool =
|
proc adjustAssertion(index: var Index; outerValue: Value; delta: int): bool =
|
||||||
case index.allAssertions.change(outerValue, delta)
|
case index.allAssertions.change(outerValue, delta)
|
||||||
of cdAbsentToPresent:
|
of cdAbsentToPresent:
|
||||||
result = true
|
result = true
|
||||||
|
@ -268,37 +268,37 @@ proc adjustAssertion(index: var Index; turn: Turn; outerValue: Value; delta: int
|
||||||
c.cache.incl(v)
|
c.cache.incl(v)
|
||||||
proc modLeaf(l: Leaf; v: Value) =
|
proc modLeaf(l: Leaf; v: Value) =
|
||||||
l.cache.incl(v)
|
l.cache.incl(v)
|
||||||
proc modObserver(turn: Turn; group: ObserverGroup; vs: seq[Value]) =
|
proc modObserver(group: ObserverGroup; vs: seq[Value]) =
|
||||||
let change = group.cachedCaptures.change(vs, +1)
|
let change = group.cachedCaptures.change(vs, +1)
|
||||||
if change == cdAbsentToPresent:
|
if change == cdAbsentToPresent:
|
||||||
for (observer, captureMap) in group.observers.pairs:
|
for (observer, captureMap) in group.observers.pairs:
|
||||||
captureMap[vs] = publish(turn, observer, vs.toPreserves)
|
captureMap[vs] = publish(observer, vs.toPreserves)
|
||||||
# TODO: this handle is coming from the facet?
|
# TODO: this handle is coming from the facet?
|
||||||
modify(index.root, turn, outerValue, addedEvent, modContinuation, modLeaf, modObserver)
|
modify(index.root, outerValue, addedEvent, modContinuation, modLeaf, modObserver)
|
||||||
of cdPresentToAbsent:
|
of cdPresentToAbsent:
|
||||||
result = true
|
result = true
|
||||||
proc modContinuation(c: Continuation; v: Value) =
|
proc modContinuation(c: Continuation; v: Value) =
|
||||||
c.cache.excl(v)
|
c.cache.excl(v)
|
||||||
proc modLeaf(l: Leaf; v: Value) =
|
proc modLeaf(l: Leaf; v: Value) =
|
||||||
l.cache.excl(v)
|
l.cache.excl(v)
|
||||||
proc modObserver(turn: Turn; group: ObserverGroup; vs: seq[Value]) =
|
proc modObserver(group: ObserverGroup; vs: seq[Value]) =
|
||||||
if group.cachedCaptures.change(vs, -1) == cdPresentToAbsent:
|
if group.cachedCaptures.change(vs, -1) == cdPresentToAbsent:
|
||||||
for (observer, captureMap) in group.observers.pairs:
|
for (observer, captureMap) in group.observers.pairs:
|
||||||
var h: Handle
|
var h: Handle
|
||||||
if captureMap.take(vs, h):
|
if captureMap.take(vs, h):
|
||||||
retract(observer.target, turn, h)
|
retract(observer, h)
|
||||||
modify(index.root, turn, outerValue, removedEvent, modContinuation, modLeaf, modObserver)
|
modify(index.root, outerValue, removedEvent, modContinuation, modLeaf, modObserver)
|
||||||
else: discard
|
else: discard
|
||||||
|
|
||||||
proc continuationNoop(c: Continuation; v: Value) = discard
|
proc continuationNoop(c: Continuation; v: Value) = discard
|
||||||
proc leafNoop(l: Leaf; v: Value) = discard
|
proc leafNoop(l: Leaf; v: Value) = discard
|
||||||
|
|
||||||
proc add*(index: var Index; turn: Turn; v: Value): bool =
|
proc add*(index: var Index; v: Value): bool =
|
||||||
adjustAssertion(index, turn, v, +1)
|
adjustAssertion(index, v, +1)
|
||||||
proc remove*(index: var Index; turn: Turn; v: Value): bool =
|
proc remove*(index: var Index; v: Value): bool =
|
||||||
adjustAssertion(index, turn, v, -1)
|
adjustAssertion(index, v, -1)
|
||||||
|
|
||||||
proc deliverMessage*(index: var Index; turn: Turn; v: Value) =
|
proc deliverMessage*(index: var Index; v: Value) =
|
||||||
proc observersCb(turn: Turn; group: ObserverGroup; vs: seq[Value]) =
|
proc observersCb(group: ObserverGroup; vs: seq[Value]) =
|
||||||
for observer in group.observers.keys: message(turn, observer, vs.toPreserves)
|
for observer in group.observers.keys: message(observer, vs.toPreserves)
|
||||||
index.root.modify(turn, v, messageEvent, continuationNoop, leafNoop, observersCb)
|
index.root.modify(v, messageEvent, continuationNoop, leafNoop, observersCb)
|
||||||
|
|
|
@ -37,22 +37,26 @@ proc `??`*(pat: Pattern; bindings: openArray[(int, Pattern)]): Pattern {.inline.
|
||||||
patterns.inject(pat, bindings)
|
patterns.inject(pat, bindings)
|
||||||
|
|
||||||
type
|
type
|
||||||
PublishProc = proc (turn: Turn; v: Value; h: Handle) {.closure.}
|
PublishProc = proc (v: Value; h: Handle) {.closure.}
|
||||||
RetractProc = proc (turn: Turn; h: Handle) {.closure.}
|
RetractProc = proc (h: Handle) {.closure.}
|
||||||
MessageProc = proc (turn: Turn; v: Value) {.closure.}
|
MessageProc = proc (v: Value) {.closure.}
|
||||||
|
|
||||||
ClosureEntity = ref object of Entity
|
ClosureEntity = ref object of Entity
|
||||||
publishImpl*: PublishProc
|
publishCb*: PublishProc
|
||||||
retractImpl*: RetractProc
|
retractCb*: RetractProc
|
||||||
messageImpl*: MessageProc
|
messageCb*: MessageProc
|
||||||
|
|
||||||
method publish(e: ClosureEntity; turn: Turn; a: AssertionRef; h: Handle) =
|
proc publishCont(e: Entity; v: Value; h: Handle) {.cps: Cont.} =
|
||||||
if not e.publishImpl.isNil: e.publishImpl(turn, a.value, h)
|
var ce = ClosureEntity(e)
|
||||||
|
if not ce.publishCb.isNil: ce.publishCb(v, h)
|
||||||
|
|
||||||
method retract(e: ClosureEntity; turn: Turn; h: Handle) =
|
proc retractCont(e: Entity; h: Handle) {.cps: Cont.} =
|
||||||
if not e.retractImpl.isNil: e.retractImpl(turn, h)
|
var ce = ClosureEntity(e)
|
||||||
|
if not ce.retractCb.isNil: ce.retractCb(h)
|
||||||
|
|
||||||
method message(e: ClosureEntity; turn: Turn; a: AssertionRef) =
|
proc messageCont(e: Entity; v: Value) {.cps: Cont.} =
|
||||||
if not e.messageImpl.isNil: e.messageImpl(turn, a.value)
|
var ce = ClosureEntity(e)
|
||||||
|
if not ce.messageCb.isNil: ce.messageCb(v)
|
||||||
|
|
||||||
proc argumentCount(handler: NimNode): int =
|
proc argumentCount(handler: NimNode): int =
|
||||||
handler.expectKind {nnkDo, nnkStmtList}
|
handler.expectKind {nnkDo, nnkStmtList}
|
||||||
|
@ -108,7 +112,7 @@ proc wrapMessageHandler(handler: NimNode): NimNode =
|
||||||
handlerSym = genSym(nskProc, "message")
|
handlerSym = genSym(nskProc, "message")
|
||||||
bindingsSym = ident"bindings"
|
bindingsSym = ident"bindings"
|
||||||
quote do:
|
quote do:
|
||||||
proc `handlerSym`(turn: Turn; `bindingsSym`: Value) =
|
proc `handlerSym`(`bindingsSym`: Value) =
|
||||||
`varSection`
|
`varSection`
|
||||||
if fromPreserves(`valuesSym`, bindings):
|
if fromPreserves(`valuesSym`, bindings):
|
||||||
`body`
|
`body`
|
||||||
|
@ -150,9 +154,8 @@ macro onPublish*(ds: Cap; pattern: Pattern; handler: untyped) =
|
||||||
discard observe(activeTurn(), `ds`, `pattern`, ClosureEntity(publishImpl: `handlerSym`))
|
discard observe(activeTurn(), `ds`, `pattern`, ClosureEntity(publishImpl: `handlerSym`))
|
||||||
]#
|
]#
|
||||||
|
|
||||||
#[
|
macro onMessage*(cap: Cap; pattern: Pattern; handler: untyped) =
|
||||||
macro onMessage*(ds: Cap; pattern: Pattern; handler: untyped) =
|
## Call `handler` when an message matching `pattern` is broadcasted at `cap`.
|
||||||
## Call `handler` when an message matching `pattern` is broadcasted at `ds`.
|
|
||||||
let
|
let
|
||||||
argCount = argumentCount(handler)
|
argCount = argumentCount(handler)
|
||||||
handlerProc = wrapMessageHandler(handler)
|
handlerProc = wrapMessageHandler(handler)
|
||||||
|
@ -161,8 +164,10 @@ macro onMessage*(ds: Cap; pattern: Pattern; handler: untyped) =
|
||||||
if `argCount` != 0 and `pattern`.analyse.capturePaths.len != `argCount`:
|
if `argCount` != 0 and `pattern`.analyse.capturePaths.len != `argCount`:
|
||||||
raiseAssert($`pattern`.analyse.capturePaths.len & " values captured but handler has " & $`argCount` & " arguments - " & $`pattern`)
|
raiseAssert($`pattern`.analyse.capturePaths.len & " values captured but handler has " & $`argCount` & " arguments - " & $`pattern`)
|
||||||
`handlerProc`
|
`handlerProc`
|
||||||
discard observe(activeTurn(), `ds`, `pattern`, ClosureEntity(messageImpl: `handlerSym`))
|
discard observe(`cap`, `pattern`, ClosureEntity(
|
||||||
]#
|
messageImpl: whelp messageCont,
|
||||||
|
messageCb: `handlerSym`,
|
||||||
|
))
|
||||||
|
|
||||||
#[
|
#[
|
||||||
macro during*(ds: Cap; pattern: Pattern; publishBody, retractBody: untyped) =
|
macro during*(ds: Cap; pattern: Pattern; publishBody, retractBody: untyped) =
|
||||||
|
@ -202,15 +207,15 @@ proc wrapHandler(body: NimNode; ident: string): NimNode =
|
||||||
proc `sym`() =
|
proc `sym`() =
|
||||||
`body`
|
`body`
|
||||||
|
|
||||||
macro onStop*(body: untyped) =
|
#[
|
||||||
|
macro onStop*(facet: Facet; body: untyped) =
|
||||||
let
|
let
|
||||||
handlerDef = wrapHandler(body, "onStop")
|
handlerDef = wrapHandler(body, "onStop")
|
||||||
handlerSym = handlerDef[0]
|
handlerSym = handlerDef[0]
|
||||||
result = quote do:
|
result = quote do:
|
||||||
`handlerDef`
|
`handlerDef`
|
||||||
addOnStopHandler(`handlerSym`)
|
addOnStopHandler(facet, `handlerSym`)
|
||||||
|
|
||||||
#[
|
|
||||||
macro onStop*(body: untyped) =
|
macro onStop*(body: untyped) =
|
||||||
quote do:
|
quote do:
|
||||||
block:
|
block:
|
||||||
|
@ -220,6 +225,3 @@ macro onStop*(body: untyped) =
|
||||||
`body`
|
`body`
|
||||||
return
|
return
|
||||||
]#
|
]#
|
||||||
|
|
||||||
proc runActor*(name: string; bootProc: FacetProc) =
|
|
||||||
let actor = spawnActor(name, bootProc)
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# Package
|
# Package
|
||||||
|
|
||||||
version = "20240216"
|
version = "20240219"
|
||||||
author = "Emery Hemingway"
|
author = "Emery Hemingway"
|
||||||
description = "Syndicated actors for conversational concurrency"
|
description = "Syndicated actors for conversational concurrency"
|
||||||
license = "Unlicense"
|
license = "Unlicense"
|
||||||
|
|
|
@ -3,29 +3,34 @@
|
||||||
|
|
||||||
import std/times
|
import std/times
|
||||||
import pkg/cps
|
import pkg/cps
|
||||||
import syndicate
|
import sam/syndicate
|
||||||
|
|
||||||
# import syndicate/actors/timers
|
import sam/actors/timers
|
||||||
|
|
||||||
proc now: float64 = getTime().toUnixFloat()
|
proc now: float64 = getTime().toUnixFloat()
|
||||||
|
|
||||||
runActor("timer-test") do (root: Facet):
|
runActor("timer-test") do (root: Facet):
|
||||||
|
echo "actor calls boot proc with root facte ", root
|
||||||
|
|
||||||
let ds = facet.newDataspace()
|
let ds = root.newDataspace()
|
||||||
let h = facet.publish(ds, "hello world!".toPreserves)
|
echo "new dataspace ", ds
|
||||||
|
let h = publish(ds, "hello world!".toPreserves)
|
||||||
|
echo "published to handle ", h
|
||||||
|
|
||||||
facet.onMessage(ds, grab()) do (v: Value):
|
onMessage(ds, grab()) do (v: Value):
|
||||||
stderr.writeLine "observed message ", v
|
stderr.writeLine "observed message ", v
|
||||||
|
|
||||||
facet.message(ds, "hello world!".toPreserves)
|
message(ds, "hello world!".toPreserves)
|
||||||
facet.retract(h)
|
echo "sent a message"
|
||||||
facet.sync(ds)
|
retract(ds, h)
|
||||||
|
echo "retracted handle ", h
|
||||||
|
# facet.sync(ds)
|
||||||
|
|
||||||
facet.onStop:
|
root.onStop:
|
||||||
echo "anonymous stop handler was invoked"
|
echo "anonymous stop handler was invoked"
|
||||||
|
|
||||||
echo "stopping actor"
|
echo "stopping actor"
|
||||||
facet.stopActor()
|
root.stopActor()
|
||||||
echo "actor stopped but still executing?"
|
echo "actor stopped but still executing?"
|
||||||
|
|
||||||
#[
|
#[
|
||||||
|
|
Loading…
Reference in New Issue