diff --git a/docs/_diagrams/actor_tree.svg b/docs/_diagrams/actor_tree.svg new file mode 100644 index 00000000..bc09dc38 --- /dev/null +++ b/docs/_diagrams/actor_tree.svg @@ -0,0 +1,125 @@ + + + + + + + + + + + + +root actorsubactor'worker_0'subactor'worker_1'subactor'deeper'main()ActorNurserytrio task treeserve()ActorNurserytrio task tree opens opens spawns +supervises spawns +supervises spawns +supervises + + + + + + + diff --git a/docs/_diagrams/context_handshake.svg b/docs/_diagrams/context_handshake.svg new file mode 100644 index 00000000..04c7e988 --- /dev/null +++ b/docs/_diagrams/context_handshake.svg @@ -0,0 +1,120 @@ + + + + + + + + + + + + +parent task(Portal)child actor task(@tractor.context fn) Start: open_context(fn, **kwargs) StartAck Started: await ctx.started(value) Yield: stream.send() Yield: stream.send() Stop: stream closed Return: fn return value + + + + + + + + + diff --git a/docs/_diagrams/debug_lock.svg b/docs/_diagrams/debug_lock.svg new file mode 100644 index 00000000..21a7e67e --- /dev/null +++ b/docs/_diagrams/debug_lock.svg @@ -0,0 +1,118 @@ + + + + + + + + + + + + +subactor 'mary'root actor(owns the tty)subactor 'bob' tractor.pause(): acquire tty lock granted: pdb REPL is yours tractor.pause(): acquire tty lock continue: release lock granted: pdb REPL is yours + + + + + + + diff --git a/docs/_diagrams/error_propagation.svg b/docs/_diagrams/error_propagation.svg new file mode 100644 index 00000000..6684a4c3 --- /dev/null +++ b/docs/_diagrams/error_propagation.svg @@ -0,0 +1,117 @@ + + + + + + + + + + + + +root actor(ActorNursery)subactor 'gertie'(healthy, gets cancelled)subactor 'bobbie'raises NameError supervises supervises RemoteActorError(boxed NameError) cancel() + + + + + + diff --git a/docs/_diagrams/infected_aio.svg b/docs/_diagrams/infected_aio.svg new file mode 100644 index 00000000..66dc8b84 --- /dev/null +++ b/docs/_diagrams/infected_aio.svg @@ -0,0 +1,122 @@ + + + + + + + + + + + + +subactor process (infect_asyncio=True)parent actor (pure trio)asyncio event loopasyncio.Taskaio_echo_server()trio (guest mode)trio task@tractor.context fn LinkedTaskChannel.send()/.receive() IPC: Context + MsgStream + + + + diff --git a/docs/_diagrams/runtime_stack.svg b/docs/_diagrams/runtime_stack.svg new file mode 100644 index 00000000..e8da9bc2 --- /dev/null +++ b/docs/_diagrams/runtime_stack.svg @@ -0,0 +1,107 @@ + + + + + + + + + + + + +your app: plain trio tasks + nurseriestractor runtime: actors, portals,contexts + streams, RPCIPC Channel: msgspec-typed msgsover TCP | UDS transportsOS: one process per actor + + + diff --git a/docs/_diagrams/streaming_pipeline.svg b/docs/_diagrams/streaming_pipeline.svg new file mode 100644 index 00000000..a0015947 --- /dev/null +++ b/docs/_diagrams/streaming_pipeline.svg @@ -0,0 +1,123 @@ + + + + + + + + + + + + +actor 'streamer_0'actor 'streamer_1'actor 'aggregator'root actorstream_data(0)stream_data(1)aggregate()main() async for:yielded ints async for:yielded ints dedupedstream + + + + + diff --git a/docs/diagrams/actor_tree.d2 b/docs/diagrams/actor_tree.d2 new file mode 100644 index 00000000..6bec7a9e --- /dev/null +++ b/docs/diagrams/actor_tree.d2 @@ -0,0 +1,31 @@ +# tractor docs diagram: the hero supervision tree. +# A process tree of trio-task-trees; every arrow is +# a parent which *must wait* on its children. +vars: { + d2-config: { + sketch: true + theme-id: 1 + pad: 16 + layout-engine: elk + } +} +direction: down +root: "root actor" { + main: "main()" + an: "ActorNursery" + main -> an: opens +} +w0: "subactor\n'worker_0'" { + tasks: "trio task tree" +} +w1: "subactor\n'worker_1'" { + main: "serve()" + an: "ActorNursery" + main -> an: opens +} +gc: "subactor\n'deeper'" { + tasks: "trio task tree" +} +root.an -> w0: "spawns +\nsupervises" +root.an -> w1: "spawns +\nsupervises" +w1.an -> gc: "spawns +\nsupervises" diff --git a/docs/diagrams/context_handshake.d2 b/docs/diagrams/context_handshake.d2 new file mode 100644 index 00000000..11971825 --- /dev/null +++ b/docs/diagrams/context_handshake.d2 @@ -0,0 +1,21 @@ +# tractor docs diagram: the inter-actor `Context` +# dialog; tractor's SC-transitive msg protocol as +# seen from both sides of `Portal.open_context()`. +vars: { + d2-config: { + sketch: true + theme-id: 1 + pad: 16 + layout-engine: elk + } +} +shape: sequence_diagram +parent: "parent task\n(Portal)" +child: "child actor task\n(@tractor.context fn)" +parent -> child: "Start: open_context(fn, **kwargs)" +child -> parent: "StartAck" +child -> parent: "Started: await ctx.started(value)" +parent -> child: "Yield: stream.send()" +child -> parent: "Yield: stream.send()" +parent -> child: "Stop: stream closed" +child -> parent: "Return: fn return value" diff --git a/docs/diagrams/debug_lock.d2 b/docs/diagrams/debug_lock.d2 new file mode 100644 index 00000000..7a96b430 --- /dev/null +++ b/docs/diagrams/debug_lock.d2 @@ -0,0 +1,19 @@ +# tractor docs diagram: multi-actor debugger REPL +# serialization via the root actor's tty lock. +vars: { + d2-config: { + sketch: true + theme-id: 1 + pad: 16 + layout-engine: elk + } +} +shape: sequence_diagram +mary: "subactor 'mary'" +root: "root actor\n(owns the tty)" +bob: "subactor 'bob'" +mary -> root: "tractor.pause(): acquire tty lock" +root -> mary: "granted: pdb REPL is yours" +bob -> root: "tractor.pause(): acquire tty lock" +mary -> root: "continue: release lock" +root -> bob: "granted: pdb REPL is yours" diff --git a/docs/diagrams/error_propagation.d2 b/docs/diagrams/error_propagation.d2 new file mode 100644 index 00000000..5a956d8f --- /dev/null +++ b/docs/diagrams/error_propagation.d2 @@ -0,0 +1,27 @@ +# tractor docs diagram: one-cancels-all error +# propagation up a (sub-)actor tree; no zombies, +# no lost errors. +vars: { + d2-config: { + sketch: true + theme-id: 1 + pad: 16 + layout-engine: elk + } +} +direction: down +root: "root actor\n(ActorNursery)" +gertie: "subactor 'gertie'\n(healthy, gets cancelled)" +bobbie: "subactor 'bobbie'\nraises NameError" +root -> gertie: supervises +root -> bobbie: supervises +bobbie -> root: "RemoteActorError\n(boxed NameError)" { + style: { + stroke-dash: 3 + } +} +root -> gertie: "cancel()" { + style: { + stroke-dash: 3 + } +} diff --git a/docs/diagrams/infected_aio.d2 b/docs/diagrams/infected_aio.d2 new file mode 100644 index 00000000..da706053 --- /dev/null +++ b/docs/diagrams/infected_aio.d2 @@ -0,0 +1,23 @@ +# tractor docs diagram: "infected asyncio" mode; +# trio runs as a guest on the asyncio loop with +# SC supervision linking tasks across both. +vars: { + d2-config: { + sketch: true + theme-id: 1 + pad: 16 + layout-engine: elk + } +} +direction: down +sub: "subactor process (infect_asyncio=True)" { + aio: "asyncio event loop" { + aiotask: "asyncio.Task\naio_echo_server()" + trio: "trio (guest mode)" { + triotask: "trio task\n@tractor.context fn" + } + trio.triotask <-> aiotask: "LinkedTaskChannel\n.send()/.receive()" + } +} +parent: "parent actor (pure trio)" +parent <-> sub.aio.trio.triotask: "IPC: Context + MsgStream" diff --git a/docs/diagrams/runtime_stack.d2 b/docs/diagrams/runtime_stack.d2 new file mode 100644 index 00000000..72602060 --- /dev/null +++ b/docs/diagrams/runtime_stack.d2 @@ -0,0 +1,16 @@ +# tractor docs diagram: the layered runtime view +# inside any single actor process. +vars: { + d2-config: { + sketch: true + theme-id: 1 + pad: 16 + layout-engine: elk + } +} +grid-rows: 4 +grid-gap: 0 +app: "your app: plain trio tasks + nurseries" +tractor: "tractor runtime: actors, portals,\ncontexts + streams, RPC" +ipc: "IPC Channel: msgspec-typed msgs\nover TCP | UDS transports" +os: "OS: one process per actor" diff --git a/docs/diagrams/streaming_pipeline.d2 b/docs/diagrams/streaming_pipeline.d2 new file mode 100644 index 00000000..1a13ba7b --- /dev/null +++ b/docs/diagrams/streaming_pipeline.d2 @@ -0,0 +1,27 @@ +# tractor docs diagram: multi-actor streaming +# pipeline topology from +# examples/full_fledged_streaming_service.py +vars: { + d2-config: { + sketch: true + theme-id: 1 + pad: 16 + layout-engine: elk + } +} +direction: right +s0: "actor 'streamer_0'" { + fn: "stream_data(0)" +} +s1: "actor 'streamer_1'" { + fn: "stream_data(1)" +} +agg: "actor 'aggregator'" { + fn: "aggregate()" +} +main: "root actor" { + fn: "main()" +} +s0 -> agg: "async for:\nyielded ints" +s1 -> agg: "async for:\nyielded ints" +agg -> main: "deduped\nstream"