2023-05-06 19:09:45 +00:00
|
|
|
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
2021-09-24 19:25:47 +00:00
|
|
|
# SPDX-License-Identifier: Unlicense
|
|
|
|
|
2023-05-06 19:09:45 +00:00
|
|
|
import std/[asyncfutures, deques, hashes, monotimes, options, sets, tables, times]
|
2022-03-19 00:09:19 +00:00
|
|
|
import preserves
|
2021-09-24 19:25:47 +00:00
|
|
|
import ../syndicate/protocols/[protocol, sturdy]
|
|
|
|
|
2023-07-13 13:15:13 +00:00
|
|
|
const tracing = defined(traceSyndicate)
|
|
|
|
|
|
|
|
when tracing:
|
|
|
|
import std/streams
|
|
|
|
from std/os import getEnv
|
|
|
|
import ./protocols/trace
|
|
|
|
|
2021-11-03 18:21:52 +00:00
|
|
|
export Handle
|
|
|
|
|
2023-03-23 19:06:44 +00:00
|
|
|
template generateIdType(typ: untyped) =
|
|
|
|
type typ* = distinct Natural
|
|
|
|
proc `==`*(x, y: typ): bool {.borrow.}
|
|
|
|
proc `$`*(id: typ): string {.borrow.}
|
2021-09-24 19:25:47 +00:00
|
|
|
|
|
|
|
generateIdType(ActorId)
|
|
|
|
generateIdType(FacetId)
|
|
|
|
generateIdType(EndpointId)
|
|
|
|
generateIdType(FieldId)
|
|
|
|
generateIdType(TurnId)
|
|
|
|
|
|
|
|
type
|
|
|
|
Oid = sturdy.Oid
|
2023-07-24 15:13:36 +00:00
|
|
|
Assertion* = Preserve[Cap]
|
|
|
|
Caveat = sturdy.Caveat[Cap]
|
2023-05-18 10:20:44 +00:00
|
|
|
Attenuation = seq[Caveat]
|
2023-07-24 15:13:36 +00:00
|
|
|
Rewrite = sturdy.Rewrite[Cap]
|
2021-09-24 19:25:47 +00:00
|
|
|
|
2023-07-13 13:15:13 +00:00
|
|
|
AssertionRef* = ref object
|
2023-07-24 15:13:36 +00:00
|
|
|
value*: Preserve[Cap]
|
|
|
|
# if the Enity methods take a Preserve[Cap] object then the generated
|
2023-07-13 13:15:13 +00:00
|
|
|
# C code has "redefinition of struct" problems when orc is enabled
|
|
|
|
|
2021-09-24 19:25:47 +00:00
|
|
|
Entity* = ref object of RootObj
|
|
|
|
oid*: Oid # oid is how Entities are identified over the wire
|
|
|
|
|
2023-07-24 15:13:36 +00:00
|
|
|
Cap* {.unpreservable.} = ref object
|
2021-09-24 19:25:47 +00:00
|
|
|
relay*: Facet
|
|
|
|
target*: Entity
|
|
|
|
attenuation*: Attenuation
|
|
|
|
|
2023-07-24 15:13:36 +00:00
|
|
|
Ref* {.deprecated: "Ref was renamed to Cap".} = Cap
|
|
|
|
|
2021-09-24 19:25:47 +00:00
|
|
|
OutboundAssertion = ref object
|
|
|
|
handle: Handle
|
2023-07-24 15:13:36 +00:00
|
|
|
peer: Cap
|
2021-09-24 19:25:47 +00:00
|
|
|
established: bool
|
|
|
|
OutboundTable = Table[Handle, OutboundAssertion]
|
|
|
|
|
2022-03-12 16:08:22 +00:00
|
|
|
Actor* = ref object
|
2021-09-24 19:25:47 +00:00
|
|
|
future: Future[void]
|
|
|
|
name: string
|
2023-07-12 14:16:20 +00:00
|
|
|
handleAllocator: ref Handle
|
|
|
|
# a fresh actor gets a new ref Handle and
|
|
|
|
# all actors spawned from it get the same ref.
|
2021-09-24 19:25:47 +00:00
|
|
|
root: Facet
|
|
|
|
exitReason: ref Exception
|
|
|
|
exitHooks: seq[TurnAction]
|
2023-07-13 13:15:13 +00:00
|
|
|
id: ActorId
|
2021-09-24 19:25:47 +00:00
|
|
|
exiting: bool
|
2023-07-13 13:15:13 +00:00
|
|
|
when tracing:
|
|
|
|
turnIdAllocator: ref TurnId
|
|
|
|
traceStream: FileStream
|
2021-09-24 19:25:47 +00:00
|
|
|
|
|
|
|
TurnAction* = proc (t: var Turn) {.gcsafe.}
|
|
|
|
|
|
|
|
Queues = TableRef[Facet, seq[TurnAction]]
|
|
|
|
|
|
|
|
Turn* = object # an object that should remain on the stack
|
2022-04-23 21:05:48 +00:00
|
|
|
facet: Facet
|
2021-09-24 19:25:47 +00:00
|
|
|
queues: Queues # a ref object that can outlive Turn
|
2023-08-01 10:03:47 +00:00
|
|
|
when tracing:
|
2023-07-13 13:15:13 +00:00
|
|
|
desc: TurnDescription[void]
|
2021-09-24 19:25:47 +00:00
|
|
|
|
|
|
|
Facet* = ref FacetObj
|
|
|
|
FacetObj = object
|
|
|
|
actor*: Actor
|
2023-07-13 13:15:13 +00:00
|
|
|
parent: Facet
|
2021-09-24 19:25:47 +00:00
|
|
|
children: HashSet[Facet]
|
|
|
|
outbound: OutboundTable
|
|
|
|
shutdownActions: seq[TurnAction]
|
|
|
|
inertCheckPreventers: int
|
2023-07-13 13:15:13 +00:00
|
|
|
id: FacetId
|
2021-09-24 19:25:47 +00:00
|
|
|
isAlive: bool
|
|
|
|
|
2023-07-13 13:15:13 +00:00
|
|
|
when tracing:
|
|
|
|
|
|
|
|
proc nextTurnId(facet: Facet): TurnId =
|
|
|
|
result = succ(facet.actor.turnIdAllocator[])
|
|
|
|
facet.actor.turnIdAllocator[] = result
|
|
|
|
|
|
|
|
proc trace(actor: Actor; act: ActorActivation) =
|
|
|
|
if not actor.traceStream.isNil:
|
|
|
|
var entry = TraceEntry[void](
|
|
|
|
timestamp: getTime().toUnixFloat(),
|
|
|
|
actor: initRecord("named", actor.name.toPreserve(void)),
|
|
|
|
item: act)
|
|
|
|
actor.traceStream.writeText entry.toPreserve(void)
|
|
|
|
actor.traceStream.writeLine()
|
|
|
|
|
|
|
|
proc path(facet: Facet): seq[trace.FacetId[void]] =
|
|
|
|
var f = facet
|
|
|
|
while not f.isNil:
|
|
|
|
result.add f.id.toPreserve
|
|
|
|
f = f.parent
|
2022-12-22 04:59:16 +00:00
|
|
|
|
|
|
|
method publish*(e: Entity; turn: var Turn; v: AssertionRef; h: Handle) {.base, gcsafe.} = discard
|
|
|
|
method retract*(e: Entity; turn: var Turn; h: Handle) {.base, gcsafe.} = discard
|
|
|
|
method message*(e: Entity; turn: var Turn; v: AssertionRef) {.base, gcsafe.} = discard
|
2023-07-24 15:13:36 +00:00
|
|
|
method sync*(e: Entity; turn: var Turn; peer: Cap) {.base, gcsafe.} = discard
|
2022-12-22 04:59:16 +00:00
|
|
|
|
2021-09-24 19:25:47 +00:00
|
|
|
using
|
|
|
|
actor: Actor
|
|
|
|
facet: Facet
|
|
|
|
turn: var Turn
|
|
|
|
action: TurnAction
|
|
|
|
|
|
|
|
proc labels(f: Facet): string =
|
|
|
|
proc catLabels(f: Facet; labels: var string) =
|
|
|
|
labels.add ':'
|
2023-07-13 13:15:13 +00:00
|
|
|
if not f.parent.isNil:
|
|
|
|
catLabels(f.parent, labels)
|
2021-09-24 19:25:47 +00:00
|
|
|
labels.add ':'
|
2023-07-13 13:15:13 +00:00
|
|
|
when tracing:
|
|
|
|
labels.add $f.id
|
2021-09-24 19:25:47 +00:00
|
|
|
result.add f.actor.name
|
|
|
|
catLabels(f, result)
|
|
|
|
|
|
|
|
proc `$`*(f: Facet): string =
|
|
|
|
"<Facet:" & f.labels & ">"
|
|
|
|
|
2023-07-24 15:13:36 +00:00
|
|
|
proc `$`*(r: Cap): string =
|
2021-09-24 19:25:47 +00:00
|
|
|
"<Ref:" & r.relay.labels & ">"
|
|
|
|
|
|
|
|
proc `$`*(actor: Actor): string =
|
|
|
|
"<Actor:" & actor.name & ">" # TODO: ambigous
|
|
|
|
|
2023-07-24 15:13:36 +00:00
|
|
|
proc attenuate(r: Cap; a: Attenuation): Cap =
|
2021-09-24 19:25:47 +00:00
|
|
|
if a.len == 0: result = r
|
2023-07-24 15:13:36 +00:00
|
|
|
else: result = Cap(
|
2021-09-24 19:25:47 +00:00
|
|
|
relay: r.relay,
|
|
|
|
target: r.target,
|
|
|
|
attenuation: a & r.attenuation)
|
|
|
|
|
|
|
|
proc hash*(facet): Hash =
|
|
|
|
facet.id.hash
|
|
|
|
|
2023-07-24 15:13:36 +00:00
|
|
|
proc hash*(r: Cap): Hash = !$(r.relay.hash !& r.target.unsafeAddr.hash)
|
2021-09-24 19:25:47 +00:00
|
|
|
|
|
|
|
proc nextHandle(facet: Facet): Handle =
|
2023-07-12 14:16:20 +00:00
|
|
|
result = succ(facet.actor.handleAllocator[])
|
|
|
|
facet.actor.handleAllocator[] = result
|
2021-09-24 19:25:47 +00:00
|
|
|
|
2022-04-23 21:05:48 +00:00
|
|
|
proc facet*(turn: var Turn): Facet = turn.facet
|
|
|
|
|
2021-09-24 19:25:47 +00:00
|
|
|
proc enqueue(turn: var Turn; target: Facet; action: TurnAction) =
|
|
|
|
if target in turn.queues:
|
|
|
|
turn.queues[target].add action
|
|
|
|
else:
|
|
|
|
turn.queues[target] = @[action]
|
|
|
|
|
2023-07-24 15:13:36 +00:00
|
|
|
type Bindings = Table[Preserve[Cap], Preserve[Cap]]
|
2021-09-24 19:25:47 +00:00
|
|
|
|
2022-03-07 23:14:57 +00:00
|
|
|
proc match(bindings: var Bindings; p: Pattern; v: Assertion): bool =
|
|
|
|
case p.orKind
|
|
|
|
of PatternKind.Pdiscard: result = true
|
|
|
|
of PatternKind.Patom:
|
|
|
|
result = case p.patom
|
|
|
|
of PAtom.Boolean: v.isBoolean
|
|
|
|
of PAtom.Float: v.isFloat
|
|
|
|
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):
|
2023-07-24 15:13:36 +00:00
|
|
|
bindings[toPreserve(p.pbind.pattern, Cap)] = v
|
2022-03-07 23:14:57 +00:00
|
|
|
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:
|
2021-09-24 19:25:47 +00:00
|
|
|
result = true
|
2022-03-07 23:14:57 +00:00
|
|
|
for key, pp in p.pcompound.dict.entries:
|
2022-10-27 00:24:10 +00:00
|
|
|
let vv = step(v, key)
|
|
|
|
if vv.isNone or not match(bindings, pp, get vv):
|
2022-03-07 23:14:57 +00:00
|
|
|
result = true
|
|
|
|
break
|
|
|
|
|
|
|
|
proc match(p: Pattern; v: Assertion): Option[Bindings] =
|
2021-09-24 19:25:47 +00:00
|
|
|
var b: Bindings
|
2022-03-07 23:14:57 +00:00
|
|
|
if match(b, p, v):
|
|
|
|
result = some b
|
2021-09-24 19:25:47 +00:00
|
|
|
|
|
|
|
proc instantiate(t: Template; bindings: Bindings): Assertion =
|
2022-03-07 23:14:57 +00:00
|
|
|
case t.orKind
|
|
|
|
of TemplateKind.Tattenuate:
|
|
|
|
let v = instantiate(t.tattenuate.template, bindings)
|
|
|
|
if not v.isEmbedded:
|
|
|
|
raise newException(ValueError, "Attempt to attenuate non-capability")
|
|
|
|
result = embed(attenuate(v.embed, t.tattenuate.attenuation))
|
|
|
|
of TemplateKind.TRef:
|
2023-05-18 10:20:44 +00:00
|
|
|
let n = $t.tref.binding.int
|
2023-07-24 15:13:36 +00:00
|
|
|
try: result = bindings[toPreserve(n, Cap)]
|
2022-03-07 23:14:57 +00:00
|
|
|
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:
|
2023-07-24 15:13:36 +00:00
|
|
|
result = initSequence(t.tcompound.arr.items.len, Cap)
|
2022-03-07 23:14:57 +00:00
|
|
|
for i, tt in t.tcompound.arr.items:
|
|
|
|
result[i] = instantiate(tt, bindings)
|
|
|
|
of TCompoundKind.dict:
|
2023-07-24 15:13:36 +00:00
|
|
|
result = initDictionary(Cap)
|
2022-03-07 23:14:57 +00:00
|
|
|
for key, tt in t.tcompound.dict.entries:
|
|
|
|
result[key] = instantiate(tt, bindings)
|
2021-09-24 19:25:47 +00:00
|
|
|
|
|
|
|
proc rewrite(r: Rewrite; v: Assertion): Assertion =
|
|
|
|
let bindings = match(r.pattern, v)
|
|
|
|
if bindings.isSome:
|
|
|
|
result = instantiate(r.template, get bindings)
|
|
|
|
|
|
|
|
proc examineAlternatives(cav: Caveat; v: Assertion): Assertion =
|
|
|
|
case cav.orKind
|
2023-05-18 10:20:44 +00:00
|
|
|
of CaveatKind.Rewrite:
|
2021-09-24 19:25:47 +00:00
|
|
|
result = rewrite(cav.rewrite, v)
|
2023-05-18 10:20:44 +00:00
|
|
|
of CaveatKind.Alts:
|
2021-09-24 19:25:47 +00:00
|
|
|
for r in cav.alts.alternatives:
|
|
|
|
result = rewrite(r, v)
|
|
|
|
if not result.isFalse: break
|
2023-05-18 10:20:44 +00:00
|
|
|
of CaveatKind.Reject: discard
|
|
|
|
of CaveatKind.unknown: discard
|
2021-09-24 19:25:47 +00:00
|
|
|
|
|
|
|
proc runRewrites*(a: Attenuation; v: Assertion): Assertion =
|
|
|
|
result = v
|
|
|
|
for stage in a:
|
|
|
|
result = examineAlternatives(stage, result)
|
|
|
|
if result.isFalse: break
|
|
|
|
|
2023-07-24 15:13:36 +00:00
|
|
|
proc publish(turn: var Turn; r: Cap; v: Assertion; h: Handle) =
|
2022-12-22 04:59:16 +00:00
|
|
|
var a = runRewrites(r.attenuation, v)
|
2021-09-24 19:25:47 +00:00
|
|
|
if not a.isFalse:
|
|
|
|
let e = OutboundAssertion(
|
|
|
|
handle: h, peer: r, established: false)
|
2022-03-12 16:08:22 +00:00
|
|
|
turn.facet.outbound[h] = e
|
2021-09-24 19:25:47 +00:00
|
|
|
enqueue(turn, r.relay) do (turn: var Turn):
|
|
|
|
e.established = true
|
2022-12-22 04:59:16 +00:00
|
|
|
publish(r.target, turn, AssertionRef(value: a), e.handle)
|
2023-07-13 13:15:13 +00:00
|
|
|
when tracing:
|
|
|
|
var act = ActionDescription[void](orKind: ActionDescriptionKind.enqueue)
|
|
|
|
act.enqueue.event.target.actor = turn.facet.actor.id.toPreserve
|
|
|
|
act.enqueue.event.target.facet = turn.facet.id.toPreserve
|
|
|
|
act.enqueue.event.target.oid = r.target.oid.toPreserve
|
|
|
|
act.enqueue.event.detail = trace.TurnEvent[void](orKind: TurnEventKind.assert)
|
|
|
|
act.enqueue.event.detail.assert.assertion.value.value =
|
2023-07-24 15:13:36 +00:00
|
|
|
contract(v) do (r: Cap) -> Preserve[void]:
|
2023-07-13 13:15:13 +00:00
|
|
|
discard
|
|
|
|
act.enqueue.event.detail.assert.handle = h
|
|
|
|
turn.desc.actions.add act
|
|
|
|
|
2021-09-24 19:25:47 +00:00
|
|
|
|
2023-07-24 15:13:36 +00:00
|
|
|
proc publish*(turn: var Turn; r: Cap; a: Assertion): Handle =
|
2022-03-12 16:08:22 +00:00
|
|
|
result = turn.facet.nextHandle()
|
2021-09-24 19:25:47 +00:00
|
|
|
publish(turn, r, a, result)
|
|
|
|
|
2023-07-24 15:13:36 +00:00
|
|
|
proc publish*[T](turn: var Turn; r: Cap; a: T): Handle =
|
|
|
|
publish(turn, r, toPreserve(a, Cap))
|
2021-09-24 19:25:47 +00:00
|
|
|
|
|
|
|
proc retract(turn: var Turn; e: OutboundAssertion) =
|
|
|
|
enqueue(turn, e.peer.relay) do (turn: var Turn):
|
|
|
|
if e.established:
|
|
|
|
e.established = false
|
|
|
|
e.peer.target.retract(turn, e.handle)
|
|
|
|
|
|
|
|
proc retract*(turn: var Turn; h: Handle) =
|
|
|
|
var e: OutboundAssertion
|
2022-03-12 16:08:22 +00:00
|
|
|
if turn.facet.outbound.pop(h, e):
|
2021-09-24 19:25:47 +00:00
|
|
|
turn.retract(e)
|
|
|
|
|
2023-07-24 15:13:36 +00:00
|
|
|
proc message*(turn: var Turn; r: Cap; v: Assertion) =
|
2022-12-22 04:59:16 +00:00
|
|
|
var a = runRewrites(r.attenuation, v)
|
2021-09-24 19:25:47 +00:00
|
|
|
if not a.isFalse:
|
|
|
|
enqueue(turn, r.relay) do (turn: var Turn):
|
2022-12-22 04:59:16 +00:00
|
|
|
r.target.message(turn, AssertionRef(value: a))
|
2021-09-24 19:25:47 +00:00
|
|
|
|
2023-07-24 15:13:36 +00:00
|
|
|
proc message*[T](turn: var Turn; r: Cap; v: T) =
|
|
|
|
message(turn, r, toPreserve(v, Cap))
|
2021-10-28 16:57:09 +00:00
|
|
|
|
2023-07-24 15:13:36 +00:00
|
|
|
proc sync(turn: var Turn; e: Entity; peer: Cap) =
|
2021-09-24 19:25:47 +00:00
|
|
|
e.sync(turn, peer)
|
|
|
|
# or turn.message(peer, true) ?
|
|
|
|
|
2023-07-24 15:13:36 +00:00
|
|
|
proc sync*(turn: var Turn; r, peer: Cap) =
|
2021-09-24 19:25:47 +00:00
|
|
|
enqueue(turn, r.relay) do (turn: var Turn):
|
|
|
|
sync(turn, r.target, peer)
|
|
|
|
|
2023-07-24 15:13:36 +00:00
|
|
|
proc replace*[T](turn: var Turn; cap: Cap; h: Handle; v: T): Handle =
|
|
|
|
result = publish(turn, cap, v)
|
2022-10-27 20:31:53 +00:00
|
|
|
if h != default(Handle):
|
|
|
|
retract(turn, h)
|
2021-09-24 19:25:47 +00:00
|
|
|
|
2023-07-24 15:13:36 +00:00
|
|
|
proc replace*[T](turn: var Turn; cap: Cap; h: var Handle; v: T): Handle {.discardable.} =
|
2022-05-21 18:21:02 +00:00
|
|
|
var old = h
|
2023-07-24 15:13:36 +00:00
|
|
|
h = publish(turn, cap, v)
|
2022-10-27 20:31:53 +00:00
|
|
|
if old != default(Handle):
|
|
|
|
retract(turn, old)
|
2022-05-21 18:21:02 +00:00
|
|
|
h
|
|
|
|
|
2021-09-24 19:25:47 +00:00
|
|
|
proc stop*(turn: var Turn) {.gcsafe.}
|
|
|
|
|
|
|
|
proc run*(facet; action: TurnAction; zombieTurn = false) {.gcsafe.}
|
|
|
|
|
2023-07-13 13:15:13 +00:00
|
|
|
proc newFacet(actor; parent: Facet; initialAssertions: OutboundTable): Facet =
|
2021-09-24 19:25:47 +00:00
|
|
|
result = Facet(
|
|
|
|
id: getMonoTime().ticks.FacetId,
|
|
|
|
actor: actor,
|
|
|
|
parent: parent,
|
|
|
|
outbound: initialAssertions,
|
|
|
|
isAlive: true)
|
2023-07-13 13:15:13 +00:00
|
|
|
if not parent.isNil: parent.children.incl result
|
2021-09-24 19:25:47 +00:00
|
|
|
|
2023-07-13 13:15:13 +00:00
|
|
|
proc newFacet(actor; parent: Facet): Facet =
|
2021-09-24 19:25:47 +00:00
|
|
|
var initialAssertions: OutboundTable
|
|
|
|
newFacet(actor, parent, initialAssertions)
|
|
|
|
|
|
|
|
proc isInert(facet): bool =
|
2022-03-12 16:08:22 +00:00
|
|
|
result = facet.children.len == 0 and
|
2023-07-13 13:15:13 +00:00
|
|
|
(facet.outbound.len == 0 or facet.parent.isNil) and
|
2022-03-12 16:08:22 +00:00
|
|
|
facet.inertCheckPreventers == 0
|
2021-09-24 19:25:47 +00:00
|
|
|
|
2022-03-12 16:08:22 +00:00
|
|
|
proc preventInertCheck*(facet): (proc() {.gcsafe.}) {.discardable.} =
|
2021-09-24 19:25:47 +00:00
|
|
|
var armed = true
|
|
|
|
inc facet.inertCheckPreventers
|
|
|
|
proc disarm() =
|
|
|
|
if armed:
|
|
|
|
armed = false
|
|
|
|
dec facet.inertCheckPreventers
|
|
|
|
result = disarm
|
|
|
|
|
|
|
|
proc inFacet(turn: var Turn; facet; act: TurnAction) =
|
|
|
|
## Call an action with a facet using a temporary `Turn`
|
|
|
|
## that shares the `Queues` of the calling `Turn`.
|
2022-03-12 16:08:22 +00:00
|
|
|
var t = Turn(facet: facet, queues: turn.queues)
|
2021-09-24 19:25:47 +00:00
|
|
|
act(t)
|
|
|
|
|
|
|
|
proc terminate(actor; turn; reason: ref Exception) {.gcsafe.}
|
|
|
|
|
|
|
|
proc terminate(facet; turn: var Turn; orderly: bool) {.gcsafe.} =
|
|
|
|
if facet.isAlive:
|
|
|
|
facet.isAlive = false
|
|
|
|
let parent = facet.parent
|
2023-07-13 13:15:13 +00:00
|
|
|
if not parent.isNil:
|
|
|
|
parent.children.excl facet
|
2021-09-24 19:25:47 +00:00
|
|
|
block:
|
2022-03-12 16:08:22 +00:00
|
|
|
var turn = Turn(facet: facet, queues: turn.queues)
|
2022-03-16 15:34:29 +00:00
|
|
|
while facet.children.len > 0:
|
|
|
|
facet.children.pop.terminate(turn, orderly)
|
2021-09-24 19:25:47 +00:00
|
|
|
if orderly:
|
|
|
|
for act in facet.shutdownActions:
|
|
|
|
act(turn)
|
|
|
|
for a in facet.outbound.values: turn.retract(a)
|
|
|
|
if orderly:
|
2023-07-13 13:15:13 +00:00
|
|
|
if not parent.isNil:
|
|
|
|
if parent.isInert:
|
|
|
|
parent.terminate(turn, true)
|
2021-09-24 19:25:47 +00:00
|
|
|
else:
|
2022-03-12 16:08:22 +00:00
|
|
|
terminate(facet.actor, turn, nil)
|
2023-07-13 13:15:13 +00:00
|
|
|
when tracing:
|
|
|
|
var act = ActionDescription[void](orKind: ActionDescriptionKind.facetStop)
|
|
|
|
act.facetstop.path = facet.path
|
|
|
|
turn.desc.actions.add act
|
2021-09-24 19:25:47 +00:00
|
|
|
|
|
|
|
proc stopIfInertAfter(action: TurnAction): TurnAction =
|
|
|
|
proc wrapper(turn: var Turn) =
|
|
|
|
action(turn)
|
2022-03-12 16:08:22 +00:00
|
|
|
enqueue(turn, turn.facet) do (turn: var Turn):
|
2023-07-13 13:15:13 +00:00
|
|
|
if (not turn.facet.parent.isNil and
|
|
|
|
(not turn.facet.parent.isAlive)) or
|
2022-03-12 16:08:22 +00:00
|
|
|
turn.facet.isInert:
|
2021-09-24 19:25:47 +00:00
|
|
|
stop(turn)
|
|
|
|
wrapper
|
|
|
|
|
2023-03-26 18:03:22 +00:00
|
|
|
proc inFacet*(turn: var Turn; bootProc: TurnAction): Facet =
|
2023-07-13 13:15:13 +00:00
|
|
|
result = newFacet(turn.facet.actor, turn.facet)
|
|
|
|
when tracing:
|
|
|
|
var act = ActionDescription[void](orKind: ActionDescriptionKind.facetstart)
|
|
|
|
act.facetstart.path.add result.path
|
|
|
|
turn.desc.actions.add act
|
2021-09-24 19:25:47 +00:00
|
|
|
inFacet(turn, result, stopIfInertAfter(bootProc))
|
|
|
|
|
2023-03-26 18:03:22 +00:00
|
|
|
proc facet*(turn: var Turn; bootProc: TurnAction): Facet {.deprecated.} = inFacet(turn, bootProc)
|
|
|
|
|
2023-07-22 10:32:52 +00:00
|
|
|
proc newActor(name: string; handleAlloc: ref Handle): Actor =
|
2021-09-24 19:25:47 +00:00
|
|
|
let
|
|
|
|
now = getTime()
|
|
|
|
seed = now.toUnix * 1_000_000_000 + now.nanosecond
|
|
|
|
result = Actor(
|
|
|
|
name: name,
|
2023-07-22 10:32:52 +00:00
|
|
|
id: ActorId(seed),
|
|
|
|
handleAllocator: handleAlloc,
|
|
|
|
)
|
2023-07-13 13:15:13 +00:00
|
|
|
result.root = newFacet(result, nil)
|
2021-09-24 19:25:47 +00:00
|
|
|
result.future = newFuture[void]($result)
|
2023-07-13 13:15:13 +00:00
|
|
|
when tracing:
|
|
|
|
var act = ActorActivation[void](orKind: ActorActivationKind.start)
|
|
|
|
act.start.actorName = Name[void](orKind: NameKind.named)
|
|
|
|
act.start.actorName.named.name = name.toPreserve(void)
|
|
|
|
trace(result, act)
|
2023-07-12 14:16:20 +00:00
|
|
|
|
|
|
|
proc run(actor; bootProc: TurnAction; initialAssertions: OutboundTable) =
|
2023-07-13 13:15:13 +00:00
|
|
|
run(newFacet(actor, actor.root, initialAssertions), stopIfInertAfter(bootProc))
|
2021-09-24 19:25:47 +00:00
|
|
|
|
2022-03-12 16:08:22 +00:00
|
|
|
proc bootActor*(name: string; bootProc: TurnAction): Actor =
|
2021-09-24 19:25:47 +00:00
|
|
|
var initialAssertions: OutboundTable
|
2023-07-22 10:32:52 +00:00
|
|
|
result = newActor(name, new(ref Handle))
|
2023-07-13 13:15:13 +00:00
|
|
|
when tracing:
|
|
|
|
new result.turnIdAllocator
|
|
|
|
let path = getEnv("SYNDICATE_TRACE_FILE", "/tmp/" & name & ".trace.pr")
|
|
|
|
case path
|
|
|
|
of "": stderr.writeLine "$SYNDICATE_TRACE_FILE unset, not tracing actor ", name
|
|
|
|
of "-": result.traceStream = newFileStream(stderr)
|
|
|
|
else: result.traceStream = openFileStream(path, fmWrite)
|
2023-07-12 14:16:20 +00:00
|
|
|
run(result, bootProc, initialAssertions)
|
2021-09-24 19:25:47 +00:00
|
|
|
|
2023-07-22 10:32:52 +00:00
|
|
|
proc spawn*(name: string; turn: var Turn; bootProc: TurnAction; initialAssertions = initHashSet[Handle]()): Actor =
|
|
|
|
let actor = newActor(name, turn.facet.actor.handleAllocator)
|
2022-03-12 16:08:22 +00:00
|
|
|
enqueue(turn, turn.facet) do (turn: var Turn):
|
2021-09-24 19:25:47 +00:00
|
|
|
var newOutBound: Table[Handle, OutboundAssertion]
|
|
|
|
for key in initialAssertions:
|
2022-03-12 16:08:22 +00:00
|
|
|
discard turn.facet.outbound.pop(key, newOutbound[key])
|
2023-07-13 13:15:13 +00:00
|
|
|
when tracing:
|
|
|
|
actor.turnIdAllocator = turn.facet.actor.turnIdAllocator
|
|
|
|
actor.traceStream = turn.facet.actor.traceStream
|
|
|
|
var act = ActionDescription[void](orKind: ActionDescriptionKind.spawn)
|
|
|
|
act.spawn.id = actor.id.toPreserve
|
|
|
|
turn.desc.actions.add act
|
2023-07-12 14:16:20 +00:00
|
|
|
run(actor, bootProc, newOutBound)
|
2023-07-22 10:32:52 +00:00
|
|
|
actor
|
2021-09-24 19:25:47 +00:00
|
|
|
|
2023-07-24 15:13:36 +00:00
|
|
|
proc newInertCap*(): Cap =
|
2022-03-12 16:08:22 +00:00
|
|
|
let a = bootActor("inert") do (turn: var Turn): turn.stop()
|
2023-07-24 15:13:36 +00:00
|
|
|
Cap(relay: a.root)
|
2021-09-24 19:25:47 +00:00
|
|
|
|
|
|
|
proc atExit*(actor; action) = actor.exitHooks.add action
|
|
|
|
|
|
|
|
proc terminate(actor; turn; reason: ref Exception) =
|
|
|
|
if not actor.exiting:
|
2023-07-13 13:15:13 +00:00
|
|
|
when tracing:
|
|
|
|
var act = ActorActivation[void](orKind: ActorActivationKind.stop)
|
|
|
|
if not reason.isNil:
|
|
|
|
act.stop.status = ExitStatus(orKind: ExitStatusKind.Error)
|
|
|
|
act.stop.status.error.message = reason.msg
|
|
|
|
trace(actor, act)
|
2021-09-24 19:25:47 +00:00
|
|
|
actor.exiting = true
|
|
|
|
actor.exitReason = reason
|
|
|
|
for hook in actor.exitHooks: hook(turn)
|
|
|
|
proc finish(turn: var Turn) =
|
2023-06-11 20:19:58 +00:00
|
|
|
actor.root.terminate(turn, reason.isNil)
|
|
|
|
if actor.exitReason.isNil:
|
2021-09-24 19:25:47 +00:00
|
|
|
actor.future.complete()
|
|
|
|
else:
|
2023-06-11 20:19:58 +00:00
|
|
|
actor.future.fail actor.exitReason
|
2022-12-22 05:01:06 +00:00
|
|
|
callSoon do ():
|
2021-09-24 19:25:47 +00:00
|
|
|
run(actor.root, finish, true)
|
|
|
|
|
2022-12-08 04:38:33 +00:00
|
|
|
proc terminate*(facet; e: ref Exception) =
|
2021-09-24 19:25:47 +00:00
|
|
|
run(facet.actor.root) do (turn: var Turn):
|
|
|
|
facet.actor.terminate(turn, e)
|
|
|
|
|
2023-05-17 09:42:17 +00:00
|
|
|
proc asyncCheck*(facet: Facet; fut: FutureBase) =
|
|
|
|
## Sets a callback on `fut` which propagates exceptions to `facet`.
|
|
|
|
addCallback(fut) do ():
|
|
|
|
if fut.failed: terminate(facet, fut.error)
|
|
|
|
|
2021-09-24 19:25:47 +00:00
|
|
|
proc asyncCheck*(turn; fut: FutureBase) =
|
2022-09-04 16:50:46 +00:00
|
|
|
## Sets a callback on `fut` which propagates exceptions to the facet of `turn`.
|
2023-05-17 09:42:17 +00:00
|
|
|
asyncCheck(turn.facet, fut)
|
2021-09-24 19:25:47 +00:00
|
|
|
|
|
|
|
template tryFacet(facet; body: untyped) =
|
2023-05-17 09:42:17 +00:00
|
|
|
try: body
|
|
|
|
except CatchableError as err: terminate(facet, err)
|
2021-09-24 19:25:47 +00:00
|
|
|
|
|
|
|
proc run*(facet; action: TurnAction; zombieTurn = false) =
|
2023-07-13 13:15:13 +00:00
|
|
|
if zombieTurn or (facet.actor.exitReason.isNil and facet.isAlive):
|
|
|
|
tryFacet(facet):
|
|
|
|
var queues = newTable[Facet, seq[TurnAction]]()
|
|
|
|
block:
|
|
|
|
var turn = Turn(facet: facet, queues: queues)
|
|
|
|
action(turn)
|
|
|
|
when tracing:
|
|
|
|
turn.desc.id = facet.nextTurnId.toPreserve
|
|
|
|
facet.actor.trace ActorActivation[void](
|
|
|
|
orKind: ActorActivationKind.turn, turn: turn.desc)
|
|
|
|
for facet, queue in queues:
|
|
|
|
for action in queue: run(facet, action)
|
2021-09-24 19:25:47 +00:00
|
|
|
|
2023-07-24 15:13:36 +00:00
|
|
|
proc run*(cap: Cap; action: TurnAction) =
|
|
|
|
## Convenience proc to run a `TurnAction` in the scope of a `Cap`.
|
|
|
|
run(cap.relay, action)
|
2022-07-08 12:50:10 +00:00
|
|
|
|
2023-05-17 09:42:17 +00:00
|
|
|
proc addCallback*(fut: FutureBase; facet: Facet; act: TurnAction) =
|
|
|
|
## Add a callback to a `Future` that will be called at a later `Turn`
|
|
|
|
## within the context of `facet`.
|
|
|
|
addCallback(fut) do ():
|
|
|
|
if fut.failed: terminate(facet, fut.error)
|
2023-07-13 13:15:13 +00:00
|
|
|
else:
|
|
|
|
when tracing:
|
|
|
|
run(facet) do (turn: var Turn):
|
|
|
|
turn.desc.cause = TurnCause[void](orKind: TurnCauseKind.external)
|
|
|
|
turn.desc.cause.external.description = "Future".toPreserve
|
|
|
|
act(turn)
|
|
|
|
else:
|
|
|
|
run(facet, act)
|
2023-05-17 09:42:17 +00:00
|
|
|
|
2022-09-04 16:50:46 +00:00
|
|
|
proc addCallback*(fut: FutureBase; turn: var Turn; act: TurnAction) =
|
2023-05-17 09:42:17 +00:00
|
|
|
## Add a callback to a `Future` that will be called at a later `Turn`
|
|
|
|
## with the same context as the current.
|
|
|
|
if fut.failed:
|
|
|
|
terminate(turn.facet, fut.error)
|
|
|
|
elif fut.finished:
|
|
|
|
enqueue(turn, turn.facet, act)
|
2022-09-04 16:50:46 +00:00
|
|
|
else:
|
2023-05-18 10:20:44 +00:00
|
|
|
addCallback(fut, turn.facet, act)
|
2022-09-04 16:50:46 +00:00
|
|
|
|
2023-07-12 14:15:39 +00:00
|
|
|
proc addCallback*[T](fut: Future[T]; turn: var Turn; act: proc (t: var Turn, x: T) {.gcsafe.}) =
|
2023-07-23 07:28:44 +00:00
|
|
|
addCallback(fut, turn) do (turn: var Turn):
|
|
|
|
if fut.failed: terminate(turn.facet, fut.error)
|
2023-07-12 14:15:39 +00:00
|
|
|
else:
|
2023-07-23 07:28:44 +00:00
|
|
|
when tracing:
|
|
|
|
turn.desc.cause = TurnCause[void](orKind: TurnCauseKind.external)
|
|
|
|
turn.desc.cause.external.description = "Future".toPreserve
|
|
|
|
act(turn, read fut)
|
2023-07-12 14:15:39 +00:00
|
|
|
|
2021-09-24 19:25:47 +00:00
|
|
|
proc stop*(turn: var Turn, facet: Facet) =
|
2023-07-13 13:15:13 +00:00
|
|
|
if facet.parent.isNil:
|
|
|
|
facet.terminate(turn, true)
|
|
|
|
else:
|
|
|
|
enqueue(turn, facet.parent) do (turn: var Turn):
|
|
|
|
facet.terminate(turn, true)
|
2021-09-24 19:25:47 +00:00
|
|
|
|
|
|
|
proc stop*(turn: var Turn) =
|
2022-03-12 16:08:22 +00:00
|
|
|
stop(turn, turn.facet)
|
2021-09-24 19:25:47 +00:00
|
|
|
|
2023-05-17 09:42:17 +00:00
|
|
|
proc onStop*(facet: Facet; act: TurnAction) =
|
|
|
|
## Add a `proc (turn: var Turn)` action to `facet` to be called as it stops.
|
|
|
|
add(facet.shutdownActions, act)
|
|
|
|
|
2021-09-24 19:25:47 +00:00
|
|
|
proc stopActor*(turn: var Turn) =
|
2022-03-12 16:08:22 +00:00
|
|
|
let actor = turn.facet.actor
|
2023-08-01 10:03:47 +00:00
|
|
|
enqueue(turn, actor.root) do (turn: var Turn):
|
2021-09-24 19:25:47 +00:00
|
|
|
terminate(actor, turn, nil)
|
|
|
|
|
|
|
|
proc freshen*(turn: var Turn, act: TurnAction) =
|
|
|
|
assert(turn.queues.len == 0, "Attempt to freshen a non-stale Turn")
|
2022-03-12 16:08:22 +00:00
|
|
|
run(turn.facet, act)
|
2021-09-24 19:25:47 +00:00
|
|
|
|
2023-07-24 15:13:36 +00:00
|
|
|
proc newCap*(relay: Facet; e: Entity): Cap =
|
|
|
|
Cap(relay: relay, target: e)
|
2021-09-24 19:25:47 +00:00
|
|
|
|
2023-07-24 15:13:36 +00:00
|
|
|
proc newCap*(turn; e: Entity): Cap =
|
|
|
|
Cap(relay: turn.facet, target: e)
|
2021-09-24 19:25:47 +00:00
|
|
|
|
2023-07-24 15:13:36 +00:00
|
|
|
proc sync*(turn, refer: Cap, cb: proc(t: Turn) {.gcsafe.}) =
|
2023-07-13 13:15:13 +00:00
|
|
|
raiseAssert "not implemented"
|
2021-09-24 19:25:47 +00:00
|
|
|
|
2022-06-16 03:27:25 +00:00
|
|
|
proc future*(actor): Future[void] = actor.future
|