Compare commits
18 Commits
Author | SHA1 | Date |
---|---|---|
|
98580c2bb6 | |
|
18609d01ce | |
|
0a7d129991 | |
|
3ea91ecd24 | |
|
3d4b6e0bbc | |
|
75d8d6d3bf | |
|
7bf9b3fe48 | |
|
f54d9ae402 | |
|
9f59bb1e94 | |
|
9eae178723 | |
|
a8a17b4978 | |
|
fe268dea8d | |
|
da93f9eb1f | |
|
b83ef93484 | |
|
b2f680a215 | |
|
ff1e1e6231 | |
|
d5d5717976 | |
|
c519d909ab |
3
Tupfile
3
Tupfile
|
@ -1,2 +1,3 @@
|
||||||
include_rules
|
include_rules
|
||||||
: lock.json |> !nim_cfg |> | ./<lock>
|
: |> !nim_lk |> {lockfile}
|
||||||
|
: {lockfile} |> !nim_cfg |> | ./<lock>
|
||||||
|
|
45
lock.json
45
lock.json
|
@ -6,12 +6,22 @@
|
||||||
"bigints"
|
"bigints"
|
||||||
],
|
],
|
||||||
"path": "/nix/store/jvrm392g8adfsgf36prgwkbyd7vh5jsw-source",
|
"path": "/nix/store/jvrm392g8adfsgf36prgwkbyd7vh5jsw-source",
|
||||||
"ref": "20231006",
|
|
||||||
"rev": "86ea14d31eea9275e1408ca34e6bfe9c99989a96",
|
"rev": "86ea14d31eea9275e1408ca34e6bfe9c99989a96",
|
||||||
"sha256": "15pcpmnk1bnw3k8769rjzcpg00nahyrypwbxs88jnwr4aczp99j4",
|
"sha256": "15pcpmnk1bnw3k8769rjzcpg00nahyrypwbxs88jnwr4aczp99j4",
|
||||||
"srcDir": "src",
|
"srcDir": "src",
|
||||||
"url": "https://github.com/ehmry/nim-bigints/archive/86ea14d31eea9275e1408ca34e6bfe9c99989a96.tar.gz"
|
"url": "https://github.com/ehmry/nim-bigints/archive/86ea14d31eea9275e1408ca34e6bfe9c99989a96.tar.gz"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"method": "fetchzip",
|
||||||
|
"packages": [
|
||||||
|
"cps"
|
||||||
|
],
|
||||||
|
"path": "/nix/store/m9vpcf3dq6z2h1xpi1vlw0ycxp91s5p7-source",
|
||||||
|
"rev": "2a4d771a715ba45cfba3a82fa625ae7ad6591c8b",
|
||||||
|
"sha256": "0c62k5wpq9z9mn8cd4rm8jjc4z0xmnak4piyj5dsfbyj6sbdw2bf",
|
||||||
|
"srcDir": "",
|
||||||
|
"url": "https://github.com/nim-works/cps/archive/2a4d771a715ba45cfba3a82fa625ae7ad6591c8b.tar.gz"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"method": "fetchzip",
|
"method": "fetchzip",
|
||||||
"packages": [
|
"packages": [
|
||||||
|
@ -28,12 +38,11 @@
|
||||||
"packages": [
|
"packages": [
|
||||||
"nimcrypto"
|
"nimcrypto"
|
||||||
],
|
],
|
||||||
"path": "/nix/store/zyr8zwh7vaiycn1s4r8cxwc71f2k5l0h-source",
|
"path": "/nix/store/jwz8pqbv6rsm8w4fjzdb37r0wzjn5hv0-source",
|
||||||
"ref": "traditional-api",
|
"rev": "d58da671799c69c0b3208b96c154e13c8b1a9e90",
|
||||||
"rev": "602c5d20c69c76137201b5d41f788f72afb95aa8",
|
"sha256": "12dm0gsy10ppga7zf7hpf4adaqjrd9b740n2w926xyazq1njf6k9",
|
||||||
"sha256": "1dmdmgb6b9m5f8dyxk781nnd61dsk3hdxqks7idk9ncnpj9fng65",
|
|
||||||
"srcDir": "",
|
"srcDir": "",
|
||||||
"url": "https://github.com/cheatfate/nimcrypto/archive/602c5d20c69c76137201b5d41f788f72afb95aa8.tar.gz"
|
"url": "https://github.com/cheatfate/nimcrypto/archive/d58da671799c69c0b3208b96c154e13c8b1a9e90.tar.gz"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"method": "fetchzip",
|
"method": "fetchzip",
|
||||||
|
@ -41,7 +50,6 @@
|
||||||
"npeg"
|
"npeg"
|
||||||
],
|
],
|
||||||
"path": "/nix/store/ffkxmjmigfs7zhhiiqm0iw2c34smyciy-source",
|
"path": "/nix/store/ffkxmjmigfs7zhhiiqm0iw2c34smyciy-source",
|
||||||
"ref": "1.2.1",
|
|
||||||
"rev": "26d62fdc40feb84c6533956dc11d5ee9ea9b6c09",
|
"rev": "26d62fdc40feb84c6533956dc11d5ee9ea9b6c09",
|
||||||
"sha256": "0xpzifjkfp49w76qmaylan8q181bs45anmp46l4bwr3lkrr7bpwh",
|
"sha256": "0xpzifjkfp49w76qmaylan8q181bs45anmp46l4bwr3lkrr7bpwh",
|
||||||
"srcDir": "src",
|
"srcDir": "src",
|
||||||
|
@ -53,11 +61,32 @@
|
||||||
"preserves"
|
"preserves"
|
||||||
],
|
],
|
||||||
"path": "/nix/store/6nnn5di5vip1vladlb7z56rbw18d1y7j-source",
|
"path": "/nix/store/6nnn5di5vip1vladlb7z56rbw18d1y7j-source",
|
||||||
"ref": "20240208",
|
|
||||||
"rev": "2825bceecf33a15b9b7942db5331a32cbc39b281",
|
"rev": "2825bceecf33a15b9b7942db5331a32cbc39b281",
|
||||||
"sha256": "145vf46fy3wc52j6vs509fm9bi5lx7c53gskbkpcfbkv82l86dgk",
|
"sha256": "145vf46fy3wc52j6vs509fm9bi5lx7c53gskbkpcfbkv82l86dgk",
|
||||||
"srcDir": "src",
|
"srcDir": "src",
|
||||||
"url": "https://git.syndicate-lang.org/ehmry/preserves-nim/archive/2825bceecf33a15b9b7942db5331a32cbc39b281.tar.gz"
|
"url": "https://git.syndicate-lang.org/ehmry/preserves-nim/archive/2825bceecf33a15b9b7942db5331a32cbc39b281.tar.gz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "fetchzip",
|
||||||
|
"packages": [
|
||||||
|
"stew"
|
||||||
|
],
|
||||||
|
"path": "/nix/store/mqg8qzsbcc8xqabq2yzvlhvcyqypk72c-source",
|
||||||
|
"rev": "3c91b8694e15137a81ec7db37c6c58194ec94a6a",
|
||||||
|
"sha256": "17lfhfxp5nxvld78xa83p258y80ks5jb4n53152cdr57xk86y07w",
|
||||||
|
"srcDir": "",
|
||||||
|
"url": "https://github.com/status-im/nim-stew/archive/3c91b8694e15137a81ec7db37c6c58194ec94a6a.tar.gz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "fetchzip",
|
||||||
|
"packages": [
|
||||||
|
"sys"
|
||||||
|
],
|
||||||
|
"path": "/nix/store/ayplzmq7xdzrp3n6ly6dnskf5c5aiihp-source",
|
||||||
|
"rev": "3b86a5083a4aa178994fe4ffdc046d340aa13b32",
|
||||||
|
"sha256": "0qz9hag7synp8sx2b6caazm2kidvd0lv2p0h98sslkyzaf4icnal",
|
||||||
|
"srcDir": "src",
|
||||||
|
"url": "https://github.com/alaviss/nim-sys/archive/3b86a5083a4aa178994fe4ffdc046d340aa13b32.tar.gz"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
include_rules
|
|
||||||
: foreach *.nim |> !nim_check |>
|
|
|
@ -1,16 +1,32 @@
|
||||||
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||||
# SPDX-License-Identifier: Unlicense
|
# SPDX-License-Identifier: Unlicense
|
||||||
|
|
||||||
import std/[asyncfutures, hashes, monotimes, options, sets, tables, times]
|
import std/[deques, hashes, monotimes, options, sets, sequtils, tables, times]
|
||||||
|
import pkg/cps
|
||||||
import preserves
|
import preserves
|
||||||
import ../syndicate/protocols/[protocol, sturdy]
|
import ../syndicate/protocols/[protocol, sturdy]
|
||||||
|
|
||||||
const tracing = defined(traceSyndicate)
|
export cps
|
||||||
|
|
||||||
when tracing:
|
const traceSyndicate {.booldefine.}: bool = true
|
||||||
|
|
||||||
|
when traceSyndicate:
|
||||||
import std/streams
|
import std/streams
|
||||||
from std/os import getEnv
|
from std/os import getEnv
|
||||||
import ./protocols/trace
|
import ./protocols/trace
|
||||||
|
|
||||||
|
type TraceSink = ref object
|
||||||
|
stream: FileStream
|
||||||
|
|
||||||
|
proc newTraceSink: TraceSink =
|
||||||
|
new result
|
||||||
|
let path = getEnv("SYNDICATE_TRACE_FILE", "")
|
||||||
|
case path
|
||||||
|
of "": quit"$SYNDICATE_TRACE_FILE unset"
|
||||||
|
of "-": actor.stream = newFileStream(stderr)
|
||||||
|
else: result.stream = openFileStream(path, fmWrite)
|
||||||
|
|
||||||
|
proc write(s: TraceSink; e: TraceEntry) = s.write(e.toPreserves)
|
||||||
|
|
||||||
export Handle
|
export Handle
|
||||||
|
|
||||||
|
@ -62,20 +78,23 @@ type
|
||||||
exitHooks: seq[TurnAction]
|
exitHooks: seq[TurnAction]
|
||||||
id: ActorId
|
id: ActorId
|
||||||
exiting, exited: bool
|
exiting, exited: bool
|
||||||
when tracing:
|
when traceSyndicate:
|
||||||
turnIdAllocator: ref TurnId
|
turnIdAllocator: ref TurnId
|
||||||
traceStream: FileStream
|
traceStream: FileStream
|
||||||
|
|
||||||
TurnAction* = proc (t: var Turn) {.gcsafe.}
|
TurnAction* = proc (t: Turn)
|
||||||
|
|
||||||
Queues = TableRef[Facet, seq[TurnAction]]
|
Queues = TableRef[Facet, Deque[Cont]]
|
||||||
|
|
||||||
Turn* = object # an object that should remain on the stack
|
Turn* = ref object
|
||||||
facet: Facet
|
facet: Facet
|
||||||
queues: Queues # a ref object that can outlive Turn
|
queues: Queues
|
||||||
when tracing:
|
when traceSyndicate:
|
||||||
desc: TurnDescription
|
desc: TurnDescription
|
||||||
|
|
||||||
|
Cont* = ref object of Continuation
|
||||||
|
turn*: Turn
|
||||||
|
|
||||||
Facet* = ref FacetObj
|
Facet* = ref FacetObj
|
||||||
FacetObj = object
|
FacetObj = object
|
||||||
actor*: Actor
|
actor*: Actor
|
||||||
|
@ -87,20 +106,31 @@ type
|
||||||
id: FacetId
|
id: FacetId
|
||||||
isAlive: bool
|
isAlive: bool
|
||||||
|
|
||||||
when tracing:
|
proc pass*(a, b: Cont): Cont =
|
||||||
|
assert not a.turn.isNil
|
||||||
|
b.turn = move a.turn
|
||||||
|
return b
|
||||||
|
|
||||||
|
template turnAction*(prc: typed): untyped =
|
||||||
|
cps(Cont, prc)
|
||||||
|
|
||||||
|
proc activeTurn*(c: Cont): Turn {.cpsVoodoo.} =
|
||||||
|
assert not c.turn.isNil
|
||||||
|
c.turn
|
||||||
|
|
||||||
|
when traceSyndicate:
|
||||||
|
|
||||||
proc nextTurnId(facet: Facet): TurnId =
|
proc nextTurnId(facet: Facet): TurnId =
|
||||||
result = succ(facet.actor.turnIdAllocator[])
|
result = succ(facet.actor.turnIdAllocator[])
|
||||||
facet.actor.turnIdAllocator[] = result
|
facet.actor.turnIdAllocator[] = result
|
||||||
|
|
||||||
proc trace(actor: Actor; act: ActorActivation) =
|
proc trace(actor: Actor; act: ActorActivation) =
|
||||||
if not actor.traceStream.isNil:
|
assert not actor.traceStream.isNil
|
||||||
var entry = TraceEntry(
|
var entry = TraceEntry(
|
||||||
timestamp: getTime().toUnixFloat(),
|
timestamp: getTime().toUnixFloat(),
|
||||||
actor: initRecord("named", actor.name.toPreserves),
|
actor: initRecord("named", actor.name.toPreserves),
|
||||||
item: act)
|
item: act)
|
||||||
actor.traceStream.writeText entry.toPreserves
|
actor.traceStream.writeLine($entry.toPreserves)
|
||||||
actor.traceStream.writeLine()
|
|
||||||
|
|
||||||
proc path(facet: Facet): seq[trace.FacetId] =
|
proc path(facet: Facet): seq[trace.FacetId] =
|
||||||
var f = facet
|
var f = facet
|
||||||
|
@ -108,15 +138,15 @@ when tracing:
|
||||||
result.add f.id.toPreserves
|
result.add f.id.toPreserves
|
||||||
f = f.parent
|
f = f.parent
|
||||||
|
|
||||||
method publish*(e: Entity; turn: var Turn; v: AssertionRef; h: Handle) {.base, gcsafe.} = discard
|
method publish*(e: Entity; turn: Turn; v: AssertionRef; h: Handle) {.base.} = discard
|
||||||
method retract*(e: Entity; turn: var Turn; h: Handle) {.base, gcsafe.} = discard
|
method retract*(e: Entity; turn: Turn; h: Handle) {.base.} = discard
|
||||||
method message*(e: Entity; turn: var Turn; v: AssertionRef) {.base, gcsafe.} = discard
|
method message*(e: Entity; turn: Turn; v: AssertionRef) {.base.} = discard
|
||||||
method sync*(e: Entity; turn: var Turn; peer: Cap) {.base, gcsafe.} = discard
|
method sync*(e: Entity; turn: Turn; peer: Cap) {.base.} = discard
|
||||||
|
|
||||||
using
|
using
|
||||||
actor: Actor
|
actor: Actor
|
||||||
facet: Facet
|
facet: Facet
|
||||||
turn: var Turn
|
turn: Turn
|
||||||
action: TurnAction
|
action: TurnAction
|
||||||
|
|
||||||
proc labels(f: Facet): string =
|
proc labels(f: Facet): string =
|
||||||
|
@ -125,7 +155,7 @@ proc labels(f: Facet): string =
|
||||||
if not f.parent.isNil:
|
if not f.parent.isNil:
|
||||||
catLabels(f.parent, labels)
|
catLabels(f.parent, labels)
|
||||||
labels.add ':'
|
labels.add ':'
|
||||||
when tracing:
|
when traceSyndicate:
|
||||||
labels.add $f.id
|
labels.add $f.id
|
||||||
result.add f.actor.name
|
result.add f.actor.name
|
||||||
catLabels(f, result)
|
catLabels(f, result)
|
||||||
|
@ -155,13 +185,14 @@ proc nextHandle(facet: Facet): Handle =
|
||||||
result = succ(facet.actor.handleAllocator[])
|
result = succ(facet.actor.handleAllocator[])
|
||||||
facet.actor.handleAllocator[] = result
|
facet.actor.handleAllocator[] = result
|
||||||
|
|
||||||
proc facet*(turn: var Turn): Facet = turn.facet
|
proc facet*(turn: Turn): Facet = turn.facet
|
||||||
|
|
||||||
proc enqueue(turn: var Turn; target: Facet; action: TurnAction) =
|
proc enqueue(turn: Turn; target: Facet; cont: Cont) =
|
||||||
|
cont.turn = turn
|
||||||
if target in turn.queues:
|
if target in turn.queues:
|
||||||
turn.queues[target].add action
|
turn.queues[target].addLast cont
|
||||||
else:
|
else:
|
||||||
turn.queues[target] = @[action]
|
turn.queues[target] = toDeque([cont])
|
||||||
|
|
||||||
type Bindings = Table[Value, Value]
|
type Bindings = Table[Value, Value]
|
||||||
|
|
||||||
|
@ -275,16 +306,18 @@ proc runRewrites*(a: Attenuation; v: Value): Value =
|
||||||
result = examineAlternatives(stage, result)
|
result = examineAlternatives(stage, result)
|
||||||
if result.isFalse: break
|
if result.isFalse: break
|
||||||
|
|
||||||
proc publish(turn: var Turn; r: Cap; v: Value; h: Handle) =
|
proc publish(target: Entity; e: OutboundAssertion; a: AssertionRef) {.turnAction.} =
|
||||||
|
e.established = true
|
||||||
|
publish(target, activeTurn(), a, e.handle)
|
||||||
|
|
||||||
|
proc publish(turn: Turn; r: Cap; v: Value; h: Handle) =
|
||||||
var a = runRewrites(r.attenuation, v)
|
var a = runRewrites(r.attenuation, v)
|
||||||
if not a.isFalse:
|
if not a.isFalse:
|
||||||
let e = OutboundAssertion(
|
let e = OutboundAssertion(
|
||||||
handle: h, peer: r, established: false)
|
handle: h, peer: r, established: false)
|
||||||
turn.facet.outbound[h] = e
|
turn.facet.outbound[h] = e
|
||||||
enqueue(turn, r.relay) do (turn: var Turn):
|
enqueue(turn, r.relay, whelp publish(r.target, e, AssertionRef(value: a)))
|
||||||
e.established = true
|
when traceSyndicate:
|
||||||
publish(r.target, turn, AssertionRef(value: a), e.handle)
|
|
||||||
when tracing:
|
|
||||||
var act = ActionDescription(orKind: ActionDescriptionKind.enqueue)
|
var act = ActionDescription(orKind: ActionDescriptionKind.enqueue)
|
||||||
act.enqueue.event.target.actor = turn.facet.actor.id.toPreserves
|
act.enqueue.event.target.actor = turn.facet.actor.id.toPreserves
|
||||||
act.enqueue.event.target.facet = turn.facet.id.toPreserves
|
act.enqueue.event.target.facet = turn.facet.id.toPreserves
|
||||||
|
@ -295,55 +328,61 @@ proc publish(turn: var Turn; r: Cap; v: Value; h: Handle) =
|
||||||
act.enqueue.event.detail.assert.handle = h
|
act.enqueue.event.detail.assert.handle = h
|
||||||
turn.desc.actions.add act
|
turn.desc.actions.add act
|
||||||
|
|
||||||
proc publish*(turn: var Turn; r: Cap; a: Value): Handle {.discardable.} =
|
proc publish*(turn: Turn; r: Cap; a: Value): Handle =
|
||||||
result = turn.facet.nextHandle()
|
result = turn.facet.nextHandle()
|
||||||
publish(turn, r, a, result)
|
publish(turn, r, a, result)
|
||||||
|
|
||||||
proc publish*[T](turn: var Turn; r: Cap; a: T): Handle {.discardable.} =
|
proc publish*(r: Cap; a: Value): Handle {.turnAction.} =
|
||||||
publish(turn, r, a.toPreserves)
|
publish(activeTurn(), r, a, result)
|
||||||
|
|
||||||
proc retract(turn: var Turn; e: OutboundAssertion) =
|
proc retract(e: OutboundAssertion) {.turnAction.} =
|
||||||
enqueue(turn, e.peer.relay) do (turn: var Turn):
|
if e.established:
|
||||||
if e.established:
|
e.established = false
|
||||||
e.established = false
|
e.peer.target.retract(activeTurn(), e.handle)
|
||||||
e.peer.target.retract(turn, e.handle)
|
|
||||||
|
|
||||||
proc retract*(turn: var Turn; h: Handle) =
|
proc retract(turn: Turn; e: OutboundAssertion) =
|
||||||
|
enqueue(turn, e.peer.relay, whelp retract(e))
|
||||||
|
|
||||||
|
proc retract*(turn: Turn; h: Handle) =
|
||||||
var e: OutboundAssertion
|
var e: OutboundAssertion
|
||||||
if turn.facet.outbound.pop(h, e):
|
if turn.facet.outbound.pop(h, e):
|
||||||
turn.retract(e)
|
turn.retract(e)
|
||||||
|
|
||||||
proc message*(turn: var Turn; r: Cap; v: Value) =
|
proc message(target: Entity; a: AssertionRef) {.turnAction.} =
|
||||||
|
target.message(activeTurn(), a)
|
||||||
|
|
||||||
|
proc message*(turn: Turn; r: Cap; v: Value) =
|
||||||
var a = runRewrites(r.attenuation, v)
|
var a = runRewrites(r.attenuation, v)
|
||||||
if not a.isFalse:
|
if not a.isFalse:
|
||||||
enqueue(turn, r.relay) do (turn: var Turn):
|
enqueue(turn, r.relay, whelp message(r.target, AssertionRef(value: a)))
|
||||||
r.target.message(turn, AssertionRef(value: a))
|
|
||||||
|
|
||||||
proc message*[T](turn: var Turn; r: Cap; v: T) =
|
proc message*(target: Cap; value: Value) {.turnAction.} =
|
||||||
message(turn, r, v.toPreserves)
|
message(activeTurn(), target, value)
|
||||||
|
|
||||||
proc sync(turn: var Turn; e: Entity; peer: Cap) =
|
proc sync(e: Entity; peer: Cap) {.turnAction.} =
|
||||||
|
e.sync(activeTurn(), peer)
|
||||||
|
|
||||||
|
proc sync(turn: Turn; e: Entity; peer: Cap) =
|
||||||
e.sync(turn, peer)
|
e.sync(turn, peer)
|
||||||
|
|
||||||
proc sync*(turn: var Turn; r, peer: Cap) =
|
proc sync*(turn: Turn; r, peer: Cap) =
|
||||||
enqueue(turn, r.relay) do (turn: var Turn):
|
enqueue(turn, r.relay, whelp sync(r.target, peer))
|
||||||
sync(turn, r.target, peer)
|
|
||||||
|
|
||||||
proc replace*[T](turn: var Turn; cap: Cap; h: Handle; v: T): Handle =
|
proc replace*[T](turn: Turn; cap: Cap; h: Handle; v: T): Handle =
|
||||||
result = publish(turn, cap, v)
|
result = publish(turn, cap, v)
|
||||||
if h != default(Handle):
|
if h != default(Handle):
|
||||||
retract(turn, h)
|
retract(turn, h)
|
||||||
|
|
||||||
proc replace*[T](turn: var Turn; cap: Cap; h: var Handle; v: T): Handle {.discardable.} =
|
proc replace*[T](turn: Turn; cap: Cap; h: var Handle; v: T): Handle {.discardable.} =
|
||||||
var old = h
|
var old = h
|
||||||
h = publish(turn, cap, v)
|
h = publish(turn, cap, v)
|
||||||
if old != default(Handle):
|
if old != default(Handle):
|
||||||
retract(turn, old)
|
retract(turn, old)
|
||||||
h
|
h
|
||||||
|
|
||||||
proc stop*(turn: var Turn) {.gcsafe.}
|
proc stop*(turn: Turn)
|
||||||
|
|
||||||
proc run*(facet; action: TurnAction; zombieTurn = false) {.gcsafe.}
|
proc run*(facet; action: TurnAction; zombieTurn = false)
|
||||||
|
|
||||||
proc newFacet(actor; parent: Facet; initialAssertions: OutboundTable): Facet =
|
proc newFacet(actor; parent: Facet; initialAssertions: OutboundTable): Facet =
|
||||||
result = Facet(
|
result = Facet(
|
||||||
|
@ -372,15 +411,15 @@ proc preventInertCheck*(facet): (proc() {.gcsafe.}) {.discardable.} =
|
||||||
dec facet.inertCheckPreventers
|
dec facet.inertCheckPreventers
|
||||||
result = disarm
|
result = disarm
|
||||||
|
|
||||||
proc inFacet(turn: var Turn; facet; act: TurnAction) =
|
proc inFacet(turn: Turn; facet; act: TurnAction) =
|
||||||
## Call an action with a facet using a temporary `Turn`
|
## Call an action with a facet using a temporary `Turn`
|
||||||
## that shares the `Queues` of the calling `Turn`.
|
## that shares the `Queues` of the calling `Turn`.
|
||||||
var t = Turn(facet: facet, queues: turn.queues)
|
var t = Turn(facet: facet, queues: turn.queues)
|
||||||
act(t)
|
act(t)
|
||||||
|
|
||||||
proc terminate(actor; turn; reason: ref Exception) {.gcsafe.}
|
proc terminate(actor; turn; reason: ref Exception)
|
||||||
|
|
||||||
proc terminate(facet; turn: var Turn; orderly: bool) {.gcsafe.} =
|
proc terminate(facet; turn: Turn; orderly: bool) =
|
||||||
if facet.isAlive:
|
if facet.isAlive:
|
||||||
facet.isAlive = false
|
facet.isAlive = false
|
||||||
let parent = facet.parent
|
let parent = facet.parent
|
||||||
|
@ -400,33 +439,41 @@ proc terminate(facet; turn: var Turn; orderly: bool) {.gcsafe.} =
|
||||||
parent.terminate(turn, true)
|
parent.terminate(turn, true)
|
||||||
else:
|
else:
|
||||||
terminate(facet.actor, turn, nil)
|
terminate(facet.actor, turn, nil)
|
||||||
when tracing:
|
when traceSyndicate:
|
||||||
var act = ActionDescription(orKind: ActionDescriptionKind.facetStop)
|
var act = ActionDescription(orKind: ActionDescriptionKind.facetStop)
|
||||||
act.facetstop.path = facet.path
|
act.facetstop.path = facet.path
|
||||||
turn.desc.actions.add act
|
turn.desc.actions.add act
|
||||||
|
|
||||||
|
proc stopIfInert() {.turnAction.} =
|
||||||
|
let turn = activeTurn()
|
||||||
|
if (not turn.facet.parent.isNil and (not turn.facet.parent.isAlive)) or turn.facet.isInert:
|
||||||
|
stop(turn)
|
||||||
|
|
||||||
proc stopIfInertAfter(action: TurnAction): TurnAction =
|
proc stopIfInertAfter(action: TurnAction): TurnAction =
|
||||||
proc wrapper(turn: var Turn) =
|
proc wrapper(turn: Turn) =
|
||||||
action(turn)
|
action(turn)
|
||||||
enqueue(turn, turn.facet) do (turn: var Turn):
|
enqueue(turn, turn.facet, whelp stopIfInert())
|
||||||
if (not turn.facet.parent.isNil and
|
|
||||||
(not turn.facet.parent.isAlive)) or
|
|
||||||
turn.facet.isInert:
|
|
||||||
stop(turn)
|
|
||||||
wrapper
|
wrapper
|
||||||
|
|
||||||
proc newFacet*(turn: var Turn): Facet = newFacet(turn.facet.actor, turn.facet)
|
proc newFacet*(turn: Turn): Facet = newFacet(turn.facet.actor, turn.facet)
|
||||||
|
|
||||||
proc inFacet*(turn: var Turn; bootProc: TurnAction): Facet =
|
proc inFacet*(turn: Turn; bootProc: TurnAction): Facet =
|
||||||
result = newFacet(turn)
|
result = newFacet(turn)
|
||||||
when tracing:
|
when traceSyndicate:
|
||||||
var act = ActionDescription(orKind: ActionDescriptionKind.facetstart)
|
var act = ActionDescription(orKind: ActionDescriptionKind.facetstart)
|
||||||
act.facetstart.path.add result.path
|
act.facetstart.path.add result.path
|
||||||
turn.desc.actions.add act
|
turn.desc.actions.add act
|
||||||
inFacet(turn, result, stopIfInertAfter(bootProc))
|
inFacet(turn, result, stopIfInertAfter(bootProc))
|
||||||
|
|
||||||
proc facet*(turn: var Turn; bootProc: TurnAction): Facet {.deprecated.} = inFacet(turn, bootProc)
|
proc facet*(turn: Turn; bootProc: TurnAction): Facet {.deprecated.} = inFacet(turn, bootProc)
|
||||||
|
|
||||||
|
proc run(actor; bootProc: TurnAction; initialAssertions: OutboundTable) =
|
||||||
|
run(newFacet(actor, actor.root, initialAssertions), stopIfInertAfter(bootProc))
|
||||||
|
|
||||||
|
proc run(actor; bootProc: TurnAction) =
|
||||||
|
var initialAssertions: OutboundTable
|
||||||
|
run(newFacet(actor, actor.root, initialAssertions), stopIfInertAfter(bootProc))
|
||||||
|
|
||||||
proc newActor(name: string; handleAlloc: ref Handle): Actor =
|
proc newActor(name: string; handleAlloc: ref Handle): Actor =
|
||||||
let
|
let
|
||||||
now = getTime()
|
now = getTime()
|
||||||
|
@ -437,69 +484,84 @@ proc newActor(name: string; handleAlloc: ref Handle): Actor =
|
||||||
handleAllocator: handleAlloc,
|
handleAllocator: handleAlloc,
|
||||||
)
|
)
|
||||||
result.root = newFacet(result, nil)
|
result.root = newFacet(result, nil)
|
||||||
when tracing:
|
when traceSyndicate:
|
||||||
var act = ActorActivation(orKind: ActorActivationKind.start)
|
|
||||||
act.start.actorName = Name(orKind: NameKind.named)
|
|
||||||
act.start.actorName.named.name = name.toPreserves
|
|
||||||
trace(result, act)
|
|
||||||
|
|
||||||
proc run(actor; bootProc: TurnAction; initialAssertions: OutboundTable) =
|
|
||||||
run(newFacet(actor, actor.root, initialAssertions), stopIfInertAfter(bootProc))
|
|
||||||
|
|
||||||
proc bootActor*(name: string; bootProc: TurnAction): Actor =
|
|
||||||
var initialAssertions: OutboundTable
|
|
||||||
result = newActor(name, new(ref Handle))
|
|
||||||
when tracing:
|
|
||||||
new result.turnIdAllocator
|
new result.turnIdAllocator
|
||||||
|
|
||||||
|
proc newActor*(name: string): Actor =
|
||||||
|
newActor(name, new(ref Handle))
|
||||||
|
|
||||||
|
proc bootActor*(name: string; bootProc: TurnAction) =
|
||||||
|
var
|
||||||
|
initialAssertions: OutboundTable
|
||||||
|
actor = newActor(name)
|
||||||
|
when traceSyndicate:
|
||||||
let path = getEnv("SYNDICATE_TRACE_FILE", "/tmp/" & name & ".trace.pr")
|
let path = getEnv("SYNDICATE_TRACE_FILE", "/tmp/" & name & ".trace.pr")
|
||||||
case path
|
case path
|
||||||
of "": stderr.writeLine "$SYNDICATE_TRACE_FILE unset, not tracing actor ", name
|
of "": stderr.writeLine "$SYNDICATE_TRACE_FILE unset, not tracing actor ", name
|
||||||
of "-": result.traceStream = newFileStream(stderr)
|
of "-": actor.traceStream = newFileStream(stderr)
|
||||||
else: result.traceStream = openFileStream(path, fmWrite)
|
else: actor.traceStream = openFileStream(path, fmWrite)
|
||||||
run(result, bootProc, initialAssertions)
|
when traceSyndicate:
|
||||||
|
var act = ActorActivation(orKind: ActorActivationKind.start)
|
||||||
|
act.start.actorName = Name(orKind: NameKind.named)
|
||||||
|
act.start.actorName.named.name = name.toPreserves
|
||||||
|
var entry = TraceEntry(
|
||||||
|
timestamp: getTime().toUnixFloat(),
|
||||||
|
item: act)
|
||||||
|
actor.traceStream.writeLine($entry.toPreserves)
|
||||||
|
let turn = newTurn(actor, TurnCauseExternal(description: "top-level actor"))
|
||||||
|
run(actor, bootProc, initialAssertions)
|
||||||
|
|
||||||
proc spawn*(name: string; turn: var Turn; bootProc: TurnAction; initialAssertions = initHashSet[Handle]()): Actor {.discardable.} =
|
proc bootActor*(name: string; cont: Cont) =
|
||||||
|
bootActor(name) do (turn: Turn):
|
||||||
|
enqueue(turn, turn.facet, cont)
|
||||||
|
|
||||||
|
proc spawnActor(actor: Actor; bootProc: TurnAction; initialAssertions: HashSet[Handle]) {.turnAction.} =
|
||||||
|
let turn = activeTurn()
|
||||||
|
var newOutBound: Table[Handle, OutboundAssertion]
|
||||||
|
for key in initialAssertions:
|
||||||
|
discard turn.facet.outbound.pop(key, newOutbound[key])
|
||||||
|
when traceSyndicate:
|
||||||
|
actor.turnIdAllocator = turn.facet.actor.turnIdAllocator
|
||||||
|
actor.traceStream = turn.facet.actor.traceStream
|
||||||
|
var act = ActionDescription(orKind: ActionDescriptionKind.spawn)
|
||||||
|
act.spawn.id = actor.id.toPreserves
|
||||||
|
turn.desc.actions.add act
|
||||||
|
run(actor, bootProc, newOutBound)
|
||||||
|
|
||||||
|
proc spawn*(name: string; turn: Turn; bootProc: TurnAction; initialAssertions = initHashSet[Handle]()): Actor {.discardable.} =
|
||||||
let actor = newActor(name, turn.facet.actor.handleAllocator)
|
let actor = newActor(name, turn.facet.actor.handleAllocator)
|
||||||
enqueue(turn, turn.facet) do (turn: var Turn):
|
enqueue(turn, turn.facet, whelp spawnActor(actor, bootProc, initialAssertions))
|
||||||
var newOutBound: Table[Handle, OutboundAssertion]
|
|
||||||
for key in initialAssertions:
|
|
||||||
discard turn.facet.outbound.pop(key, newOutbound[key])
|
|
||||||
when tracing:
|
|
||||||
actor.turnIdAllocator = turn.facet.actor.turnIdAllocator
|
|
||||||
actor.traceStream = turn.facet.actor.traceStream
|
|
||||||
var act = ActionDescription(orKind: ActionDescriptionKind.spawn)
|
|
||||||
act.spawn.id = actor.id.toPreserves
|
|
||||||
turn.desc.actions.add act
|
|
||||||
run(actor, bootProc, newOutBound)
|
|
||||||
actor
|
actor
|
||||||
|
|
||||||
proc newInertCap*(): Cap =
|
proc newInertCap*(): Cap =
|
||||||
let a = bootActor("inert") do (turn: var Turn): turn.stop()
|
let a = newActor("inert")
|
||||||
|
run(a) do (turn: Turn): turn.stop()
|
||||||
Cap(relay: a.root)
|
Cap(relay: a.root)
|
||||||
|
|
||||||
proc atExit*(actor; action) = actor.exitHooks.add action
|
proc atExit*(actor; action) = actor.exitHooks.add action
|
||||||
|
|
||||||
|
proc terminate(actor: Actor; orderly: bool) {.turnAction.} =
|
||||||
|
actor.root.terminate(activeTurn(), orderly)
|
||||||
|
actor.exited = true
|
||||||
|
|
||||||
proc terminate(actor; turn; reason: ref Exception) =
|
proc terminate(actor; turn; reason: ref Exception) =
|
||||||
if not actor.exiting:
|
if not actor.exiting:
|
||||||
actor.exiting = true
|
actor.exiting = true
|
||||||
actor.exitReason = reason
|
actor.exitReason = reason
|
||||||
when tracing:
|
when traceSyndicate:
|
||||||
var act = ActorActivation(orKind: ActorActivationKind.stop)
|
var act = ActorActivation(orKind: ActorActivationKind.stop)
|
||||||
if not reason.isNil:
|
if not reason.isNil:
|
||||||
act.stop.status = ExitStatus(orKind: ExitStatusKind.Error)
|
act.stop.status = ExitStatus(orKind: ExitStatusKind.Error)
|
||||||
act.stop.status.error.message = reason.msg
|
act.stop.status.error.message = reason.msg
|
||||||
trace(actor, act)
|
trace(actor, act)
|
||||||
for hook in actor.exitHooks: hook(turn)
|
for hook in actor.exitHooks: hook(turn)
|
||||||
proc finish(turn: var Turn) =
|
enqueue(turn, actor.root, whelp terminate(actor, reason.isNil))
|
||||||
actor.root.terminate(turn, reason.isNil)
|
|
||||||
actor.exited = true
|
|
||||||
callSoon do ():
|
|
||||||
run(actor.root, finish, true)
|
|
||||||
|
|
||||||
proc terminate*(facet; e: ref Exception) =
|
proc terminate*(facet; e: ref Exception) =
|
||||||
run(facet.actor.root) do (turn: var Turn):
|
run(facet.actor.root) do (turn: Turn):
|
||||||
facet.actor.terminate(turn, e)
|
facet.actor.terminate(turn, e)
|
||||||
|
|
||||||
|
#[
|
||||||
proc asyncCheck*(facet: Facet; fut: FutureBase) =
|
proc asyncCheck*(facet: Facet; fut: FutureBase) =
|
||||||
## Sets a callback on `fut` which propagates exceptions to `facet`.
|
## Sets a callback on `fut` which propagates exceptions to `facet`.
|
||||||
addCallback(fut) do ():
|
addCallback(fut) do ():
|
||||||
|
@ -508,44 +570,67 @@ proc asyncCheck*(facet: Facet; fut: FutureBase) =
|
||||||
proc asyncCheck*(turn; fut: FutureBase) =
|
proc asyncCheck*(turn; fut: FutureBase) =
|
||||||
## Sets a callback on `fut` which propagates exceptions to the facet of `turn`.
|
## Sets a callback on `fut` which propagates exceptions to the facet of `turn`.
|
||||||
asyncCheck(turn.facet, fut)
|
asyncCheck(turn.facet, fut)
|
||||||
|
]#
|
||||||
|
|
||||||
template tryFacet(facet; body: untyped) =
|
template tryFacet(facet; body: untyped) =
|
||||||
try: body
|
try: body
|
||||||
except CatchableError as err: terminate(facet, err)
|
except CatchableError as err: terminate(facet, err)
|
||||||
|
|
||||||
|
proc run(facet: Facet; turn: Turn; deq: var Deque[Cont]): int =
|
||||||
|
## Return the number of continuations processed.
|
||||||
|
while deq.len > 0:
|
||||||
|
var c = deq.popFirst()
|
||||||
|
try:
|
||||||
|
while not c.isNil and not c.fn.isNil:
|
||||||
|
c.turn = turn
|
||||||
|
var y = c.fn
|
||||||
|
var x = y(c)
|
||||||
|
inc(result)
|
||||||
|
c = Cont(x)
|
||||||
|
except CatchableError as err:
|
||||||
|
if not c.dismissed:
|
||||||
|
writeStackFrames c
|
||||||
|
terminate(facet, err)
|
||||||
|
stderr.writeLine("ran ", result, " continuations for ", facet)
|
||||||
|
|
||||||
proc run*(facet; action: TurnAction; zombieTurn = false) =
|
proc run*(facet; action: TurnAction; zombieTurn = false) =
|
||||||
if zombieTurn or (facet.actor.exitReason.isNil and facet.isAlive):
|
if zombieTurn or (facet.actor.exitReason.isNil and facet.isAlive):
|
||||||
tryFacet(facet):
|
tryFacet(facet):
|
||||||
var queues = newTable[Facet, seq[TurnAction]]()
|
var queues = newTable[Facet, Deque[Cont]]()
|
||||||
block:
|
var turn = Turn(facet: facet, queues: queues)
|
||||||
var turn = Turn(facet: facet, queues: queues)
|
action(turn)
|
||||||
action(turn)
|
when traceSyndicate:
|
||||||
when tracing:
|
turn.desc.id = facet.nextTurnId.toPreserves
|
||||||
turn.desc.id = facet.nextTurnId.toPreserves
|
facet.actor.trace ActorActivation(
|
||||||
facet.actor.trace ActorActivation(
|
orKind: ActorActivationKind.turn, turn: turn.desc)
|
||||||
orKind: ActorActivationKind.turn, turn: turn.desc)
|
assert not turn.isNil
|
||||||
for facet, queue in queues:
|
var n = 1
|
||||||
for action in queue: run(facet, action)
|
while n > 0:
|
||||||
|
n = 0
|
||||||
|
var facets = queues.keys.toSeq
|
||||||
|
for facet in facets:
|
||||||
|
n.inc run(facet, turn, queues[facet])
|
||||||
|
|
||||||
proc run*(cap: Cap; action: TurnAction) =
|
proc run*(cap: Cap; action: TurnAction) =
|
||||||
## Convenience proc to run a `TurnAction` in the scope of a `Cap`.
|
## Convenience proc to run a `TurnAction` in the scope of a `Cap`.
|
||||||
run(cap.relay, action)
|
run(cap.relay, action)
|
||||||
|
|
||||||
|
#[
|
||||||
proc addCallback*(fut: FutureBase; facet: Facet; act: TurnAction) =
|
proc addCallback*(fut: FutureBase; facet: Facet; act: TurnAction) =
|
||||||
## Add a callback to a `Future` that will be called at a later `Turn`
|
## Add a callback to a `Future` that will be called at a later `Turn`
|
||||||
## within the context of `facet`.
|
## within the context of `facet`.
|
||||||
addCallback(fut) do ():
|
addCallback(fut) do ():
|
||||||
if fut.failed: terminate(facet, fut.error)
|
if fut.failed: terminate(facet, fut.error)
|
||||||
else:
|
else:
|
||||||
when tracing:
|
when traceSyndicate:
|
||||||
run(facet) do (turn: var Turn):
|
run(facet) do (turn: Turn):
|
||||||
turn.desc.cause = TurnCause(orKind: TurnCauseKind.external)
|
turn.desc.cause = TurnCause(orKind: TurnCauseKind.external)
|
||||||
turn.desc.cause.external.description = "Future".toPreserves
|
turn.desc.cause.external.description = "Future".toPreserves
|
||||||
act(turn)
|
act(turn)
|
||||||
else:
|
else:
|
||||||
run(facet, act)
|
run(facet, act)
|
||||||
|
|
||||||
proc addCallback*(fut: FutureBase; turn: var Turn; act: TurnAction) =
|
proc addCallback*(fut: FutureBase; turn: Turn; act: TurnAction) =
|
||||||
## Add a callback to a `Future` that will be called at a later `Turn`
|
## Add a callback to a `Future` that will be called at a later `Turn`
|
||||||
## with the same context as the current.
|
## with the same context as the current.
|
||||||
if fut.failed:
|
if fut.failed:
|
||||||
|
@ -555,35 +640,35 @@ proc addCallback*(fut: FutureBase; turn: var Turn; act: TurnAction) =
|
||||||
else:
|
else:
|
||||||
addCallback(fut, turn.facet, act)
|
addCallback(fut, turn.facet, act)
|
||||||
|
|
||||||
proc addCallback*[T](fut: Future[T]; turn: var Turn; act: proc (t: var Turn, x: T) {.gcsafe.}) =
|
proc addCallback*[T](fut: Future[T]; turn: Turn; act: proc (t: Turn, x: T) {.gcsafe.}) =
|
||||||
addCallback(fut, turn) do (turn: var Turn):
|
addCallback(fut, turn) do (turn: Turn):
|
||||||
if fut.failed: terminate(turn.facet, fut.error)
|
if fut.failed: terminate(turn.facet, fut.error)
|
||||||
else:
|
else:
|
||||||
when tracing:
|
when traceSyndicate:
|
||||||
turn.desc.cause = TurnCause(orKind: TurnCauseKind.external)
|
turn.desc.cause = TurnCause(orKind: TurnCauseKind.external)
|
||||||
turn.desc.cause.external.description = "Future".toPreserves
|
turn.desc.cause.external.description = "Future".toPreserves
|
||||||
act(turn, read fut)
|
act(turn, read fut)
|
||||||
|
]#
|
||||||
|
|
||||||
proc stop*(turn: var Turn, facet: Facet) =
|
proc stop*(turn: Turn, facet: Facet) =
|
||||||
if facet.parent.isNil:
|
if facet.parent.isNil:
|
||||||
facet.terminate(turn, true)
|
facet.terminate(turn, true)
|
||||||
else:
|
else:
|
||||||
enqueue(turn, facet.parent) do (turn: var Turn):
|
enqueue(turn, facet.parent, whelp terminate(facet.actor, true))
|
||||||
facet.terminate(turn, true)
|
# TODO: terminate the actor?
|
||||||
|
|
||||||
proc stop*(turn: var Turn) =
|
proc stop*(turn: Turn) =
|
||||||
stop(turn, turn.facet)
|
stop(turn, turn.facet)
|
||||||
|
|
||||||
proc onStop*(facet: Facet; act: TurnAction) =
|
proc onStop*(facet: Facet; act: TurnAction) =
|
||||||
## Add a `proc (turn: var Turn)` action to `facet` to be called as it stops.
|
## Add a `proc (turn: Turn)` action to `facet` to be called as it stops.
|
||||||
add(facet.shutdownActions, act)
|
add(facet.shutdownActions, act)
|
||||||
|
|
||||||
proc stopActor*(turn: var Turn) =
|
proc stopActor*(turn: Turn) =
|
||||||
let actor = turn.facet.actor
|
let actor = turn.facet.actor
|
||||||
enqueue(turn, actor.root) do (turn: var Turn):
|
enqueue(turn, actor.root, whelp terminate(actor, true))
|
||||||
terminate(actor, turn, nil)
|
|
||||||
|
|
||||||
proc freshen*(turn: var Turn, act: TurnAction) =
|
proc freshen*(turn: Turn, act: TurnAction) =
|
||||||
assert(turn.queues.len == 0, "Attempt to freshen a non-stale Turn")
|
assert(turn.queues.len == 0, "Attempt to freshen a non-stale Turn")
|
||||||
run(turn.facet, act)
|
run(turn.facet, act)
|
||||||
|
|
||||||
|
@ -599,13 +684,16 @@ proc newCap*(e: Entity; turn): Cap =
|
||||||
type SyncContinuation {.final.} = ref object of Entity
|
type SyncContinuation {.final.} = ref object of Entity
|
||||||
action: TurnAction
|
action: TurnAction
|
||||||
|
|
||||||
method message(entity: SyncContinuation; turn: var Turn; v: AssertionRef) =
|
method message(entity: SyncContinuation; turn: Turn; v: AssertionRef) =
|
||||||
entity.action(turn)
|
entity.action(turn)
|
||||||
|
|
||||||
proc sync*(turn: var Turn; refer: Cap; act: TurnAction) =
|
proc sync*(turn: Turn; refer: Cap; act: TurnAction) =
|
||||||
sync(turn, refer, newCap(turn, SyncContinuation(action: act)))
|
sync(turn, refer, newCap(turn, SyncContinuation(action: act)))
|
||||||
|
|
||||||
proc running*(actor): bool =
|
proc running*(actor): bool =
|
||||||
result = not actor.exited
|
result = not actor.exited
|
||||||
if not (result or actor.exitReason.isNil):
|
if not (result or actor.exitReason.isNil):
|
||||||
raise actor.exitReason
|
raise actor.exitReason
|
||||||
|
|
||||||
|
proc newCap*(e: Entity): Cap {.turnAction.} =
|
||||||
|
Cap(relay: activeTurn().facet, target: e)
|
|
@ -0,0 +1,677 @@
|
||||||
|
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||||
|
# SPDX-License-Identifier: Unlicense
|
||||||
|
|
||||||
|
# Use procedures to call into SAM,
|
||||||
|
# use continuations to call within SAM.
|
||||||
|
|
||||||
|
import std/[deques, hashes, options, sets, tables, times]
|
||||||
|
import pkg/cps
|
||||||
|
import preserves
|
||||||
|
import ./protocols/[protocol, sturdy]
|
||||||
|
|
||||||
|
# const traceSyndicate {.booldefine.}: bool = true
|
||||||
|
const traceSyndicate* = true
|
||||||
|
|
||||||
|
when traceSyndicate:
|
||||||
|
import std/streams
|
||||||
|
from std/os import getEnv
|
||||||
|
import ./protocols/trace
|
||||||
|
|
||||||
|
export protocol.Handle
|
||||||
|
|
||||||
|
type
|
||||||
|
Cont* = ref object of Continuation
|
||||||
|
turn: Turn
|
||||||
|
|
||||||
|
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.}
|
||||||
|
|
||||||
|
HandlerDeque = seq[ContinuationProc[Continuation]]
|
||||||
|
|
||||||
|
FacetState = enum fFresh, fRunning, fEnded
|
||||||
|
|
||||||
|
Callback* = proc () {.closure.}
|
||||||
|
|
||||||
|
OutboundTable = Table[Handle, OutboundAssertion]
|
||||||
|
OutboundAssertion = ref object
|
||||||
|
handle: Handle
|
||||||
|
peer: Cap
|
||||||
|
established: bool
|
||||||
|
|
||||||
|
Facet* = ref object
|
||||||
|
## https://synit.org/book/glossary.html#facet
|
||||||
|
actor: Actor
|
||||||
|
parent: Facet
|
||||||
|
children: seq[Facet]
|
||||||
|
outbound: OutboundTable
|
||||||
|
stopHandlers: HandlerDeque
|
||||||
|
stopCallbacks: seq[Callback]
|
||||||
|
state: FacetState
|
||||||
|
id: FacetId
|
||||||
|
|
||||||
|
FacetProc* = proc (f: Facet) {.closure.}
|
||||||
|
## Type for callbacks to be called within a turn.
|
||||||
|
## The `Facet` parameter is the owning facet.
|
||||||
|
|
||||||
|
Turn = ref object
|
||||||
|
## https://synit.org/book/glossary.html#turn
|
||||||
|
actor: Actor
|
||||||
|
work: Dequeue[Cont]
|
||||||
|
actions: seq[Cont]
|
||||||
|
event: Option[protocol.Event]
|
||||||
|
rollback: bool
|
||||||
|
when traceSyndicate:
|
||||||
|
desc: TurnDescription
|
||||||
|
|
||||||
|
Entity* = ref object of RootObj
|
||||||
|
## https://synit.org/book/glossary.html#entity
|
||||||
|
publishImpl: PublishProc
|
||||||
|
retractImpl: RetractProc
|
||||||
|
messageImpl: MessageProc
|
||||||
|
syncImpl: SyncProc
|
||||||
|
facet*: Facet
|
||||||
|
# This implementation associates Entities to
|
||||||
|
# Facets, which is not to be taken as a SAM
|
||||||
|
# axiom.
|
||||||
|
oid*: sturdy.Oid # oid is how Entities are identified over the wire
|
||||||
|
|
||||||
|
Cap* {.final, preservesEmbedded.} = ref object of EmbeddedObj
|
||||||
|
target*: Entity
|
||||||
|
attenuation*: seq[sturdy.Caveat]
|
||||||
|
|
||||||
|
Actor* = ref object
|
||||||
|
## https://synit.org/book/glossary.html#actor
|
||||||
|
# TODO: run on a seperate thread.
|
||||||
|
# crashHandlers: HandlerDeque
|
||||||
|
root: Facet
|
||||||
|
handleAllocator: Handle
|
||||||
|
facetIdAllocator: int
|
||||||
|
id: ActorId
|
||||||
|
when traceSyndicate:
|
||||||
|
traceStream: FileStream
|
||||||
|
stopped: bool
|
||||||
|
|
||||||
|
var turnQueue {.threadvar, used.}: Deque[Turn]
|
||||||
|
|
||||||
|
proc queueTurn(turn: sink Turn) =
|
||||||
|
turnQueue.addLast(turn)
|
||||||
|
|
||||||
|
proc turnsPending*(): bool =
|
||||||
|
turnQueue.len > 0
|
||||||
|
|
||||||
|
template turnWork*(prc: typed): untyped =
|
||||||
|
## Pragma to mark work that executes in a `Turn` context.
|
||||||
|
cps(Cont, prc)
|
||||||
|
|
||||||
|
proc activeTurn(c: Cont): Turn {.cpsVoodoo.} =
|
||||||
|
## Return the active `Turn` within a turn context.
|
||||||
|
assert not c.turn.isNil
|
||||||
|
c.turn
|
||||||
|
|
||||||
|
proc activeFacet(c: Cont): Facet {.cpsVoodoo.} =
|
||||||
|
## Return the active `Facet` within a turn context.
|
||||||
|
assert not c.turn.isNil
|
||||||
|
assert not c.turn.facet.isNil
|
||||||
|
c.turn.facet
|
||||||
|
|
||||||
|
using
|
||||||
|
actor: Actor
|
||||||
|
facet: Facet
|
||||||
|
entity: Entity
|
||||||
|
cap: Cap
|
||||||
|
turn: Turn
|
||||||
|
|
||||||
|
proc `$`*(facet): string = $facet.id
|
||||||
|
proc `$`*(cap): string = "#:" & $cast[uint](cap.unsafeAddr)
|
||||||
|
|
||||||
|
proc hash*(x: Actor|Facet|Cap): Hash = x.unsafeAddr.hash
|
||||||
|
|
||||||
|
proc relay*(cap): Facet =
|
||||||
|
assert not cap.target.facet.isNil
|
||||||
|
cap.target.facet
|
||||||
|
|
||||||
|
proc collectPath(result: var seq[FacetId]; facet) =
|
||||||
|
if not facet.parent.isNil:
|
||||||
|
collectPath(result, facet.parent)
|
||||||
|
result.add(facet.id)
|
||||||
|
|
||||||
|
proc stopped*(facet): bool = facet.state != fRunning
|
||||||
|
|
||||||
|
when traceSyndicate:
|
||||||
|
proc traceFlush(actor) =
|
||||||
|
if not actor.traceStream.isNil:
|
||||||
|
actor.traceStream.flush()
|
||||||
|
|
||||||
|
proc trace(actor; act: ActorActivation) =
|
||||||
|
if not actor.traceStream.isNil:
|
||||||
|
var entry = TraceEntry(
|
||||||
|
timestamp: getTime().toUnixFloat(),
|
||||||
|
actor: actor.id,
|
||||||
|
item: act,
|
||||||
|
)
|
||||||
|
actor.traceStream.writeLine($entry.toPreserves)
|
||||||
|
|
||||||
|
proc traceTurn(actor) =
|
||||||
|
if not actor.traceStream.isNil:
|
||||||
|
actor.trace(ActorActivation(
|
||||||
|
orKind: ActorActivationKind.turn,
|
||||||
|
turn: actor.turn.desc,
|
||||||
|
))
|
||||||
|
actor.traceFlush()
|
||||||
|
reset actor.turn.desc
|
||||||
|
|
||||||
|
proc traceTarget(facet): trace.Target =
|
||||||
|
Target(
|
||||||
|
actor: facet.actor.id,
|
||||||
|
facet: facet.id,
|
||||||
|
)
|
||||||
|
|
||||||
|
proc traceTarget(cap): trace.Target =
|
||||||
|
let facet = cap.relay
|
||||||
|
Target(
|
||||||
|
actor: facet.actor.id,
|
||||||
|
facet: facet.id,
|
||||||
|
oid: cap.target.oid.toPreserves,
|
||||||
|
)
|
||||||
|
|
||||||
|
proc traceEnqueue(actor; e: TargetedTurnEvent) =
|
||||||
|
actor.turn.desc.actions.add ActionDescription(
|
||||||
|
orKind: ActionDescriptionKind.enqueue,
|
||||||
|
enqueue: ActionDescriptionEnqueue(event: e),
|
||||||
|
)
|
||||||
|
|
||||||
|
proc traceDequeue(actor; e: TargetedTurnEvent) =
|
||||||
|
actor.turn.desc.actions.add ActionDescription(
|
||||||
|
orKind: ActionDescriptionKind.dequeue,
|
||||||
|
dequeue: ActionDescriptionDequeue(event: e),
|
||||||
|
)
|
||||||
|
|
||||||
|
proc pass*(a, b: Cont): Cont =
|
||||||
|
assert not a.facet.isNil
|
||||||
|
b.facet = a.facet
|
||||||
|
b
|
||||||
|
|
||||||
|
proc queueWork*(facet; c: Cont) =
|
||||||
|
c.facet = facet
|
||||||
|
facet.actor.turn.work.addLast(c)
|
||||||
|
|
||||||
|
proc queueAction*(facet; c: Cont) =
|
||||||
|
c.facet = facet
|
||||||
|
facet.actor.turn.actions.add(c)
|
||||||
|
|
||||||
|
proc yieldWork(c: Cont): Cont {.cpsMagic.} =
|
||||||
|
## Suspend and enqueue the caller until later in the turn.
|
||||||
|
assert not c.facet.isNil
|
||||||
|
c.facet.queueWork(c)
|
||||||
|
nil
|
||||||
|
|
||||||
|
proc yieldToActions(c: Cont): Cont {.cpsMagic.} =
|
||||||
|
assert not c.facet.isNil
|
||||||
|
c.facet.actor.turn.actions.add(c)
|
||||||
|
nil
|
||||||
|
|
||||||
|
proc terminate(actor; err: ref Exception) =
|
||||||
|
when traceSyndicate:
|
||||||
|
actor.traceTurn()
|
||||||
|
raise err
|
||||||
|
|
||||||
|
proc terminate(facet; err: ref Exception) =
|
||||||
|
terminate(facet.actor, err)
|
||||||
|
|
||||||
|
proc complete(c: Cont) =
|
||||||
|
var c = c
|
||||||
|
try:
|
||||||
|
while not c.isNil and not c.fn.isNil:
|
||||||
|
var y = c.fn
|
||||||
|
var x = y(c)
|
||||||
|
c = Cont(x)
|
||||||
|
except CatchableError as err:
|
||||||
|
c.facet.actor.turn.rollback = true
|
||||||
|
if not c.dismissed:
|
||||||
|
writeStackFrames c
|
||||||
|
terminate(c.facet, err)
|
||||||
|
|
||||||
|
proc run*(actor): bool =
|
||||||
|
if actor.stopped: return
|
||||||
|
try:
|
||||||
|
result = actor.turn.work.len > 0
|
||||||
|
while actor.turn.work.len > 0:
|
||||||
|
actor.turn.work.popFirst().complete()
|
||||||
|
var i: int
|
||||||
|
while i < actor.turn.actions.len:
|
||||||
|
complete(move actor.turn.actions[i])
|
||||||
|
inc i
|
||||||
|
actor.turn.actions.setLen(0)
|
||||||
|
when traceSyndicate:
|
||||||
|
actor.traceTurn()
|
||||||
|
if actor.stopped:
|
||||||
|
trace(actor, ActorActivation(orkind: ActorActivationKind.stop))
|
||||||
|
for child in actor.children:
|
||||||
|
result = result and run(child)
|
||||||
|
except Exception as err:
|
||||||
|
actor.terminate(err)
|
||||||
|
|
||||||
|
proc start(actor; cont: Cont) =
|
||||||
|
when traceSyndicate:
|
||||||
|
var act = ActorActivation(orkind: ActorActivationKind.start)
|
||||||
|
trace(actor, act)
|
||||||
|
actor.root.state = fRunning
|
||||||
|
actor.root.startExternalTurn()
|
||||||
|
actor.root.queueWork(cont)
|
||||||
|
|
||||||
|
proc stop*(actor)
|
||||||
|
|
||||||
|
proc runNextStop(c: Cont; facet: Facet): Cont {.cpsMagic.} =
|
||||||
|
c.fn = facet.stopHandlers.pop()
|
||||||
|
result = c
|
||||||
|
|
||||||
|
proc runNextFacetStop() {.cps: Cont.} =
|
||||||
|
activeFacet().runNextStop()
|
||||||
|
|
||||||
|
proc stop(facet; reason: FacetStopReason) =
|
||||||
|
let actor = facet.actor
|
||||||
|
while facet.stopHandlers.len > 0:
|
||||||
|
var c = whelp runNextFacetStop()
|
||||||
|
c.facet = facet
|
||||||
|
complete(c)
|
||||||
|
while facet.stopCallbacks.len > 0:
|
||||||
|
var cb = facet.stopCallbacks.pop()
|
||||||
|
cb()
|
||||||
|
while facet.children.len > 0:
|
||||||
|
stop(facet.children.pop(), FacetStopReason.parentStopping)
|
||||||
|
when traceSyndicate:
|
||||||
|
var act = ActionDescription(orKind: ActionDescriptionKind.facetstop)
|
||||||
|
collectPath(act.facetstop.path, facet)
|
||||||
|
act.facetStop.reason = reason
|
||||||
|
actor.turn.desc.actions.add act
|
||||||
|
if facet.parent.isNil:
|
||||||
|
actor.root = nil
|
||||||
|
stop(actor)
|
||||||
|
|
||||||
|
proc stop*(actor) =
|
||||||
|
if not actor.root.isNil:
|
||||||
|
stop(actor.root, FacetStopReason.actorStopping)
|
||||||
|
actor.stopped = true
|
||||||
|
|
||||||
|
proc stopped*(actor): bool =
|
||||||
|
actor.`stopped`
|
||||||
|
|
||||||
|
# proc stopFacetAction(reason: FacetStopReason) {.syndicate.} =
|
||||||
|
# stop(c.facet, reason)
|
||||||
|
|
||||||
|
proc stopActorAction() {.cps: Cont.} =
|
||||||
|
yieldToActions()
|
||||||
|
activeFacet().actor.stop()
|
||||||
|
|
||||||
|
proc stopActor*(facet) =
|
||||||
|
let c = whelp stopActorAction()
|
||||||
|
c.facet = facet
|
||||||
|
facet.actor.turn.actions.add(c)
|
||||||
|
|
||||||
|
proc stopActor*(cap) =
|
||||||
|
## Stop an `Actor` from a `Cap`.
|
||||||
|
stopActor(cap.relay)
|
||||||
|
|
||||||
|
type
|
||||||
|
AssertionRef* = ref object
|
||||||
|
value*: Value
|
||||||
|
# if the Enity methods take a Value object then the generated
|
||||||
|
# C code has "redefinition of struct" problems when orc is enabled
|
||||||
|
|
||||||
|
proc newCap*(facet; entity): Cap =
|
||||||
|
doAssert entity.facet.isNil
|
||||||
|
entity.facet = facet
|
||||||
|
Cap(target: entity)
|
||||||
|
|
||||||
|
proc nextHandle(facet: Facet): Handle =
|
||||||
|
inc(facet.actor.handleAllocator)
|
||||||
|
facet.actor.handleAllocator
|
||||||
|
|
||||||
|
proc actor(cap): Actor = cap.relay.actor
|
||||||
|
|
||||||
|
type Bindings = Table[Value, Value]
|
||||||
|
|
||||||
|
proc attenuate(cap; att: seq[Caveat]): Cap =
|
||||||
|
if att.len == 0: cap
|
||||||
|
else: Cap(
|
||||||
|
target: cap.target,
|
||||||
|
attenuation: att & cap.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 setActions*(entity;
|
||||||
|
publish = PublishProc();
|
||||||
|
retract = RetractProc();
|
||||||
|
message = MessageProc();
|
||||||
|
sync = SyncProc();
|
||||||
|
): Entity {.discardable} =
|
||||||
|
## Set the action handlers for an `Entity`.
|
||||||
|
result = entity
|
||||||
|
result.publishImpl = publish
|
||||||
|
result.retractImpl = retract
|
||||||
|
result.messageImpl = message
|
||||||
|
result.syncImpl = sync
|
||||||
|
|
||||||
|
proc publish(c: Cont; e: Entity; v: Value; h: Handle): Cont {.cpsMagic.} =
|
||||||
|
if not e.publishImpl.fn.isNil:
|
||||||
|
result = pass(c, e.publishImpl.call(e, v, h))
|
||||||
|
|
||||||
|
proc retract(c: Cont; e: Entity; h: Handle): Cont {.cpsMagic.} =
|
||||||
|
if not e.retractImpl.fn.isNil:
|
||||||
|
result = pass(c, e.retractImpl.call(e, h))
|
||||||
|
|
||||||
|
proc message(c: Cont; e: Entity; v: Value): Cont {.cpsMagic.} =
|
||||||
|
if not e.messageImpl.fn.isNil:
|
||||||
|
result = pass(c, e.messageImpl.call(e, v))
|
||||||
|
|
||||||
|
proc sync(c: Cont; e: Entity; p: Cap): Cont {.cpsMagic.} =
|
||||||
|
if not e.syncImpl.fn.isNil:
|
||||||
|
result = pass(c, e.syncImpl.call(e, p))
|
||||||
|
|
||||||
|
proc turnPublish(cap: Cap; val: Value; h: Handle) {.turnWork.} =
|
||||||
|
when traceSyndicate:
|
||||||
|
var traceEvent = TargetedTurnEvent(
|
||||||
|
target: cap.traceTarget,
|
||||||
|
detail: trace.TurnEvent(orKind: trace.TurnEventKind.assert)
|
||||||
|
)
|
||||||
|
traceEvent.detail = trace.TurnEvent(orKind: TurnEventKind.assert)
|
||||||
|
traceEvent.detail.assert = TurnEventAssert(
|
||||||
|
assertion: AssertionDescription(orKind: AssertionDescriptionKind.value),
|
||||||
|
handle: h,
|
||||||
|
)
|
||||||
|
traceEvent.detail.assert.assertion.value.value = val
|
||||||
|
cap.actor.traceEnqueue(traceEvent)
|
||||||
|
cap.relay.outbound[h] = OutboundAssertion(handle: h, peer: cap)
|
||||||
|
yieldToActions()
|
||||||
|
if activeTurn().rollback:
|
||||||
|
cap.relay.outbound.del(h)
|
||||||
|
else:
|
||||||
|
when traceSyndicate:
|
||||||
|
cap.actor.traceDequeue(traceEvent)
|
||||||
|
cap.target.publish(val, h)
|
||||||
|
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)
|
||||||
|
)
|
||||||
|
traceEvent.detail.retract.handle = h
|
||||||
|
cap.actor.traceEnqueue(traceEvent)
|
||||||
|
yieldToActions()
|
||||||
|
if not activeTurn().rollback:
|
||||||
|
when traceSyndicate:
|
||||||
|
cap.actor.traceDequeue(traceEvent)
|
||||||
|
var e: OutboundAssertion
|
||||||
|
if cap.relay.outbound.pop(h, e):
|
||||||
|
cap.target.retract(h)
|
||||||
|
|
||||||
|
proc turnMessage(cap: Cap; val: Value) {.turnWork.} =
|
||||||
|
var val = runRewrites(val, cap.attenuation)
|
||||||
|
when traceSyndicate:
|
||||||
|
var traceEvent = TargetedTurnEvent(
|
||||||
|
target: cap.traceTarget,
|
||||||
|
detail: trace.TurnEvent(orKind: trace.TurnEventKind.message)
|
||||||
|
)
|
||||||
|
traceEvent.detail.message.body.value.value = val
|
||||||
|
cap.actor.traceEnqueue(traceEvent)
|
||||||
|
yieldToActions()
|
||||||
|
if not activeTurn().rollback:
|
||||||
|
when traceSyndicate:
|
||||||
|
cap.actor.traceDequeue(traceEvent)
|
||||||
|
cap.target.message(val)
|
||||||
|
|
||||||
|
proc turnSync(cap: Cap; peer: Cap) {.turnWork.} =
|
||||||
|
when traceSyndicate:
|
||||||
|
var traceEvent = TargetedTurnEvent(
|
||||||
|
target: cap.traceTarget,
|
||||||
|
detail: trace.TurnEvent(orKind: trace.TurnEventKind.sync)
|
||||||
|
)
|
||||||
|
traceEvent.detail.sync.peer = peer.traceTarget
|
||||||
|
cap.actor.traceEnqueue(traceEvent)
|
||||||
|
yieldToActions()
|
||||||
|
if not activeTurn().rollback:
|
||||||
|
when traceSyndicate:
|
||||||
|
cap.actor.traceDequeue(traceEvent)
|
||||||
|
cap.target.sync(peer)
|
||||||
|
|
||||||
|
proc publish*(cap; val: Value): Handle =
|
||||||
|
## Publish a `Value` to a `Cap` returning `Handle`
|
||||||
|
## for later retraction.
|
||||||
|
# The publish action on an Entity always goes through
|
||||||
|
# here first.
|
||||||
|
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 Preserves-convertable value to a `Cap`
|
||||||
|
## returning `Handle` for later retraction.
|
||||||
|
publish(cap, x.toPreserves)
|
||||||
|
|
||||||
|
proc retract*(cap; h: Handle) =
|
||||||
|
## Retract a `Handle` from a `Cap`.
|
||||||
|
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)
|
||||||
|
return c
|
||||||
|
|
||||||
|
proc addOnStopHandler(c: Cont; cb: Callback): Cont {.cpsMagic.} =
|
||||||
|
c.facet.stopCallbacks.add(cb)
|
||||||
|
result = c
|
||||||
|
|
||||||
|
proc onStop*(facet; cb: Callback) =
|
||||||
|
facet.stopCallbacks.add(cb)
|
||||||
|
|
||||||
|
proc facetCall(prc: FacetProc; f: Facet) {.cps: Cont.} =
|
||||||
|
prc(f)
|
||||||
|
|
||||||
|
proc facetCall(prc: FacetProc) {.cps: Cont.} =
|
||||||
|
prc(activeFacet())
|
||||||
|
|
||||||
|
proc workCall(cb: Callback) {.cps: Cont.} =
|
||||||
|
cb()
|
||||||
|
|
||||||
|
proc god(facet): Actor =
|
||||||
|
## Return the parent of all actors associated with `facet`.
|
||||||
|
var facet = facet
|
||||||
|
while not facet.parent.isNil:
|
||||||
|
facet = facet.parent
|
||||||
|
facet.actor
|
||||||
|
|
||||||
|
proc newExternalTurn(facet; desc: Value) =
|
||||||
|
result = Turn(facet: facet)
|
||||||
|
let actor = facet.actor
|
||||||
|
when traceSyndicate:
|
||||||
|
result.desc.cause = TurnCause(orKind: TurnCauseKind.external))
|
||||||
|
result.desc.cause.external description = desc
|
||||||
|
|
||||||
|
proc runExternalTurn*(facet: Facet; cb: proc ()) =
|
||||||
|
echo "startExternalTurn"
|
||||||
|
startExternalTurn(facet)
|
||||||
|
facet.queueWork(whelp workCall(cb))
|
||||||
|
while run(facet.actor): discard
|
||||||
|
echo "runExternalTurn finished"
|
||||||
|
assert facet.actor.turn.work.len == 0
|
||||||
|
assert facet.actor.turn.actions.len == 0
|
||||||
|
|
||||||
|
proc newFacet(turn: Turn; actor; parent: Facet): Facet =
|
||||||
|
inc(actor.facetIdAllocator)
|
||||||
|
result = Facet(
|
||||||
|
actor: actor,
|
||||||
|
parent: parent,
|
||||||
|
id: actor.facetIdAllocator.toPreserves,
|
||||||
|
)
|
||||||
|
if not parent.isNil:
|
||||||
|
parent.children.add result
|
||||||
|
when traceSyndicate:
|
||||||
|
var act = ActionDescription(orKind: ActionDescriptionKind.facetstart)
|
||||||
|
collectPath(act.facetstart.path, result)
|
||||||
|
actor.turn.desc.actions.add act
|
||||||
|
|
||||||
|
proc newActor(parent: Facet; name: string): Actor =
|
||||||
|
result = Actor(id: name.toPreserves)
|
||||||
|
result.root = newFacet(result, parent)
|
||||||
|
when traceSyndicate:
|
||||||
|
if parent.isNil:
|
||||||
|
let path = getEnv("SYNDICATE_TRACE_FILE", "")
|
||||||
|
case path
|
||||||
|
of "": discard
|
||||||
|
of "-": result.traceStream = newFileStream(stderr)
|
||||||
|
else: result.traceStream = openFileStream(path, fmWrite)
|
||||||
|
else:
|
||||||
|
result.traceStream = parent.actor.traceStream
|
||||||
|
|
||||||
|
proc spawnActor*(parent: Facet; name: string; bootProc: FacetProc): Actor {.discardable.} =
|
||||||
|
## Spawn a new `Actor` at `parent`.
|
||||||
|
result = newActor(parent, name)
|
||||||
|
var turn = newExternalTurn(result.root, "bootActor".toPreserves)
|
||||||
|
turn.queueWork(whelp facetCall(bootProc))
|
||||||
|
queueTurn(turn)
|
||||||
|
|
||||||
|
proc bootActor*(name: string; bootProc: FacetProc): Actor =
|
||||||
|
## Boot a new `Actor`.
|
||||||
|
result = newActor(nil, name)
|
||||||
|
var turn = newExternalTurn(result.root, "bootActor".toPreserves)
|
||||||
|
turn.queueWork(whelp facetCall(bootProc))
|
||||||
|
queueTurn(turn)
|
||||||
|
|
||||||
|
proc run(turn: sink Turn) =
|
||||||
|
try:
|
||||||
|
while turn.work.len > 0:
|
||||||
|
discard trampoline turn.work.pop()
|
||||||
|
except CatchableError as err:
|
||||||
|
terminate turn.actor(err)
|
||||||
|
return
|
||||||
|
var i: int
|
||||||
|
while i < turn.actions.len:
|
||||||
|
discard trampoline turn.actions[i]
|
||||||
|
|
||||||
|
proc runTurn*() =
|
||||||
|
## Run one turn.
|
||||||
|
turnQueue.pop().run()
|
|
@ -0,0 +1,113 @@
|
||||||
|
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||||
|
# SPDX-License-Identifier: Unlicense
|
||||||
|
|
||||||
|
import std/[asyncdispatch, times, oserrors, posix, sets, times, epoll]
|
||||||
|
import pkg/sys/[handles, ioqueue]
|
||||||
|
import preserves
|
||||||
|
import ../[bags, syndicate], ../protocols/[timer, dataspace]
|
||||||
|
|
||||||
|
export timer
|
||||||
|
|
||||||
|
type
|
||||||
|
Observe = dataspace.Observe
|
||||||
|
Time = posix.Time
|
||||||
|
|
||||||
|
when defined(linux):
|
||||||
|
import std/[epoll, posix]
|
||||||
|
|
||||||
|
{.pragma: timerfd, importc, header: "<sys/timerfd.h>".}
|
||||||
|
|
||||||
|
proc timerfd_create(clock_id: ClockId, flags: cint): cint {.timerfd.}
|
||||||
|
proc timerfd_settime(ufd: cint, flags: cint,
|
||||||
|
utmr: var Itimerspec, otmr: var Itimerspec): cint {.timerfd.}
|
||||||
|
proc timerfd_gettime(ufd: cint, curr: var Itimerspec): cint {.timerfd.}
|
||||||
|
|
||||||
|
var
|
||||||
|
TFD_NONBLOCK {.timerfd.}: cint
|
||||||
|
TFD_CLOEXEC {.timerfd.}: cint
|
||||||
|
TFD_TIMER_ABSTIME {.timerfd.}: cint
|
||||||
|
|
||||||
|
func toFloat(ts: Timespec): float =
|
||||||
|
ts.tv_sec.float + ts.tv_nsec.float / 1_000_000_000
|
||||||
|
|
||||||
|
func toTimespec(f: float): Timespec =
|
||||||
|
result.tv_sec = Time(f)
|
||||||
|
result.tv_nsec = clong(uint64(f * 1_000_000_000) mod 1_000_000_000)
|
||||||
|
|
||||||
|
proc `<`(a, b: Timespec): bool =
|
||||||
|
a.tv_sec.clong <= b.tv_sec.clong and a.tv_nsec <= b.tv_nsec
|
||||||
|
|
||||||
|
proc `+`(a, b: Timespec): Timespec =
|
||||||
|
result.tv_sec = Time a.tv_sec.clong + b.tv_sec.clong
|
||||||
|
result.tv_nsec = a.tv_nsec + b.tv_nsec
|
||||||
|
|
||||||
|
proc clock_realtime: Timespec =
|
||||||
|
if clock_gettime(CLOCK_REALTIME, result) < 0:
|
||||||
|
raiseOSError(osLastError(), "clock_gettime")
|
||||||
|
|
||||||
|
proc nsec(epoch: float): clong =
|
||||||
|
clong(uint64(epoch * 1_000_000_000) mod 1_000_000_000'u64)
|
||||||
|
|
||||||
|
type
|
||||||
|
TimerDriver = ref object
|
||||||
|
facet: Facet
|
||||||
|
target: Cap
|
||||||
|
deadlines: Bag[float]
|
||||||
|
timers: HashSet[cint]
|
||||||
|
|
||||||
|
proc spawnTimerDriver(facet: Facet; cap: Cap): TimerDriver =
|
||||||
|
let driver = TimerDriver(facet: facet, target: cap)
|
||||||
|
facet.onStop do ():
|
||||||
|
for fd in driver.timers:
|
||||||
|
unregister(FD fd)
|
||||||
|
discard close(fd)
|
||||||
|
driver
|
||||||
|
|
||||||
|
proc earliestFloat(driver: TimerDriver): float =
|
||||||
|
assert driver.deadlines.len > 0
|
||||||
|
result = high float
|
||||||
|
for deadline in driver.deadlines:
|
||||||
|
if deadline < result:
|
||||||
|
result = deadline
|
||||||
|
|
||||||
|
proc await(driver: TimerDriver; deadline: float) {.asyncio.} =
|
||||||
|
let fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK or TFD_CLOEXEC)
|
||||||
|
if fd < 0:
|
||||||
|
raiseOSError(osLastError(), "failed to acquire timer descriptor")
|
||||||
|
var
|
||||||
|
old: Itimerspec
|
||||||
|
its = Itimerspec(it_value: deadline.toTimeSpec)
|
||||||
|
if timerfd_settime(fd, TFD_TIMER_ABSTIME, its, old) < 0:
|
||||||
|
raiseOSError(osLastError(), "failed to set timeout")
|
||||||
|
driver.timers.incl(fd)
|
||||||
|
while clock_realtime().toFloat() < deadline:
|
||||||
|
wait(FD fd, Read)
|
||||||
|
if deadline in driver.deadlines:
|
||||||
|
proc pub() =
|
||||||
|
echo "publishing later-than"
|
||||||
|
discard publish(driver.target, LaterThan(seconds: deadline))
|
||||||
|
runExternalTurn(driver.facet, pub)
|
||||||
|
discard close(fd)
|
||||||
|
driver.timers.excl(fd)
|
||||||
|
|
||||||
|
proc spawnTimerActor*(ds: Cap): Actor {.discardable.} =
|
||||||
|
## Spawn a timer actor that responds to
|
||||||
|
## dataspace observations of timeouts on `ds`.
|
||||||
|
spawnActor(ds.relay, "timers") do (facet: Facet):
|
||||||
|
let driver = spawnTimerDriver(facet, ds)
|
||||||
|
let pat = inject(grab Observe(pattern: dropType LaterThan), {0: grabLit()})
|
||||||
|
during(ds, pat) do (deadline: float):
|
||||||
|
echo "timer actor sees observation of ", LaterThan(seconds: deadline)
|
||||||
|
if change(driver.deadlines, deadline, +1) == cdAbsentToPresent:
|
||||||
|
discard trampoline(whelp await(driver, deadline))
|
||||||
|
do:
|
||||||
|
discard change(driver.deadlines, deadline, -1, clamp = true)
|
||||||
|
|
||||||
|
proc after*(ds: Cap; dur: Duration; cb: proc () {.closure.}) =
|
||||||
|
## Execute `cb` after some duration of time.
|
||||||
|
var
|
||||||
|
later = clock_realtime().toFloat() +
|
||||||
|
dur.inMilliseconds.float / 1_000.0
|
||||||
|
pat = ?LaterThan(seconds: later)
|
||||||
|
onPublish(ds, pat):
|
||||||
|
cb()
|
|
@ -46,3 +46,5 @@ proc `$`*(bag: Bag): string =
|
||||||
if result.len > 1: result.add ' '
|
if result.len > 1: result.add ' '
|
||||||
result.add $x
|
result.add $x
|
||||||
result.add '}'
|
result.add '}'
|
||||||
|
|
||||||
|
export tables.contains, tables.del, tables.len
|
|
@ -0,0 +1,52 @@
|
||||||
|
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||||
|
# SPDX-License-Identifier: Unlicense
|
||||||
|
|
||||||
|
import std/[hashes, options, tables]
|
||||||
|
import pkg/cps
|
||||||
|
import preserves
|
||||||
|
import ./[actors, patterns, skeletons]
|
||||||
|
|
||||||
|
from ./protocols/protocol import Handle
|
||||||
|
from ./protocols/dataspace import Observe
|
||||||
|
|
||||||
|
type
|
||||||
|
Dataspace {.final.} = ref object of Entity
|
||||||
|
index: Index
|
||||||
|
handleMap: Table[Handle, Value]
|
||||||
|
|
||||||
|
proc dsPublish(e: Entity; v: Value; h: Handle) {.cps: Cont.} =
|
||||||
|
var ds = Dataspace(e)
|
||||||
|
if ds.index.add(v):
|
||||||
|
var obs = v.preservesTo(Observe)
|
||||||
|
if obs.isSome and obs.get.observer of Cap:
|
||||||
|
ds.index.add(obs.get.pattern, Cap(obs.get.observer))
|
||||||
|
ds.handleMap[h] = v
|
||||||
|
|
||||||
|
proc dsRetract(e: Entity; h: Handle) {.cps: Cont.} =
|
||||||
|
var ds = Dataspace(e)
|
||||||
|
var v = ds.handleMap[h]
|
||||||
|
if ds.index.remove(v):
|
||||||
|
ds.handleMap.del h
|
||||||
|
var obs = v.preservesTo(Observe)
|
||||||
|
if obs.isSome and obs.get.observer of Cap:
|
||||||
|
ds.index.remove(obs.get.pattern, Cap(obs.get.observer))
|
||||||
|
|
||||||
|
proc dsMessage(e: Entity; v: Value) {.cps: Cont.} =
|
||||||
|
var ds = Dataspace(e)
|
||||||
|
ds.index.deliverMessage(v)
|
||||||
|
|
||||||
|
proc newDataspace*(f: Facet): Cap =
|
||||||
|
var ds = Dataspace(
|
||||||
|
index: initIndex(),
|
||||||
|
).setActions(
|
||||||
|
publish = whelp dsPublish,
|
||||||
|
retract = whelp dsRetract,
|
||||||
|
message = whelp dsMessage,
|
||||||
|
)
|
||||||
|
newCap(f, ds)
|
||||||
|
|
||||||
|
proc observe*(cap: Cap; pat: Pattern; peer: Cap): Handle =
|
||||||
|
publish(cap, Observe(pattern: pat, observer: peer))
|
||||||
|
|
||||||
|
proc observe*(cap: Cap; pat: Pattern; e: Entity): Handle =
|
||||||
|
observe(cap, pat, newCap(cap.relay, e))
|
|
@ -0,0 +1,49 @@
|
||||||
|
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||||
|
# SPDX-License-Identifier: Unlicense
|
||||||
|
|
||||||
|
import std/[hashes, tables]
|
||||||
|
import preserves
|
||||||
|
import pkg/cps
|
||||||
|
import ./[actors, patterns]
|
||||||
|
|
||||||
|
type
|
||||||
|
DuringProc* = proc (f: Facet; a: Value; h: Handle): FacetProc {.closure.}
|
||||||
|
DuringActionKind = enum null, dead, act
|
||||||
|
DuringAction = object
|
||||||
|
case kind: DuringActionKind
|
||||||
|
of null, dead: discard
|
||||||
|
of act:
|
||||||
|
retractProc: FacetProc
|
||||||
|
DuringEntity {.final.}= ref object of Entity
|
||||||
|
publishProc: DuringProc
|
||||||
|
assertionMap: Table[Handle, DuringAction]
|
||||||
|
|
||||||
|
proc duringPublish(e: Entity; v: Value; h: Handle) {.cps: Cont.} =
|
||||||
|
var de = DuringEntity(e)
|
||||||
|
let g = de.assertionMap.getOrDefault h
|
||||||
|
case g.kind
|
||||||
|
of null, dead:
|
||||||
|
var cb = de.publishProc(e.facet, v, h)
|
||||||
|
de.assertionMap[h] = DuringAction(kind: act, retractProc: cb)
|
||||||
|
of act:
|
||||||
|
raiseAssert("during: duplicate handle in publish: " & $h)
|
||||||
|
|
||||||
|
proc duringRetract(e: Entity; h: Handle) {.cps: Cont.} =
|
||||||
|
var de = DuringEntity(e)
|
||||||
|
let g = de.assertionMap.getOrDefault h
|
||||||
|
case g.kind
|
||||||
|
of null:
|
||||||
|
de.assertionMap[h] = DuringAction(kind: dead)
|
||||||
|
of dead:
|
||||||
|
raiseAssert("during: duplicate handle in retract: " & $h)
|
||||||
|
of act:
|
||||||
|
de.assertionMap.del h
|
||||||
|
if not g.retractProc.isNil:
|
||||||
|
g.retractProc(de.facet)
|
||||||
|
|
||||||
|
proc during*(cb: DuringProc): Entity =
|
||||||
|
## TODO: this doesn't follow Nim idom well.
|
||||||
|
DuringEntity(publishProc: cb).setActions(
|
||||||
|
publish = whelp duringPublish,
|
||||||
|
retract = whelp duringRetract,
|
||||||
|
)
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,9 @@
|
||||||
|
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||||
|
# SPDX-License-Identifier: Unlicense
|
||||||
|
|
||||||
|
import preserves
|
||||||
|
import ./actors, ./patterns, ./protocols/dataspace
|
||||||
|
|
||||||
|
proc observe*(turn: var Turn; ds: Cap; pat: Pattern; e: Entity): Cap {.discardable.} =
|
||||||
|
result = newCap(turn, e)
|
||||||
|
publish(turn, ds, Observe(pattern: pat, observer: result))
|
|
@ -1,10 +1,12 @@
|
||||||
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||||
# SPDX-License-Identifier: Unlicense
|
# SPDX-License-Identifier: Unlicense
|
||||||
|
|
||||||
import std/[asyncdispatch, options, tables]
|
import std/[options, tables]
|
||||||
from std/os import getEnv, `/`
|
from std/os import getEnv, `/`
|
||||||
|
import pkg/sys/[ioqueue, sockets]
|
||||||
|
|
||||||
import preserves
|
import preserves
|
||||||
import ../syndicate, /capabilities, ./durings, ./membranes, ./protocols/[gatekeeper, protocol, sturdy, transportAddress]
|
import ./syndicate, /capabilities, ./durings, ./membranes, ./protocols/[gatekeeper, protocol, sturdy, transportAddress]
|
||||||
|
|
||||||
when defined(traceSyndicate):
|
when defined(traceSyndicate):
|
||||||
when defined(posix):
|
when defined(posix):
|
||||||
|
@ -15,20 +17,18 @@ else:
|
||||||
template trace(args: varargs[untyped]): untyped = discard
|
template trace(args: varargs[untyped]): untyped = discard
|
||||||
|
|
||||||
export `$`
|
export `$`
|
||||||
|
|
||||||
type
|
|
||||||
Oid = sturdy.Oid
|
|
||||||
|
|
||||||
export Stdio, Tcp, WebSocket, Unix
|
export Stdio, Tcp, WebSocket, Unix
|
||||||
|
|
||||||
type
|
type
|
||||||
Assertion = Value
|
Assertion = Value
|
||||||
WireRef = sturdy.WireRef
|
Event = protocol.Event
|
||||||
Turn = syndicate.Turn
|
|
||||||
Handle = actors.Handle
|
Handle = actors.Handle
|
||||||
|
Oid = sturdy.Oid
|
||||||
|
Turn = syndicate.Turn
|
||||||
|
WireRef = sturdy.WireRef
|
||||||
|
|
||||||
PacketWriter = proc (turn: var Turn; buf: seq[byte]) {.closure, gcsafe.}
|
PacketWriter = proc (turn: Turn; buf: seq[byte]) {.closure, gcsafe.}
|
||||||
RelaySetup = proc (turn: var Turn; relay: Relay) {.closure, gcsafe.}
|
RelaySetup = proc (turn: Turn; relay: Relay) {.closure, gcsafe.}
|
||||||
|
|
||||||
Relay* = ref object
|
Relay* = ref object
|
||||||
facet: Facet
|
facet: Facet
|
||||||
|
@ -57,20 +57,20 @@ type
|
||||||
proc releaseCapOut(r: Relay; e: WireSymbol) =
|
proc releaseCapOut(r: Relay; e: WireSymbol) =
|
||||||
r.exported.drop e
|
r.exported.drop e
|
||||||
|
|
||||||
method publish(spe: SyncPeerEntity; t: var Turn; a: AssertionRef; h: Handle) =
|
method publish(spe: SyncPeerEntity; t: Turn; a: AssertionRef; h: Handle) =
|
||||||
spe.handleMap[h] = publish(t, spe.peer, a.value)
|
spe.handleMap[h] = publish(t, spe.peer, a.value)
|
||||||
|
|
||||||
method retract(se: SyncPeerEntity; t: var Turn; h: Handle) =
|
method retract(se: SyncPeerEntity; t: Turn; h: Handle) =
|
||||||
var other: Handle
|
var other: Handle
|
||||||
if se.handleMap.pop(h, other):
|
if se.handleMap.pop(h, other):
|
||||||
retract(t, other)
|
retract(t, other)
|
||||||
|
|
||||||
method message(se: SyncPeerEntity; t: var Turn; a: AssertionRef) =
|
method message(se: SyncPeerEntity; t: Turn; a: AssertionRef) =
|
||||||
if not se.e.isNil:
|
if not se.e.isNil:
|
||||||
se.relay.releaseCapOut(se.e)
|
se.relay.releaseCapOut(se.e)
|
||||||
message(t, se.peer, a.value)
|
message(t, se.peer, a.value)
|
||||||
|
|
||||||
method sync(se: SyncPeerEntity; t: var Turn; peer: Cap) =
|
method sync(se: SyncPeerEntity; t: Turn; peer: Cap) =
|
||||||
sync(t, se.peer, peer)
|
sync(t, se.peer, peer)
|
||||||
|
|
||||||
proc newSyncPeerEntity(r: Relay; p: Cap): SyncPeerEntity =
|
proc newSyncPeerEntity(r: Relay; p: Cap): SyncPeerEntity =
|
||||||
|
@ -107,42 +107,46 @@ proc deregister(relay: Relay; h: Handle) =
|
||||||
if relay.outboundAssertions.pop(h, outbound):
|
if relay.outboundAssertions.pop(h, outbound):
|
||||||
for e in outbound: releaseCapOut(relay, e)
|
for e in outbound: releaseCapOut(relay, e)
|
||||||
|
|
||||||
proc send(relay: Relay; turn: var Turn; rOid: protocol.Oid; m: Event) =
|
proc send(relay: Relay; turn: Turn; rOid: protocol.Oid; m: Event) =
|
||||||
if relay.pendingTurn.len == 0:
|
if relay.pendingTurn.len > 0:
|
||||||
|
stderr.writeLine "relay has pending turn events"
|
||||||
# If the pending queue is empty then schedule a packet
|
# If the pending queue is empty then schedule a packet
|
||||||
# to be sent after pending I/O is processed.
|
# to be sent after pending I/O is processed.
|
||||||
callSoon do ():
|
#[
|
||||||
relay.facet.run do (turn: var Turn):
|
assert turn.finalizer.isNil
|
||||||
|
turn.finalizer = proc () =
|
||||||
|
relay.facet.run do (turn: Turn):
|
||||||
var pkt = Packet(
|
var pkt = Packet(
|
||||||
orKind: PacketKind.Turn,
|
orKind: PacketKind.Turn,
|
||||||
turn: move relay.pendingTurn)
|
turn: move relay.pendingTurn)
|
||||||
trace "C: ", pkt
|
trace "C: ", pkt
|
||||||
relay.packetWriter(turn, encode pkt)
|
relay.packetWriter(turn, encode pkt)
|
||||||
|
]#
|
||||||
relay.pendingTurn.add TurnEvent(oid: rOid, event: m)
|
relay.pendingTurn.add TurnEvent(oid: rOid, event: m)
|
||||||
|
|
||||||
proc send(re: RelayEntity; turn: var Turn; ev: Event) =
|
proc send(re: RelayEntity; turn: Turn; ev: Event) =
|
||||||
send(re.relay, turn, protocol.Oid re.oid, ev)
|
send(re.relay, turn, protocol.Oid re.oid, ev)
|
||||||
|
|
||||||
method publish(re: RelayEntity; t: var Turn; a: AssertionRef; h: Handle) {.gcsafe.} =
|
method publish(re: RelayEntity; t: Turn; a: AssertionRef; h: Handle) {.gcsafe.} =
|
||||||
re.send(t, Event(
|
re.send(t, Event(
|
||||||
orKind: EventKind.Assert,
|
orKind: EventKind.Assert,
|
||||||
`assert`: protocol.Assert(
|
`assert`: protocol.Assert(
|
||||||
assertion: re.relay.register(a.value, h).rewritten,
|
assertion: re.relay.register(a.value, h).rewritten,
|
||||||
handle: h)))
|
handle: h)))
|
||||||
|
|
||||||
method retract(re: RelayEntity; t: var Turn; h: Handle) {.gcsafe.} =
|
method retract(re: RelayEntity; t: Turn; h: Handle) {.gcsafe.} =
|
||||||
re.relay.deregister h
|
re.relay.deregister h
|
||||||
re.send(t, Event(
|
re.send(t, Event(
|
||||||
orKind: EventKind.Retract,
|
orKind: EventKind.Retract,
|
||||||
retract: Retract(handle: h)))
|
retract: Retract(handle: h)))
|
||||||
|
|
||||||
method message(re: RelayEntity; turn: var Turn; msg: AssertionRef) {.gcsafe.} =
|
method message(re: RelayEntity; turn: Turn; msg: AssertionRef) {.gcsafe.} =
|
||||||
var (value, exported) = rewriteOut(re.relay, msg.value)
|
var (value, exported) = rewriteOut(re.relay, msg.value)
|
||||||
assert(len(exported) == 0, "cannot send a reference in a message")
|
assert(len(exported) == 0, "cannot send a reference in a message")
|
||||||
if len(exported) == 0:
|
if len(exported) == 0:
|
||||||
re.send(turn, Event(orKind: EventKind.Message, message: Message(body: value)))
|
re.send(turn, Event(orKind: EventKind.Message, message: Message(body: value)))
|
||||||
|
|
||||||
method sync(re: RelayEntity; turn: var Turn; peer: Cap) {.gcsafe.} =
|
method sync(re: RelayEntity; turn: Turn; peer: Cap) {.gcsafe.} =
|
||||||
var
|
var
|
||||||
peerEntity = newSyncPeerEntity(re.relay, peer)
|
peerEntity = newSyncPeerEntity(re.relay, peer)
|
||||||
exported: seq[WireSymbol]
|
exported: seq[WireSymbol]
|
||||||
|
@ -196,7 +200,7 @@ proc rewriteIn(relay; facet; v: Value):
|
||||||
|
|
||||||
proc close(r: Relay) = discard
|
proc close(r: Relay) = discard
|
||||||
|
|
||||||
proc dispatch(relay: Relay; turn: var Turn; cap: Cap; event: Event) {.gcsafe.} =
|
proc dispatch(relay: Relay; turn: Turn; cap: Cap; event: Event) {.gcsafe.} =
|
||||||
case event.orKind
|
case event.orKind
|
||||||
of EventKind.Assert:
|
of EventKind.Assert:
|
||||||
let (a, imported) = rewriteIn(relay, turn.facet, event.assert.assertion)
|
let (a, imported) = rewriteIn(relay, turn.facet, event.assert.assertion)
|
||||||
|
@ -219,14 +223,14 @@ proc dispatch(relay: Relay; turn: var Turn; cap: Cap; event: Event) {.gcsafe.} =
|
||||||
#[
|
#[
|
||||||
var imported: seq[WireSymbol]
|
var imported: seq[WireSymbol]
|
||||||
let k = relay.rewriteCapIn(turn, evenr.sync.peer, imported)
|
let k = relay.rewriteCapIn(turn, evenr.sync.peer, imported)
|
||||||
turn.sync(cap) do (turn: var Turn):
|
turn.sync(cap) do (turn: Turn):
|
||||||
turn.message(k, true)
|
turn.message(k, true)
|
||||||
for e in imported: relay.imported.del e
|
for e in imported: relay.imported.del e
|
||||||
]#
|
]#
|
||||||
|
|
||||||
proc dispatch(relay: Relay; v: Value) {.gcsafe.} =
|
proc dispatch(relay: Relay; v: Value) {.gcsafe.} =
|
||||||
trace "S: ", v
|
trace "S: ", v
|
||||||
run(relay.facet) do (t: var Turn):
|
run(relay.facet) do (t: Turn):
|
||||||
var pkt: Packet
|
var pkt: Packet
|
||||||
if pkt.fromPreserves(v):
|
if pkt.fromPreserves(v):
|
||||||
case pkt.orKind
|
case pkt.orKind
|
||||||
|
@ -264,8 +268,8 @@ type
|
||||||
initialCap*: Cap
|
initialCap*: Cap
|
||||||
nextLocalOid*: Option[Oid]
|
nextLocalOid*: Option[Oid]
|
||||||
|
|
||||||
proc spawnRelay(name: string; turn: var Turn; opts: RelayActorOptions; setup: RelaySetup) =
|
proc spawnRelay(name: string; turn: Turn; opts: RelayActorOptions; setup: RelaySetup) =
|
||||||
spawn(name, turn) do (turn: var Turn):
|
spawn(name, turn) do (turn: Turn):
|
||||||
let relay = Relay(
|
let relay = Relay(
|
||||||
facet: turn.facet,
|
facet: turn.facet,
|
||||||
packetWriter: opts.packetWriter,
|
packetWriter: opts.packetWriter,
|
||||||
|
@ -300,20 +304,21 @@ proc accepted(cap: Cap): Resolved =
|
||||||
|
|
||||||
when defined(posix):
|
when defined(posix):
|
||||||
|
|
||||||
|
#[
|
||||||
import std/asyncfile
|
import std/asyncfile
|
||||||
export Unix
|
export Unix
|
||||||
|
|
||||||
type StdioControlEntity = ref object of Entity
|
type StdioControlEntity = ref object of Entity
|
||||||
stdin: AsyncFile
|
stdin: AsyncFile
|
||||||
|
|
||||||
method message(entity: StdioControlEntity; turn: var Turn; ass: AssertionRef) =
|
method message(entity: StdioControlEntity; turn: Turn; ass: AssertionRef) =
|
||||||
if ass.value.preservesTo(ForceDisconnect).isSome:
|
if ass.value.preservesTo(ForceDisconnect).isSome:
|
||||||
close(entity.stdin)
|
close(entity.stdin)
|
||||||
close(stdout)
|
close(stdout)
|
||||||
|
|
||||||
proc connectTransport(turn: var Turn; ds: Cap; ta: transportAddress.Stdio) =
|
proc connectTransport(turn: Turn; ds: Cap; ta: transportAddress.Stdio) =
|
||||||
## Connect to an external dataspace over stdio.
|
## Connect to an external dataspace over stdio.
|
||||||
proc stdoutWriter(turn: var Turn; buf: seq[byte]) =
|
proc stdoutWriter(turn: Turn; buf: seq[byte]) =
|
||||||
## Blocking write to stdout.
|
## Blocking write to stdout.
|
||||||
let n = writeBytes(stdout, buf, 0, buf.len)
|
let n = writeBytes(stdout, buf, 0, buf.len)
|
||||||
flushFile(stdout)
|
flushFile(stdout)
|
||||||
|
@ -324,7 +329,7 @@ when defined(posix):
|
||||||
initialCap: ds,
|
initialCap: ds,
|
||||||
initialOid: 0.Oid.some,
|
initialOid: 0.Oid.some,
|
||||||
)
|
)
|
||||||
spawnRelay("stdio", turn, opts) do (turn: var Turn; relay: Relay):
|
spawnRelay("stdio", turn, opts) do (turn: Turn; relay: Relay):
|
||||||
let
|
let
|
||||||
facet = turn.facet
|
facet = turn.facet
|
||||||
asyncStdin = openAsync("/dev/stdin") # this is universal now?
|
asyncStdin = openAsync("/dev/stdin") # this is universal now?
|
||||||
|
@ -338,13 +343,13 @@ when defined(posix):
|
||||||
if not pktFut.failed:
|
if not pktFut.failed:
|
||||||
var buf = pktFut.read
|
var buf = pktFut.read
|
||||||
if buf.len == 0:
|
if buf.len == 0:
|
||||||
run(facet) do (turn: var Turn): stopActor(turn)
|
run(facet) do (turn: Turn): stopActor(turn)
|
||||||
else:
|
else:
|
||||||
relay.recv(cast[seq[byte]](buf))
|
relay.recv(cast[seq[byte]](buf))
|
||||||
asyncStdin.read(stdinReadSize).addCallback(readCb)
|
asyncStdin.read(stdinReadSize).addCallback(readCb)
|
||||||
asyncStdin.read(stdinReadSize).addCallback(readCb)
|
asyncStdin.read(stdinReadSize).addCallback(readCb)
|
||||||
|
|
||||||
proc connectStdio*(turn: var Turn; ds: Cap) =
|
proc connectStdio*(turn: Turn; ds: Cap) =
|
||||||
## Connect to an external dataspace over stdin and stdout.
|
## Connect to an external dataspace over stdin and stdout.
|
||||||
connectTransport(turn, ds, transportAddress.Stdio())
|
connectTransport(turn, ds, transportAddress.Stdio())
|
||||||
|
|
||||||
|
@ -354,24 +359,24 @@ when defined(posix):
|
||||||
type SocketControlEntity = ref object of Entity
|
type SocketControlEntity = ref object of Entity
|
||||||
socket: AsyncSocket
|
socket: AsyncSocket
|
||||||
|
|
||||||
method message(entity: SocketControlEntity; turn: var Turn; ass: AssertionRef) =
|
method message(entity: SocketControlEntity; turn: Turn; ass: AssertionRef) =
|
||||||
if ass.value.preservesTo(ForceDisconnect).isSome:
|
if ass.value.preservesTo(ForceDisconnect).isSome:
|
||||||
close(entity.socket)
|
close(entity.socket)
|
||||||
|
|
||||||
type ShutdownEntity* = ref object of Entity
|
type ShutdownEntity* = ref object of Entity
|
||||||
method retract(e: ShutdownEntity; turn: var Turn; h: Handle) =
|
method retract(e: ShutdownEntity; turn: Turn; h: Handle) =
|
||||||
stopActor(turn)
|
stopActor(turn)
|
||||||
|
|
||||||
proc connect(turn: var Turn; ds: Cap; transAddr: Value; socket: AsyncSocket) =
|
proc connect(turn: Turn; ds: Cap; transAddr: Value; socket: AsyncSocket) =
|
||||||
proc socketWriter(turn: var Turn; buf: seq[byte]) =
|
proc socketWriter(turn: Turn; buf: seq[byte]) =
|
||||||
asyncCheck(turn, socket.send(cast[string](buf)))
|
asyncCheck(turn, socket.send(cast[string](buf)))
|
||||||
var ops = RelayActorOptions(
|
var ops = RelayActorOptions(
|
||||||
packetWriter: socketWriter,
|
packetWriter: socketWriter,
|
||||||
initialOid: 0.Oid.some,
|
initialOid: 0.Oid.some,
|
||||||
)
|
)
|
||||||
spawnRelay("socket", turn, ops) do (turn: var Turn; relay: Relay):
|
spawnRelay("socket", turn, ops) do (turn: Turn; relay: Relay):
|
||||||
let facet = turn.facet
|
let facet = turn.facet
|
||||||
facet.actor.atExit do (turn: var Turn): close(socket)
|
facet.actor.atExit do (turn: Turn): close(socket)
|
||||||
publish(turn, ds, TransportConnection(
|
publish(turn, ds, TransportConnection(
|
||||||
`addr`: transAddr,
|
`addr`: transAddr,
|
||||||
control: SocketControlEntity(socket: socket).newCap(turn),
|
control: SocketControlEntity(socket: socket).newCap(turn),
|
||||||
|
@ -380,17 +385,17 @@ when defined(posix):
|
||||||
const recvSize = 0x4000
|
const recvSize = 0x4000
|
||||||
proc recvCb(pktFut: Future[string]) {.gcsafe.} =
|
proc recvCb(pktFut: Future[string]) {.gcsafe.} =
|
||||||
if pktFut.failed or pktFut.read.len == 0:
|
if pktFut.failed or pktFut.read.len == 0:
|
||||||
run(facet) do (turn: var Turn): stopActor(turn)
|
run(facet) do (turn: Turn): stopActor(turn)
|
||||||
else:
|
else:
|
||||||
relay.recv(cast[seq[byte]](pktFut.read))
|
relay.recv(cast[seq[byte]](pktFut.read))
|
||||||
if not socket.isClosed:
|
if not socket.isClosed:
|
||||||
socket.recv(recvSize).addCallback(recvCb)
|
socket.recv(recvSize).addCallback(recvCb)
|
||||||
socket.recv(recvSize).addCallback(recvCb)
|
socket.recv(recvSize).addCallback(recvCb)
|
||||||
|
|
||||||
proc connect(turn: var Turn; ds: Cap; ta: Value; socket: AsyncSocket; fut: Future[void]) =
|
proc connect(turn: Turn; ds: Cap; ta: Value; socket: AsyncSocket; fut: Future[void]) =
|
||||||
let facet = turn.facet
|
let facet = turn.facet
|
||||||
fut.addCallback do ():
|
fut.addCallback do ():
|
||||||
run(facet) do (turn: var Turn):
|
run(facet) do (turn: Turn):
|
||||||
if fut.failed:
|
if fut.failed:
|
||||||
var ass = TransportConnection(
|
var ass = TransportConnection(
|
||||||
`addr`: ta,
|
`addr`: ta,
|
||||||
|
@ -401,7 +406,7 @@ when defined(posix):
|
||||||
else:
|
else:
|
||||||
connect(turn, ds, ta, socket)
|
connect(turn, ds, ta, socket)
|
||||||
|
|
||||||
proc connectTransport(turn: var Turn; ds: Cap; ta: transportAddress.Tcp) =
|
proc connectTransport(turn: Turn; ds: Cap; ta: transportAddress.Tcp) =
|
||||||
let
|
let
|
||||||
facet = turn.facet
|
facet = turn.facet
|
||||||
socket = newAsyncSocket(
|
socket = newAsyncSocket(
|
||||||
|
@ -411,8 +416,14 @@ when defined(posix):
|
||||||
buffered = false,
|
buffered = false,
|
||||||
)
|
)
|
||||||
connect(turn, ds, ta.toPreserves, socket, connect(socket, ta.host, Port ta.port))
|
connect(turn, ds, ta.toPreserves, socket, connect(socket, ta.host, Port ta.port))
|
||||||
|
]#
|
||||||
|
|
||||||
proc connectTransport(turn: var Turn; ds: Cap; ta: transportAddress.Unix) =
|
proc connectTransport(turn: Turn; ds: Cap; ta: transportAddress.Tcp) {.asyncio.} =
|
||||||
|
var conn = connectTcpAsync(ta.host, Port ta.port)
|
||||||
|
# connect(turn, ds, ta.toPreserves, conn)
|
||||||
|
|
||||||
|
#[
|
||||||
|
proc connectTransport(turn: Turn; ds: Cap; ta: transportAddress.Unix) =
|
||||||
## Relay a dataspace over a UNIX socket.
|
## Relay a dataspace over a UNIX socket.
|
||||||
let socket = newAsyncSocket(
|
let socket = newAsyncSocket(
|
||||||
domain = AF_UNIX,
|
domain = AF_UNIX,
|
||||||
|
@ -420,8 +431,9 @@ when defined(posix):
|
||||||
protocol = cast[Protocol](0),
|
protocol = cast[Protocol](0),
|
||||||
buffered = false)
|
buffered = false)
|
||||||
connect(turn, ds, ta.toPreserves, socket, connectUnix(socket, ta.path))
|
connect(turn, ds, ta.toPreserves, socket, connectUnix(socket, ta.path))
|
||||||
|
]#
|
||||||
|
|
||||||
proc walk(turn: var Turn; ds, origin: Cap; route: Route; transOff, stepOff: int) {.gcsafe.} =
|
proc walk(turn: Turn; ds, origin: Cap; route: Route; transOff, stepOff: int) {.gcsafe.} =
|
||||||
if stepOff < route.pathSteps.len:
|
if stepOff < route.pathSteps.len:
|
||||||
let
|
let
|
||||||
step = route.pathSteps[stepOff]
|
step = route.pathSteps[stepOff]
|
||||||
|
@ -444,7 +456,7 @@ proc walk(turn: var Turn; ds, origin: Cap; route: Route; transOff, stepOff: int)
|
||||||
resolved: origin.accepted,
|
resolved: origin.accepted,
|
||||||
))
|
))
|
||||||
|
|
||||||
proc connectRoute(turn: var Turn; ds: Cap; route: Route; transOff: int) =
|
proc connectRoute(turn: Turn; ds: Cap; route: Route; transOff: int) =
|
||||||
let rejectPat = TransportConnection ?: {
|
let rejectPat = TransportConnection ?: {
|
||||||
0: ?route.transports[transOff],
|
0: ?route.transports[transOff],
|
||||||
2: ?:Rejected,
|
2: ?:Rejected,
|
||||||
|
@ -462,15 +474,15 @@ proc connectRoute(turn: var Turn; ds: Cap; route: Route; transOff: int) =
|
||||||
onPublish(turn, ds, acceptPat) do (origin: Cap):
|
onPublish(turn, ds, acceptPat) do (origin: Cap):
|
||||||
walk(turn, ds, origin, route, transOff, 0)
|
walk(turn, ds, origin, route, transOff, 0)
|
||||||
|
|
||||||
type StepCallback = proc (turn: var Turn; step: Value; origin, next: Cap) {.gcsafe.}
|
type StepCallback = proc (turn: Turn; step: Value; origin, next: Cap) {.gcsafe.}
|
||||||
|
|
||||||
proc spawnStepResolver(turn: var Turn; ds: Cap; stepType: Value; cb: StepCallback) =
|
proc spawnStepResolver(turn: Turn; ds: Cap; stepType: Value; cb: StepCallback) =
|
||||||
spawn($stepType & "-step", turn) do (turn: var Turn):
|
spawn($stepType & "-step", turn) do (turn: Turn):
|
||||||
let stepPat = grabRecord(stepType, grab())
|
let stepPat = grabRecord(stepType, grab())
|
||||||
let pat = ?Observe(pattern: ResolvedPathStep?:{1: stepPat}) ?? {0: grabLit(), 1: grab()}
|
let pat = ?Observe(pattern: ResolvedPathStep?:{1: stepPat}) ?? {0: grabLit(), 1: grab()}
|
||||||
during(turn, ds, pat) do (origin: Cap; stepDetail: Literal[Value]):
|
during(turn, ds, pat) do (origin: Cap; stepDetail: Literal[Value]):
|
||||||
let step = toRecord(stepType, stepDetail.value)
|
let step = toRecord(stepType, stepDetail.value)
|
||||||
proc duringCallback(turn: var Turn; ass: Value; h: Handle): TurnAction =
|
proc duringCallback(turn: Turn; ass: Value; h: Handle): TurnAction =
|
||||||
var res = ass.preservesTo Resolved
|
var res = ass.preservesTo Resolved
|
||||||
if res.isSome:
|
if res.isSome:
|
||||||
if res.get.orKind == ResolvedKind.accepted and
|
if res.get.orKind == ResolvedKind.accepted and
|
||||||
|
@ -479,43 +491,43 @@ proc spawnStepResolver(turn: var Turn; ds: Cap; stepType: Value; cb: StepCallbac
|
||||||
else:
|
else:
|
||||||
publish(turn, ds, ResolvedPathStep(
|
publish(turn, ds, ResolvedPathStep(
|
||||||
origin: origin, pathStep: step, resolved: res.get))
|
origin: origin, pathStep: step, resolved: res.get))
|
||||||
proc action(turn: var Turn) =
|
proc action(turn: Turn) =
|
||||||
stop(turn)
|
stop(turn)
|
||||||
result = action
|
result = action
|
||||||
publish(turn, origin, Resolve(
|
publish(turn, origin, Resolve(
|
||||||
step: step, observer: newCap(turn, during(duringCallback))))
|
step: step, observer: newCap(turn, during(duringCallback))))
|
||||||
|
|
||||||
proc spawnRelays*(turn: var Turn; ds: Cap) =
|
proc spawnRelays*(turn: Turn; ds: Cap) =
|
||||||
## Spawn actors that manage routes and appeasing gatekeepers.
|
## Spawn actors that manage routes and appeasing gatekeepers.
|
||||||
spawn("transport-connector", turn) do (turn: var Turn):
|
spawn("transport-connector", turn) do (turn: Turn):
|
||||||
let pat = ?Observe(pattern: !TransportConnection) ?? { 0: grab() }
|
let pat = ?Observe(pattern: !TransportConnection) ?? { 0: grab() }
|
||||||
# Use a generic pattern and type matching
|
# Use a generic pattern and type matching
|
||||||
# in the during handler because it is easy.
|
# in the during handler because it is easy.
|
||||||
|
|
||||||
let stdioPat = ?Observe(pattern: TransportConnection?:{0: ?:Stdio})
|
let stdioPat = ?Observe(pattern: TransportConnection?:{0: ?:Stdio})
|
||||||
during(turn, ds, stdioPat) do:
|
# during(turn, ds, stdioPat) do:
|
||||||
connectTransport(turn, ds, Stdio())
|
# connectTransport(turn, ds, Stdio())
|
||||||
|
|
||||||
# TODO: tcp pattern
|
# TODO: tcp pattern
|
||||||
during(turn, ds, pat) do (ta: Literal[transportAddress.Tcp]):
|
during(turn, ds, pat) do (ta: Literal[transportAddress.Tcp]):
|
||||||
connectTransport(turn, ds, ta.value)
|
connectTransport(turn, ds, ta.value)
|
||||||
|
|
||||||
# TODO: unix pattern
|
# TODO: unix pattern
|
||||||
during(turn, ds, pat) do (ta: Literal[transportAddress.Unix]):
|
# during(turn, ds, pat) do (ta: Literal[transportAddress.Unix]):
|
||||||
connectTransport(turn, ds, ta.value)
|
# connectTransport(turn, ds, ta.value)
|
||||||
|
|
||||||
spawn("path-resolver", turn) do (turn: var Turn):
|
spawn("path-resolver", turn) do (turn: Turn):
|
||||||
let pat = ?Observe(pattern: !ResolvePath) ?? {0: grab()}
|
let pat = ?Observe(pattern: !ResolvePath) ?? {0: grab()}
|
||||||
during(turn, ds, pat) do (route: Literal[Route]):
|
during(turn, ds, pat) do (route: Literal[Route]):
|
||||||
for i, transAddr in route.value.transports:
|
for i, transAddr in route.value.transports:
|
||||||
connectRoute(turn, ds, route.value, i)
|
connectRoute(turn, ds, route.value, i)
|
||||||
|
|
||||||
spawnStepResolver(turn, ds, "ref".toSymbol) do (
|
spawnStepResolver(turn, ds, "ref".toSymbol) do (
|
||||||
turn: var Turn, step: Value, origin: Cap, next: Cap):
|
turn: Turn, step: Value, origin: Cap, next: Cap):
|
||||||
publish(turn, ds, ResolvedPathStep(
|
publish(turn, ds, ResolvedPathStep(
|
||||||
origin: origin, pathStep: step, resolved: next.accepted))
|
origin: origin, pathStep: step, resolved: next.accepted))
|
||||||
|
|
||||||
type BootProc* = proc (turn: var Turn; ds: Cap) {.gcsafe.}
|
type BootProc* = proc (turn: Turn; ds: Cap) {.gcsafe.}
|
||||||
|
|
||||||
proc envRoute*: Route =
|
proc envRoute*: Route =
|
||||||
var text = getEnv("SYNDICATE_ROUTE")
|
var text = getEnv("SYNDICATE_ROUTE")
|
||||||
|
@ -528,7 +540,7 @@ proc envRoute*: Route =
|
||||||
if not result.fromPreserves(pr):
|
if not result.fromPreserves(pr):
|
||||||
raise newException(ValueError, "failed to parse $SYNDICATE_ROUTE " & $pr)
|
raise newException(ValueError, "failed to parse $SYNDICATE_ROUTE " & $pr)
|
||||||
|
|
||||||
proc resolve*(turn: var Turn; ds: Cap; route: Route; bootProc: BootProc) =
|
proc resolve*(turn: Turn; ds: Cap; route: Route; bootProc: BootProc) =
|
||||||
during(turn, ds, ResolvePath ?: {0: ?route, 3: ?:ResolvedAccepted}) do (dst: Cap):
|
during(turn, ds, ResolvePath ?: {0: ?route, 3: ?:ResolvedAccepted}) do (dst: Cap):
|
||||||
bootProc(turn, dst)
|
bootProc(turn, dst)
|
||||||
|
|
|
@ -45,8 +45,8 @@ type
|
||||||
AssertionCache = HashSet[Value]
|
AssertionCache = HashSet[Value]
|
||||||
|
|
||||||
ObserverGroup = ref object # Endpoints
|
ObserverGroup = ref object # Endpoints
|
||||||
cachedCaptures: Bag[Captures]
|
cachedCaptures: Bag[Value]
|
||||||
observers: Table[Cap, TableRef[Captures, Handle]]
|
observers: Table[Cap, TableRef[Value, Handle]]
|
||||||
|
|
||||||
Leaf = ref object
|
Leaf = ref object
|
||||||
cache: AssertionCache
|
cache: AssertionCache
|
||||||
|
@ -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: var Turn; group: ObserverGroup; vs: seq[Value]) {.gcsafe.}
|
ObserverProc = proc (group: ObserverGroup; vs: 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)
|
||||||
|
@ -76,7 +76,7 @@ proc getLeaves(cont: Continuation; constPaths: Paths): LeafMap =
|
||||||
cont.leafMap[constPaths] = result
|
cont.leafMap[constPaths] = result
|
||||||
assert not cont.isEmpty
|
assert not cont.isEmpty
|
||||||
for ass in cont.cache:
|
for ass in cont.cache:
|
||||||
let key = projectPaths(ass, constPaths)
|
var key = projectPaths(ass, constPaths)
|
||||||
if key.isSome:
|
if key.isSome:
|
||||||
var leaf = result.getOrDefault(get key)
|
var leaf = result.getOrDefault(get key)
|
||||||
if leaf.isNil:
|
if leaf.isNil:
|
||||||
|
@ -114,36 +114,36 @@ proc top(stack: TermStack): Value =
|
||||||
assert stack.len > 0
|
assert stack.len > 0
|
||||||
stack[stack.high]
|
stack[stack.high]
|
||||||
|
|
||||||
proc modify(node: Node; turn: var 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: var 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)
|
var constVals = projectPaths(outerValue, constPaths)
|
||||||
if constVals.isSome:
|
if constVals.isSome:
|
||||||
case event
|
case event
|
||||||
of addedEvent, messageEvent:
|
of addedEvent, messageEvent:
|
||||||
let leaf = constValMap.getLeaf(get constVals)
|
let leaf = constValMap.getLeaf(get constVals)
|
||||||
modLeaf(leaf, outerValue)
|
modLeaf(leaf, outerValue)
|
||||||
for capturePaths, observerGroup in leaf.observerGroups.pairs:
|
for capturePaths, observerGroup in leaf.observerGroups.pairs:
|
||||||
let captures = projectPaths(outerValue, capturePaths)
|
var captures = projectPaths(outerValue, capturePaths)
|
||||||
if captures.isSome:
|
if captures.isSome:
|
||||||
modObs(turn, observerGroup, get captures)
|
modObs(observerGroup, captures.get.toPreserves)
|
||||||
of removedEvent:
|
of removedEvent:
|
||||||
let leaf = constValMap.getOrDefault(get constVals)
|
let leaf = constValMap.getOrDefault(get constVals)
|
||||||
if not leaf.isNil:
|
if not leaf.isNil:
|
||||||
modLeaf(leaf, outerValue)
|
modLeaf(leaf, outerValue)
|
||||||
for capturePaths, observerGroup in leaf.observerGroups.pairs:
|
for capturePaths, observerGroup in leaf.observerGroups.pairs:
|
||||||
let captures = projectPaths(outerValue, capturePaths)
|
var captures = projectPaths(outerValue, capturePaths)
|
||||||
if captures.isSome:
|
if captures.isSome:
|
||||||
modObs(turn, observerGroup, get captures)
|
modObs(observerGroup, captures.get.toPreserves)
|
||||||
if leaf.isEmpty:
|
if leaf.isEmpty:
|
||||||
constValMap.del(get constVals)
|
constValMap.del(get constVals)
|
||||||
|
|
||||||
|
|
||||||
proc walk(node: Node; turn: var 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: var 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)
|
||||||
|
@ -223,11 +223,11 @@ proc getEndpoints(leaf: Leaf; capturePaths: Paths): ObserverGroup =
|
||||||
leaf.observerGroups[capturePaths] = result
|
leaf.observerGroups[capturePaths] = result
|
||||||
for term in leaf.cache:
|
for term in leaf.cache:
|
||||||
# leaf.cache would be empty if observers come before assertions
|
# leaf.cache would be empty if observers come before assertions
|
||||||
let captures = projectPaths(term, capturePaths)
|
var captures = projectPaths(term, capturePaths)
|
||||||
if captures.isSome:
|
if captures.isSome:
|
||||||
discard result.cachedCaptures.change(get captures, +1)
|
discard result.cachedCaptures.change(captures.get.toPreserves, +1)
|
||||||
|
|
||||||
proc add*(index: var Index; turn: var 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
|
||||||
|
@ -235,12 +235,12 @@ proc add*(index: var Index; turn: var Turn; pattern: Pattern; observer: Cap) =
|
||||||
leaf = constValMap.getLeaf(analysis.constValues)
|
leaf = constValMap.getLeaf(analysis.constValues)
|
||||||
endpoints = leaf.getEndpoints(analysis.capturePaths)
|
endpoints = leaf.getEndpoints(analysis.capturePaths)
|
||||||
# TODO if endpoints.cachedCaptures.len > 0:
|
# TODO if endpoints.cachedCaptures.len > 0:
|
||||||
var captureMap = newTable[seq[Value], Handle]()
|
var captureMap = newTable[Value, Handle]()
|
||||||
for capture in endpoints.cachedCaptures.items:
|
for capture in endpoints.cachedCaptures.items:
|
||||||
captureMap[capture] = publish(turn, observer, capture)
|
captureMap[capture] = publish(observer, capture.toPreserves)
|
||||||
endpoints.observers[observer] = captureMap
|
endpoints.observers[observer] = captureMap
|
||||||
|
|
||||||
proc remove*(index: var Index; turn: var 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
|
||||||
|
@ -250,9 +250,9 @@ proc remove*(index: var Index; turn: var Turn; pattern: Pattern; observer: Cap)
|
||||||
if not leaf.isNil:
|
if not leaf.isNil:
|
||||||
let endpoints = leaf.observerGroups.getOrDefault(analysis.capturePaths)
|
let endpoints = leaf.observerGroups.getOrDefault(analysis.capturePaths)
|
||||||
if not endpoints.isNil:
|
if not endpoints.isNil:
|
||||||
var captureMap: TableRef[seq[Value], Handle]
|
var captureMap: TableRef[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: var 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: var 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: var Turn; outerValue: Value; delta:
|
||||||
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: var Turn; group: ObserverGroup; vs: seq[Value]) =
|
proc modObserver(group: ObserverGroup; vs: 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: var Turn; group: ObserverGroup; vs: seq[Value]) =
|
proc modObserver(group: ObserverGroup; vs: 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: var 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: var 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: var Turn; v: Value) =
|
proc deliverMessage*(index: var Index; v: Value) =
|
||||||
proc observersCb(turn: var Turn; group: ObserverGroup; vs: seq[Value]) =
|
proc observersCb(group: ObserverGroup; vs: Value) =
|
||||||
for observer in group.observers.keys: message(turn, observer, vs)
|
for observer in group.observers.keys: message(observer, vs)
|
||||||
index.root.modify(turn, v, messageEvent, continuationNoop, leafNoop, observersCb)
|
index.root.modify(v, messageEvent, continuationNoop, leafNoop, observersCb)
|
|
@ -3,13 +3,15 @@
|
||||||
|
|
||||||
## This module implements the `Syndicate DSL <https://syndicate-lang.org/doc/syndicate/>`_.
|
## This module implements the `Syndicate DSL <https://syndicate-lang.org/doc/syndicate/>`_.
|
||||||
|
|
||||||
import std/[asyncdispatch, macros, tables, typetraits]
|
import std/[macros, tables, typetraits]
|
||||||
|
import pkg/cps
|
||||||
|
# import pkg/sys/ioqueue
|
||||||
|
|
||||||
import preserves
|
import preserves
|
||||||
export fromPreserves, toPreserves
|
export preserves
|
||||||
|
|
||||||
import ./syndicate/[actors, dataspaces, durings, patterns]
|
import ./[actors, dataspaces, durings, patterns]
|
||||||
import ./syndicate/protocols/dataspace
|
import ./protocols/dataspace
|
||||||
|
|
||||||
export actors, dataspace, dataspaces, patterns
|
export actors, dataspace, dataspaces, patterns
|
||||||
|
|
||||||
|
@ -34,22 +36,26 @@ proc `??`*(pat: Pattern; bindings: openArray[(int, Pattern)]): Pattern {.inline.
|
||||||
patterns.inject(pat, bindings)
|
patterns.inject(pat, bindings)
|
||||||
|
|
||||||
type
|
type
|
||||||
PublishProc = proc (turn: var Turn; v: Value; h: Handle) {.closure, gcsafe.}
|
PublishProc = proc (v: Value; h: Handle) {.closure.}
|
||||||
RetractProc = proc (turn: var Turn; h: Handle) {.closure, gcsafe.}
|
RetractProc = proc (h: Handle) {.closure.}
|
||||||
MessageProc = proc (turn: var Turn; v: Value) {.closure, gcsafe.}
|
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: var Turn; a: AssertionRef; h: Handle) {.gcsafe.} =
|
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: var Turn; h: Handle) {.gcsafe.} =
|
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: var Turn; a: AssertionRef) {.gcsafe.} =
|
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}
|
||||||
|
@ -85,7 +91,7 @@ proc generateHandlerNodes(handler: NimNode): HandlerNodes =
|
||||||
result.varSection = newNimNode(nnkVarSection, handler).
|
result.varSection = newNimNode(nnkVarSection, handler).
|
||||||
add(newIdentDefs(result.valuesSym, valuesTuple))
|
add(newIdentDefs(result.valuesSym, valuesTuple))
|
||||||
|
|
||||||
proc wrapPublishHandler(turn, handler: NimNode): NimNode =
|
proc wrapPublishHandler(handler: NimNode): NimNode =
|
||||||
var
|
var
|
||||||
(valuesSym, varSection, publishBody) =
|
(valuesSym, varSection, publishBody) =
|
||||||
generateHandlerNodes(handler)
|
generateHandlerNodes(handler)
|
||||||
|
@ -93,24 +99,24 @@ proc wrapPublishHandler(turn, handler: NimNode): NimNode =
|
||||||
handlerSym = genSym(nskProc, "publish")
|
handlerSym = genSym(nskProc, "publish")
|
||||||
bindingsSym = ident"bindings"
|
bindingsSym = ident"bindings"
|
||||||
quote do:
|
quote do:
|
||||||
proc `handlerSym`(`turn`: var Turn; `bindingsSym`: Value; `handleSym`: Handle) =
|
proc `handlerSym`(`bindingsSym`: Value; `handleSym`: Handle) =
|
||||||
`varSection`
|
`varSection`
|
||||||
if fromPreserves(`valuesSym`, bindings):
|
if fromPreserves(`valuesSym`, bindings):
|
||||||
`publishBody`
|
`publishBody`
|
||||||
|
|
||||||
proc wrapMessageHandler(turn, handler: NimNode): NimNode =
|
proc wrapMessageHandler(handler: NimNode): NimNode =
|
||||||
var
|
var
|
||||||
(valuesSym, varSection, body) =
|
(valuesSym, varSection, body) =
|
||||||
generateHandlerNodes(handler)
|
generateHandlerNodes(handler)
|
||||||
handlerSym = genSym(nskProc, "message")
|
handlerSym = genSym(nskProc, "message")
|
||||||
bindingsSym = ident"bindings"
|
bindingsSym = ident"bindings"
|
||||||
quote do:
|
quote do:
|
||||||
proc `handlerSym`(`turn`: var Turn; `bindingsSym`: Value) =
|
proc `handlerSym`(`bindingsSym`: Value) =
|
||||||
`varSection`
|
`varSection`
|
||||||
if fromPreserves(`valuesSym`, bindings):
|
if fromPreserves(`valuesSym`, bindings):
|
||||||
`body`
|
`body`
|
||||||
|
|
||||||
proc wrapDuringHandler(turn, entryBody, exitBody: NimNode): NimNode =
|
proc wrapDuringHandler(entryBody, exitBody: NimNode): NimNode =
|
||||||
var
|
var
|
||||||
(valuesSym, varSection, publishBody) =
|
(valuesSym, varSection, publishBody) =
|
||||||
generateHandlerNodes(entryBody)
|
generateHandlerNodes(entryBody)
|
||||||
|
@ -119,46 +125,54 @@ proc wrapDuringHandler(turn, entryBody, exitBody: NimNode): NimNode =
|
||||||
duringSym = genSym(nskProc, "during")
|
duringSym = genSym(nskProc, "during")
|
||||||
if exitBody.isNil:
|
if exitBody.isNil:
|
||||||
quote do:
|
quote do:
|
||||||
proc `duringSym`(`turn`: var Turn; `bindingsSym`: Value; `handleSym`: Handle): TurnAction =
|
proc `duringSym`(f: Facet; `bindingsSym`: Value; `handleSym`: Handle): FacetProc =
|
||||||
`varSection`
|
`varSection`
|
||||||
if fromPreserves(`valuesSym`, `bindingsSym`):
|
if fromPreserves(`valuesSym`, `bindingsSym`):
|
||||||
`publishBody`
|
`publishBody`
|
||||||
else:
|
else:
|
||||||
quote do:
|
quote do:
|
||||||
proc `duringSym`(`turn`: var Turn; `bindingsSym`: Value; `handleSym`: Handle): TurnAction =
|
proc `duringSym`(f: Facet; `bindingsSym`: Value; `handleSym`: Handle): FacetProc =
|
||||||
`varSection`
|
`varSection`
|
||||||
if fromPreserves(`valuesSym`, `bindingsSym`):
|
if fromPreserves(`valuesSym`, `bindingsSym`):
|
||||||
`publishBody`
|
`publishBody`
|
||||||
proc action(`turn`: var Turn) =
|
result = proc (facet: Facet) =
|
||||||
`exitBody`
|
`exitBody`
|
||||||
result = action
|
|
||||||
|
|
||||||
macro onPublish*(turn: untyped; ds: Cap; pattern: Pattern; handler: untyped) =
|
macro onPublish*(cap: Cap; pattern: Pattern; handler: untyped) =
|
||||||
## Call `handler` when an assertion matching `pattern` is published at `ds`.
|
## Call `handler` when an assertion matching `pattern` is published at `cap`.
|
||||||
let
|
let
|
||||||
argCount = argumentCount(handler)
|
argCount = argumentCount(handler)
|
||||||
handlerProc = wrapPublishHandler(turn, handler)
|
handlerProc = wrapPublishHandler(handler)
|
||||||
handlerSym = handlerProc[0]
|
handlerSym = handlerProc[0]
|
||||||
result = quote do:
|
result = quote do:
|
||||||
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(`turn`, `ds`, `pattern`, ClosureEntity(publishImpl: `handlerSym`))
|
var ce = ClosureEntity(
|
||||||
|
publishCb: `handlerSym`,
|
||||||
|
).setActions(
|
||||||
|
publish = whelp publishCont,
|
||||||
|
)
|
||||||
|
discard observe(`cap`, `pattern`, ce)
|
||||||
|
|
||||||
macro onMessage*(turn: untyped; ds: Cap; pattern: Pattern; handler: untyped) =
|
macro onMessage*(cap: Cap; pattern: Pattern; handler: untyped) =
|
||||||
## Call `handler` when an message matching `pattern` is broadcasted at `ds`.
|
## Call `handler` when an message matching `pattern` is broadcasted at `cap`.
|
||||||
let
|
let
|
||||||
argCount = argumentCount(handler)
|
argCount = argumentCount(handler)
|
||||||
handlerProc = wrapMessageHandler(turn, handler)
|
handlerProc = wrapMessageHandler(handler)
|
||||||
handlerSym = handlerProc[0]
|
handlerSym = handlerProc[0]
|
||||||
result = quote do:
|
result = quote do:
|
||||||
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(`turn`, `ds`, `pattern`, ClosureEntity(messageImpl: `handlerSym`))
|
discard observe(`cap`, `pattern`, ClosureEntity(
|
||||||
|
messageImpl: whelp messageCont,
|
||||||
|
messageCb: `handlerSym`,
|
||||||
|
name: "ClosureEntity",
|
||||||
|
))
|
||||||
|
|
||||||
macro during*(turn: untyped; ds: Cap; pattern: Pattern; publishBody, retractBody: untyped) =
|
macro during*(cap: Cap; pattern: Pattern; publishBody, retractBody: untyped) =
|
||||||
## Call `publishBody` when an assertion matching `pattern` is published to `ds` and
|
## Call `publishBody` when an assertion matching `pattern` is published to `cap` and
|
||||||
## call `retractBody` on retraction. Assertions that match `pattern` but are not
|
## call `retractBody` on retraction. Assertions that match `pattern` but are not
|
||||||
## convertable to the arguments of `publishBody` are silently discarded.
|
## convertable to the arguments of `publishBody` are silently discarded.
|
||||||
##
|
##
|
||||||
|
@ -167,36 +181,47 @@ macro during*(turn: untyped; ds: Cap; pattern: Pattern; publishBody, retractBody
|
||||||
## - `duringHandle` - dataspace handle of the assertion that triggered `publishBody`
|
## - `duringHandle` - dataspace handle of the assertion that triggered `publishBody`
|
||||||
let
|
let
|
||||||
argCount = argumentCount(publishBody)
|
argCount = argumentCount(publishBody)
|
||||||
callbackProc = wrapDuringHandler(turn, publishBody, retractBody)
|
callbackProc = wrapDuringHandler(publishBody, retractBody)
|
||||||
callbackSym = callbackProc[0]
|
callbackSym = callbackProc[0]
|
||||||
result = quote do:
|
result = quote do:
|
||||||
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`)
|
||||||
`callbackProc`
|
`callbackProc`
|
||||||
discard observe(`turn`, `ds`, `pattern`, during(`callbackSym`))
|
discard observe(`cap`, `pattern`, during(`callbackSym`))
|
||||||
|
|
||||||
macro during*(turn: untyped; ds: Cap; pattern: Pattern; publishBody: untyped) =
|
macro during*(cap: Cap; pattern: Pattern; publishBody: untyped) =
|
||||||
## Variant of `during` without a retract body.
|
## Variant of `during` without a retract body.
|
||||||
let
|
let
|
||||||
`argCount` = argumentCount(publishBody)
|
`argCount` = argumentCount(publishBody)
|
||||||
callbackProc = wrapDuringHandler(turn, publishBody, nil)
|
callbackProc = wrapDuringHandler(publishBody, nil)
|
||||||
callbackSym = callbackProc[0]
|
callbackSym = callbackProc[0]
|
||||||
result = quote do:
|
result = quote do:
|
||||||
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`)
|
||||||
`callbackProc`
|
`callbackProc`
|
||||||
discard observe(`turn`, `ds`, `pattern`, during(`callbackSym`))
|
discard observe(`cap`, `pattern`, during(`callbackSym`))
|
||||||
|
|
||||||
type BootProc = proc (turn: var Turn; ds: Cap) {.gcsafe.}
|
proc wrapHandler(body: NimNode; ident: string): NimNode =
|
||||||
type DeprecatedBootProc = proc (ds: Cap; turn: var Turn) {.gcsafe.}
|
var sym = genSym(nskProc, ident)
|
||||||
|
quote do:
|
||||||
|
proc `sym`() =
|
||||||
|
`body`
|
||||||
|
|
||||||
proc runActor*(name: string; bootProc: BootProc) =
|
#[
|
||||||
## Run an `Actor` to completion.
|
macro onStop*(facet: Facet; body: untyped) =
|
||||||
let actor = bootDataspace(name, bootProc)
|
let
|
||||||
while actor.running:
|
handlerDef = wrapHandler(body, "onStop")
|
||||||
waitFor sleepAsync(500)
|
handlerSym = handlerDef[0]
|
||||||
|
result = quote do:
|
||||||
|
`handlerDef`
|
||||||
|
addOnStopHandler(facet, `handlerSym`)
|
||||||
|
|
||||||
proc runActor*(name: string; bootProc: DeprecatedBootProc) {.deprecated.} =
|
macro onStop*(body: untyped) =
|
||||||
## Run an `Actor` to completion.
|
quote do:
|
||||||
runActor(name) do (turn: var Turn, ds: Cap):
|
block:
|
||||||
bootProc(ds, turn)
|
let facet = activeFacet()
|
||||||
|
facet.installStopHook()
|
||||||
|
if facet.stopped():
|
||||||
|
`body`
|
||||||
|
return
|
||||||
|
]#
|
|
@ -0,0 +1,19 @@
|
||||||
|
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||||
|
# SPDX-License-Identifier: Unlicense
|
||||||
|
|
||||||
|
import ./protocols/[protocol, trace]
|
||||||
|
export trace
|
||||||
|
|
||||||
|
proc traceAction*(e: protocol.Event): trace.TurnEvent =
|
||||||
|
var act = ActionDescription(orKind: ActionDescriptionKind.enqueue)
|
||||||
|
act.enqueue.event = TargetedTurnEvent(
|
||||||
|
target: cap.traceTarget,
|
||||||
|
detail: trace.TurnEvent(orKind: trace.TurnEventKind.assert)
|
||||||
|
)
|
||||||
|
act.enqueue.event.detail = trace.TurnEvent(orKind: TurnEventKind.assert)
|
||||||
|
act.enqueue.event.detail.assert = TurnEventAssert(
|
||||||
|
assertion: AssertionDescription(orKind: AssertionDescriptionKind.value),
|
||||||
|
handle: result,
|
||||||
|
)
|
||||||
|
act.enqueue.event.detail.assert.assertion.value.value = val
|
||||||
|
turn.desc.actions.add act
|
|
@ -1,35 +0,0 @@
|
||||||
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
|
||||||
# SPDX-License-Identifier: Unlicense
|
|
||||||
|
|
||||||
import std/[asyncdispatch, monotimes, times]
|
|
||||||
import preserves
|
|
||||||
import syndicate
|
|
||||||
|
|
||||||
import ../protocols/timer
|
|
||||||
from syndicate/protocols/dataspace import Observe
|
|
||||||
|
|
||||||
export timer
|
|
||||||
|
|
||||||
type Observe = dataspace.Observe
|
|
||||||
|
|
||||||
proc now: float64 = getTime().toUnixFloat()
|
|
||||||
|
|
||||||
proc spawnTimers*(turn: var Turn; ds: Cap): Actor {.discardable.} =
|
|
||||||
## Spawn a timer actor.
|
|
||||||
spawn("timer", turn) do (turn: var Turn):
|
|
||||||
|
|
||||||
during(turn, ds, inject(grab Observe(pattern: dropType LaterThan), {0: grabLit()})) do (seconds: float):
|
|
||||||
let period = seconds - now()
|
|
||||||
if period < 0.001:
|
|
||||||
discard publish(turn, ds, LaterThan(seconds: seconds))
|
|
||||||
else:
|
|
||||||
addCallback(sleepAsync(period * 1_000), turn) do (turn: var Turn):
|
|
||||||
discard publish(turn, ds, LaterThan(seconds: seconds))
|
|
||||||
|
|
||||||
proc after*(turn: var Turn; ds: Cap; dur: Duration; act: TurnAction) =
|
|
||||||
## Execute `act` after some duration of time.
|
|
||||||
let later = now() + dur.inMilliseconds.float64 * 1_000.0
|
|
||||||
onPublish(turn, ds, grab LaterThan(seconds: later)):
|
|
||||||
act(turn)
|
|
||||||
|
|
||||||
# TODO: periodic timer
|
|
|
@ -1,50 +0,0 @@
|
||||||
# SPDX-FileCopyrightText: ☭ 2022 Emery Hemingway
|
|
||||||
# SPDX-License-Identifier: Unlicense
|
|
||||||
|
|
||||||
import std/[hashes, options, tables]
|
|
||||||
import preserves
|
|
||||||
import ./actors, ./protocols/dataspace, ./skeletons
|
|
||||||
|
|
||||||
from ./protocols/protocol import Handle
|
|
||||||
|
|
||||||
type
|
|
||||||
Assertion = Value
|
|
||||||
Observe = dataspace.Observe
|
|
||||||
Turn = actors.Turn
|
|
||||||
|
|
||||||
Dataspace {.final.} = ref object of Entity
|
|
||||||
index: Index
|
|
||||||
handleMap: Table[Handle, Assertion]
|
|
||||||
|
|
||||||
method publish(ds: Dataspace; turn: var Turn; a: AssertionRef; h: Handle) {.gcsafe.} =
|
|
||||||
if add(ds.index, turn, a.value):
|
|
||||||
var obs = a.value.preservesTo(Observe)
|
|
||||||
if obs.isSome and obs.get.observer of Cap:
|
|
||||||
ds.index.add(turn, obs.get.pattern, Cap(obs.get.observer))
|
|
||||||
ds.handleMap[h] = a.value
|
|
||||||
|
|
||||||
method retract(ds: Dataspace; turn: var Turn; h: Handle) {.gcsafe.} =
|
|
||||||
let v = ds.handleMap[h]
|
|
||||||
if remove(ds.index, turn, v):
|
|
||||||
ds.handleMap.del h
|
|
||||||
var obs = v.preservesTo(Observe)
|
|
||||||
if obs.isSome and obs.get.observer of Cap:
|
|
||||||
ds.index.remove(turn, obs.get.pattern, Cap(obs.get.observer))
|
|
||||||
|
|
||||||
method message(ds: Dataspace; turn: var Turn; a: AssertionRef) {.gcsafe.} =
|
|
||||||
ds.index.deliverMessage(turn, a.value)
|
|
||||||
|
|
||||||
proc newDataspace*(turn: var Turn): Cap =
|
|
||||||
newCap(turn, Dataspace(index: initIndex()))
|
|
||||||
|
|
||||||
type BootProc = proc (turn: var Turn; ds: Cap) {.gcsafe.}
|
|
||||||
type DeprecatedBootProc = proc (ds: Cap; turn: var Turn) {.gcsafe.}
|
|
||||||
|
|
||||||
proc bootDataspace*(name: string; bootProc: BootProc): Actor =
|
|
||||||
bootActor(name) do (turn: var Turn):
|
|
||||||
discard turn.facet.preventInertCheck()
|
|
||||||
bootProc(turn, newDataspace(turn))
|
|
||||||
|
|
||||||
proc bootDataspace*(name: string; bootProc: DeprecatedBootProc): Actor {.deprecated.} =
|
|
||||||
bootDataspace(name) do (turn: var Turn, ds: Cap):
|
|
||||||
bootProc(ds, turn)
|
|
|
@ -1,48 +0,0 @@
|
||||||
# SPDX-FileCopyrightText: ☭ 2021 Emery Hemingway
|
|
||||||
# SPDX-License-Identifier: Unlicense
|
|
||||||
|
|
||||||
import std/[hashes, tables]
|
|
||||||
import preserves
|
|
||||||
import ./actors, ./patterns, ./protocols/dataspace
|
|
||||||
|
|
||||||
type
|
|
||||||
DuringProc* = proc (turn: var Turn; a: Value; h: Handle): TurnAction {.gcsafe.}
|
|
||||||
DuringActionKind = enum null, dead, act
|
|
||||||
DuringAction = object
|
|
||||||
case kind: DuringActionKind
|
|
||||||
of null, dead: discard
|
|
||||||
of act:
|
|
||||||
action: TurnAction
|
|
||||||
DuringEntity {.final.}= ref object of Entity
|
|
||||||
cb: DuringProc
|
|
||||||
assertionMap: Table[Handle, DuringAction]
|
|
||||||
|
|
||||||
method publish(de: DuringEntity; turn: var Turn; a: AssertionRef; h: Handle) =
|
|
||||||
let action = de.cb(turn, a.value, h)
|
|
||||||
# assert(not action.isNil "should have put in a no-op action")
|
|
||||||
let g = de.assertionMap.getOrDefault h
|
|
||||||
case g.kind
|
|
||||||
of null:
|
|
||||||
de.assertionMap[h] = DuringAction(kind: act, action: action)
|
|
||||||
of dead:
|
|
||||||
de.assertionMap.del h
|
|
||||||
freshen(turn, action)
|
|
||||||
of act:
|
|
||||||
raiseAssert("during: duplicate handle in publish: " & $h)
|
|
||||||
|
|
||||||
method retract(de: DuringEntity; turn: var Turn; h: Handle) =
|
|
||||||
let g = de.assertionMap.getOrDefault h
|
|
||||||
case g.kind
|
|
||||||
of null:
|
|
||||||
de.assertionMap[h] = DuringAction(kind: dead)
|
|
||||||
of dead:
|
|
||||||
raiseAssert("during: duplicate handle in retract: " & $h)
|
|
||||||
of act:
|
|
||||||
de.assertionMap.del h
|
|
||||||
if not g.action.isNil:
|
|
||||||
g.action(turn)
|
|
||||||
|
|
||||||
proc during*(cb: DuringProc): DuringEntity = DuringEntity(cb: cb)
|
|
||||||
|
|
||||||
proc observe*(turn: var Turn; ds: Cap; pat: Pattern; e: Entity): Handle =
|
|
||||||
publish(turn, ds, Observe(pattern: pat, observer: newCap(turn, e)))
|
|
|
@ -1,6 +1,6 @@
|
||||||
# Package
|
# Package
|
||||||
|
|
||||||
version = "20240208"
|
version = "20240301"
|
||||||
author = "Emery Hemingway"
|
author = "Emery Hemingway"
|
||||||
description = "Syndicated actors for conversational concurrency"
|
description = "Syndicated actors for conversational concurrency"
|
||||||
license = "Unlicense"
|
license = "Unlicense"
|
||||||
|
@ -9,4 +9,4 @@ srcDir = "src"
|
||||||
|
|
||||||
# Dependencies
|
# Dependencies
|
||||||
|
|
||||||
requires "https://github.com/ehmry/hashlib.git#f9455d4be988e14e3dc7933eb7cc7d7c4820b7ac", "nim >= 2.0.0", "https://git.syndicate-lang.org/ehmry/preserves-nim.git >= 20240208"
|
requires "https://github.com/ehmry/hashlib.git >= 20231130", "nim >= 2.0.0", "https://git.syndicate-lang.org/ehmry/preserves-nim.git >= 20240208", "https://github.com/alaviss/nim-sys.git", "https://github.com/nim-works/cps"
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
include_rules
|
include_rules
|
||||||
|
NIM_FLAGS += --define:traceSyndicate
|
||||||
: foreach *.prs |> !preserves_schema_nim |> | {schema}
|
: foreach *.prs |> !preserves_schema_nim |> | {schema}
|
||||||
: foreach t*.nim | ../../preserves-nim/<tests> {schema} $(SYNDICATE_PROTOCOL) |> !nim_run |> | ../<test>
|
: foreach t*.nim | ../../preserves-nim/<tests> {schema} $(SYNDICATE_PROTOCOL) |> !nim |> | ../<test> {test}
|
||||||
|
|
||||||
|
: foreach {test} |> SYNDICATE_TRACE_FILE=%o ./%f |> ./%B.trace.bin {bintrace}
|
||||||
|
: foreach {bintrace} |> preserves-tool convert <%f >%o |> %B
|
||||||
|
|
|
@ -23,17 +23,17 @@ proc readStdin(facet: Facet; ds: Cap; username: string) =
|
||||||
readLine()
|
readLine()
|
||||||
readLine()
|
readLine()
|
||||||
|
|
||||||
proc chat(turn: var Turn; ds: Cap; username: string) =
|
proc chat(facet: Facet; ds: Cap; username: string) =
|
||||||
during(turn, ds, ?:Present) do (who: string):
|
during(facet, ds, ?:Present) do (who: string):
|
||||||
echo who, " joined"
|
echo who, " joined"
|
||||||
do:
|
do:
|
||||||
echo who, " left"
|
echo who, " left"
|
||||||
|
|
||||||
onMessage(turn, ds, ?:Says) do (who: string, what: string):
|
onMessage(facet, ds, ?:Says) do (who: string, what: string):
|
||||||
echo who, ": ", what
|
echo who, ": ", what
|
||||||
|
|
||||||
discard publish(turn, ds, Present(username: username))
|
discard publish(facet, ds, Present(username: username))
|
||||||
readStdin(turn.facet, ds, username)
|
readStdin(facet, ds, username)
|
||||||
|
|
||||||
proc main =
|
proc main =
|
||||||
let route = envRoute()
|
let route = envRoute()
|
||||||
|
@ -48,9 +48,10 @@ proc main =
|
||||||
if username == "":
|
if username == "":
|
||||||
stderr.writeLine "--user: unspecified"
|
stderr.writeLine "--user: unspecified"
|
||||||
else:
|
else:
|
||||||
runActor("chat") do (turn: var Turn; root: Cap):
|
runActor("chat") do (root: Facet):
|
||||||
spawnRelays(turn, root)
|
let ds = facet.newDataspace()
|
||||||
resolve(turn, root, route) do (turn: var Turn; ds: Cap):
|
facet.spawnRelays(ds)
|
||||||
chat(turn, ds, username)
|
resolve(facet, ds, route) do (remote: Cap):
|
||||||
|
chat(facet, remote, username)
|
||||||
|
|
||||||
main()
|
main()
|
||||||
|
|
|
@ -1,17 +1,32 @@
|
||||||
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||||
# SPDX-License-Identifier: Unlicense
|
# SPDX-License-Identifier: Unlicense
|
||||||
|
|
||||||
import std/times
|
import std/[os, times]
|
||||||
import syndicate, syndicate/actors/timers
|
import pkg/cps
|
||||||
|
import pkg/sys/ioqueue
|
||||||
|
import sam/syndicate
|
||||||
|
import sam/actors/timers
|
||||||
|
|
||||||
proc now: float64 = getTime().toUnixFloat()
|
let actor = bootActor("timer-test") do (facet: Facet):
|
||||||
|
let timers = facet.newDataspace()
|
||||||
|
spawnTimerActor(timers)
|
||||||
|
|
||||||
runActor("test_timers") do (ds: Cap; turn: var Turn):
|
onPublish(timers, ?LaterThan(seconds: 1356091200)):
|
||||||
onPublish(turn, ds, grab(LaterThan(seconds: now()+1.0))) do:
|
echo "now in 13th bʼakʼtun"
|
||||||
stderr.writeLine "slept one second once"
|
|
||||||
onPublish(turn, ds, grab(LaterThan(seconds: now()+1.0))) do:
|
timers.after(initDuration(seconds = 3)) do ():
|
||||||
stderr.writeLine "slept one second twice"
|
echo "third timer expired"
|
||||||
onPublish(turn, ds, grab(LaterThan(seconds: now()+1.0))) do:
|
stopActor(facet)
|
||||||
stderr.writeLine "slept one second thrice"
|
|
||||||
quit()
|
timers.after(initDuration(seconds = 1)) do ():
|
||||||
spawnTimers(turn, ds)
|
echo "first timer expired"
|
||||||
|
|
||||||
|
timers.after(initDuration(seconds = 2)) do ():
|
||||||
|
echo "second timer expired"
|
||||||
|
|
||||||
|
while not actor.stopped:
|
||||||
|
echo "running actor"
|
||||||
|
if not run(actor):
|
||||||
|
echo "run(actor) did not progress"
|
||||||
|
ioqueue.run()
|
||||||
|
echo "ioqueue.run finished"
|
||||||
|
|
Loading…
Reference in New Issue