feat: runtime dashboard and docs #23

Merged
zacheryasc merged 1 commit from runtime-dashboard into master 2026-02-09 09:04:57 +00:00
26 changed files with 4466 additions and 15 deletions

121
Cargo.lock generated
View file

@ -312,6 +312,12 @@ dependencies = [
"wasm-bindgen", "wasm-bindgen",
] ]
[[package]]
name = "lazy_static"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.180" version = "0.2.180"
@ -339,6 +345,15 @@ dependencies = [
"autocfg", "autocfg",
] ]
[[package]]
name = "nu-ansi-term"
version = "0.50.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
dependencies = [
"windows-sys",
]
[[package]] [[package]]
name = "num-traits" name = "num-traits"
version = "0.2.19" version = "0.2.19"
@ -360,6 +375,12 @@ version = "11.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e"
[[package]]
name = "pin-project-lite"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
[[package]] [[package]]
name = "plotters" name = "plotters"
version = "0.3.7" version = "0.3.7"
@ -524,6 +545,18 @@ version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a96887878f22d7bad8a3b6dc5b7440e0ada9a245242924394987b21cf2210a4c" checksum = "a96887878f22d7bad8a3b6dc5b7440e0ada9a245242924394987b21cf2210a4c"
[[package]]
name = "runtime-dashboard"
version = "0.1.0"
dependencies = [
"serde",
"serde_json",
"swactor",
"tiny_http",
"tracing",
"tracing-subscriber",
]
[[package]] [[package]]
name = "rustversion" name = "rustversion"
version = "1.0.22" version = "1.0.22"
@ -591,6 +624,21 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "sharded-slab"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
dependencies = [
"lazy_static",
]
[[package]]
name = "smallvec"
version = "1.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
[[package]] [[package]]
name = "swactor" name = "swactor"
version = "0.1.0" version = "0.1.0"
@ -600,6 +648,7 @@ dependencies = [
"crossbeam-utils", "crossbeam-utils",
"getrandom", "getrandom",
"serde", "serde",
"tracing",
] ]
[[package]] [[package]]
@ -646,6 +695,15 @@ version = "0.12.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
[[package]]
name = "thread_local"
version = "1.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185"
dependencies = [
"cfg-if",
]
[[package]] [[package]]
name = "tiny_http" name = "tiny_http"
version = "0.12.0" version = "0.12.0"
@ -709,6 +767,63 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801"
[[package]]
name = "tracing"
version = "0.1.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100"
dependencies = [
"pin-project-lite",
"tracing-attributes",
"tracing-core",
]
[[package]]
name = "tracing-attributes"
version = "0.1.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tracing-core"
version = "0.1.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a"
dependencies = [
"once_cell",
"valuable",
]
[[package]]
name = "tracing-log"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
dependencies = [
"log",
"once_cell",
"tracing-core",
]
[[package]]
name = "tracing-subscriber"
version = "0.3.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e"
dependencies = [
"nu-ansi-term",
"sharded-slab",
"smallvec",
"thread_local",
"tracing-core",
"tracing-log",
]
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.22" version = "1.0.22"
@ -721,6 +836,12 @@ version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3" checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3"
[[package]]
name = "valuable"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
[[package]] [[package]]
name = "walkdir" name = "walkdir"
version = "2.5.0" version = "2.5.0"

View file

@ -1,5 +1,5 @@
[workspace] [workspace]
members = [".", "crates/swactor-python", "crates/swactor-wasm", "crates/swactor-gossip", "crates/gossip-dashboard"] members = [".", "crates/swactor-python", "crates/swactor-wasm", "crates/swactor-gossip", "crates/gossip-dashboard", "crates/runtime-dashboard"]
exclude = ["tools/depgraph"] exclude = ["tools/depgraph"]
[package] [package]
@ -15,11 +15,13 @@ crate-type = ["rlib"]
default = ["getrandom"] default = ["getrandom"]
getrandom = ["dep:getrandom"] getrandom = ["dep:getrandom"]
serde = ["dep:serde"] serde = ["dep:serde"]
tracing = ["dep:tracing"]
no_random = [] # compile without access to a source of randomness no_random = [] # compile without access to a source of randomness
[dependencies] [dependencies]
getrandom = { version = "0.2", optional = true } getrandom = { version = "0.2", optional = true }
serde = { version = "1", features = ["derive"], optional = true } serde = { version = "1", features = ["derive"], optional = true }
tracing = { version = "0.1", optional = true }
crossbeam-queue = "0.3.12" crossbeam-queue = "0.3.12"
crossbeam-utils = "0.8.21" crossbeam-utils = "0.8.21"

2
crates/runtime-dashboard/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
docs/generated/
docs/**dot

View file

@ -0,0 +1,12 @@
[package]
name = "runtime-dashboard"
version = "0.1.0"
edition = "2024"
[dependencies]
swactor = { path = "../..", features = ["serde", "tracing"] }
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["registry"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
tiny_http = "0.12"

View file

@ -0,0 +1,36 @@
# runtime-dashboard
Visual dashboard and architectural diagrams for the swactor runtime.
## Generating Diagrams
Render all `.dot` sources into SVGs:
```bash
./render_docs.sh
```
**Prerequisites** (one of):
- [Graphviz](https://graphviz.org/) — `apt install graphviz` / `brew install graphviz`
- [Node.js](https://nodejs.org/) — the script auto-installs `@viz-js/viz` into `tools/`
Generated SVGs are written to `docs/generated/` (gitignored).
## Diagram Index
### DOT sources (`docs/*.dot` → `docs/generated/*.svg`)
| Diagram | Description |
|---------|-------------|
| `architecture.dot` | Structural map of all structs/traits, grouped by module, with ownership/Arc/borrow/trait-impl edges |
| `dataflow.dot` | 5 behavioral flows: cross-worker send, same-worker send, actor spawn, external inbox, 6-phase tick cycle |
| `tick_cycle.dot` | Focused view of the `Worker::tick_once` pipeline and its 6 phases |
| `type_erasure.dot` | How generic message/actor types are erased via `Box<dyn Any>` and `Box<dyn AnyActor>` |
### Hand-authored SVGs (`docs/*.svg`, committed)
| Diagram | Description |
|---------|-------------|
| `actor_lifecycle.svg` | Lifecycle states of an actor from spawn to shutdown |
| `message_lifecycle.svg` | Path of a message from send through inbox to handler |
| `runtime_lifecycle.svg` | Runtime startup, worker creation, and shutdown sequence |

View file

@ -0,0 +1,145 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1000 700" font-family="system-ui, sans-serif">
<defs>
<!-- Drop shadow filter -->
<filter id="shadow" x="-4%" y="-4%" width="108%" height="108%">
<feDropShadow dx="1" dy="2" stdDeviation="3" flood-color="#000" flood-opacity="0.10"/>
</filter>
<!-- Arrow markers -->
<marker id="arrow-gray" viewBox="0 0 10 7" refX="10" refY="3.5"
markerWidth="10" markerHeight="7" orient="auto-start-reverse" fill="#5f6368">
<polygon points="0 0, 10 3.5, 0 7"/>
</marker>
<marker id="arrow-green" viewBox="0 0 10 7" refX="10" refY="3.5"
markerWidth="10" markerHeight="7" orient="auto-start-reverse" fill="#34a853">
<polygon points="0 0, 10 3.5, 0 7"/>
</marker>
<marker id="arrow-red" viewBox="0 0 10 7" refX="10" refY="3.5"
markerWidth="10" markerHeight="7" orient="auto-start-reverse" fill="#ea4335">
<polygon points="0 0, 10 3.5, 0 7"/>
</marker>
</defs>
<!-- Background -->
<rect width="1000" height="700" rx="8" fill="#f8f9fb" stroke="#e0e3e8" stroke-width="1.5"/>
<!-- Title -->
<text x="500" y="42" text-anchor="middle" font-size="20" font-weight="bold" fill="#202124">Actor Lifecycle</text>
<text x="500" y="62" text-anchor="middle" font-size="12" fill="#5f6368">src/runtime.rs · src/worker.rs</text>
<!-- ============================================ -->
<!-- Entry circle -->
<circle cx="90" cy="200" r="16" fill="#4285f4" filter="url(#shadow)"/>
<text x="90" y="205" text-anchor="middle" font-size="14" fill="#fff" font-weight="bold">&#x25B6;</text>
<!-- Arrow: entry → SPAWNED -->
<line x1="106" y1="200" x2="156" y2="200" stroke="#5f6368" stroke-width="1.8" marker-end="url(#arrow-gray)"/>
<text x="131" y="192" text-anchor="middle" font-size="11" fill="#5f6368">spawn()</text>
<!-- ============================================ -->
<!-- STATE: SPAWNED -->
<rect x="168" y="170" width="155" height="60" rx="12" fill="#e8f0fe" stroke="#4285f4" stroke-width="1.8" filter="url(#shadow)"/>
<text x="245" y="196" text-anchor="middle" font-size="14" font-weight="600" fill="#1a73e8">SPAWNED</text>
<text x="245" y="214" text-anchor="middle" font-size="10" fill="#5f6368">in spawn queue</text>
<!-- Arrow: SPAWNED → READY -->
<line x1="323" y1="200" x2="393" y2="200" stroke="#5f6368" stroke-width="1.8" marker-end="url(#arrow-gray)"/>
<text x="358" y="190" text-anchor="middle" font-size="11" fill="#5f6368">Phase 1/4</text>
<text x="358" y="203" text-anchor="middle" font-size="10" fill="#5f6368">drain →</text>
<text x="358" y="215" text-anchor="middle" font-size="10" fill="#5f6368">pool.insert()</text>
<!-- ============================================ -->
<!-- STATE: READY -->
<rect x="405" y="170" width="155" height="60" rx="12" fill="#e6f4ea" stroke="#34a853" stroke-width="1.8" filter="url(#shadow)"/>
<text x="482" y="196" text-anchor="middle" font-size="14" font-weight="600" fill="#137333">READY</text>
<text x="482" y="214" text-anchor="middle" font-size="10" fill="#5f6368">in pool, mailbox empty</text>
<!-- Arrow: READY → MAILBOX_PENDING (down) -->
<line x1="482" y1="230" x2="482" y2="320" stroke="#5f6368" stroke-width="1.8" marker-end="url(#arrow-gray)"/>
<text x="492" y="266" font-size="11" fill="#5f6368">send_to /</text>
<text x="492" y="280" font-size="10" fill="#5f6368">deliver pushes</text>
<text x="492" y="293" font-size="10" fill="#5f6368">to VecDeque</text>
<!-- ============================================ -->
<!-- STATE: MAILBOX_PENDING -->
<rect x="405" y="330" width="155" height="60" rx="12" fill="#fef7e0" stroke="#f9ab00" stroke-width="1.8" filter="url(#shadow)"/>
<text x="482" y="354" text-anchor="middle" font-size="13" font-weight="600" fill="#e37400">MAILBOX</text>
<text x="482" y="372" text-anchor="middle" font-size="13" font-weight="600" fill="#e37400">PENDING</text>
<!-- Arrow: MAILBOX_PENDING → PROCESSING (right) -->
<line x1="560" y1="360" x2="630" y2="360" stroke="#5f6368" stroke-width="1.8" marker-end="url(#arrow-gray)"/>
<text x="595" y="350" text-anchor="middle" font-size="11" fill="#5f6368">Phase 3</text>
<text x="595" y="374" text-anchor="middle" font-size="10" fill="#5f6368">pop_front()</text>
<!-- ============================================ -->
<!-- STATE: PROCESSING -->
<rect x="642" y="330" width="155" height="60" rx="12" fill="#e8f0fe" stroke="#4285f4" stroke-width="1.8" filter="url(#shadow)"/>
<text x="719" y="356" text-anchor="middle" font-size="14" font-weight="600" fill="#1a73e8">PROCESSING</text>
<text x="719" y="374" text-anchor="middle" font-size="10" fill="#5f6368">handler executing</text>
<!-- Arrow: PROCESSING → READY (success, up and left, curved) -->
<path d="M 719 330 Q 719 268 600 200 Q 580 190 560 200"
fill="none" stroke="#34a853" stroke-width="1.8" marker-end="url(#arrow-green)"/>
<text x="670" y="258" font-size="11" fill="#34a853" font-weight="600">handler OK</text>
<!-- Arrow: PROCESSING → PANIC_STATE (down) -->
<line x1="719" y1="390" x2="719" y2="470" stroke="#ea4335" stroke-width="1.8" marker-end="url(#arrow-red)"/>
<text x="740" y="430" font-size="11" fill="#ea4335" font-weight="600">panic!</text>
<text x="740" y="444" font-size="10" fill="#ea4335">catch_unwind</text>
<!-- ============================================ -->
<!-- STATE: PANIC_STATE -->
<rect x="642" y="480" width="155" height="60" rx="12" fill="#fce8e6" stroke="#ea4335" stroke-width="1.8" filter="url(#shadow)"/>
<text x="719" y="506" text-anchor="middle" font-size="14" font-weight="600" fill="#c5221f">PANIC</text>
<text x="719" y="524" text-anchor="middle" font-size="10" fill="#5f6368">caught, actor survives</text>
<!-- Arrow: PANIC_STATE → PROCESSING (loop back left then up) -->
<path d="M 642 510 Q 610 510 610 440 Q 610 370 630 360"
fill="none" stroke="#5f6368" stroke-width="1.8" stroke-dasharray="5,3" marker-end="url(#arrow-gray)"/>
<text x="580" y="450" font-size="10" fill="#5f6368">next msg,</text>
<text x="580" y="463" font-size="10" fill="#5f6368">next tick</text>
<!-- ============================================ -->
<!-- STATE: DEAD -->
<rect x="168" y="480" width="155" height="60" rx="12" fill="#f1f3f4" stroke="#9aa0a6" stroke-width="1.8" filter="url(#shadow)"/>
<text x="245" y="506" text-anchor="middle" font-size="14" font-weight="600" fill="#5f6368">DEAD</text>
<text x="245" y="524" text-anchor="middle" font-size="10" fill="#5f6368">removed from pool</text>
<!-- Arrow: READY → DEAD -->
<path d="M 405 220 Q 350 250 310 350 Q 280 420 280 470"
fill="none" stroke="#9aa0a6" stroke-width="1.5" stroke-dasharray="4,3" marker-end="url(#arrow-gray)"/>
<text x="310" y="380" font-size="10" fill="#9aa0a6">shutdown /</text>
<text x="310" y="393" font-size="10" fill="#9aa0a6">pool.remove()</text>
<!-- Arrow: PROCESSING → DEAD -->
<path d="M 642 385 Q 500 440 370 480"
fill="none" stroke="#9aa0a6" stroke-width="1.5" stroke-dasharray="4,3" marker-end="url(#arrow-gray)"/>
<text x="490" y="446" font-size="10" fill="#9aa0a6">shutdown</text>
<!-- Arrow: PANIC_STATE → DEAD -->
<path d="M 642 510 Q 500 520 340 510"
fill="none" stroke="#9aa0a6" stroke-width="1.5" stroke-dasharray="4,3" marker-end="url(#arrow-gray)"/>
<!-- ============================================ -->
<!-- Legend -->
<rect x="50" y="600" width="900" height="75" rx="8" fill="none" stroke="#e0e3e8" stroke-width="1"/>
<text x="70" y="622" font-size="12" font-weight="600" fill="#202124">Legend</text>
<!-- Normal arrow -->
<line x1="70" y1="645" x2="105" y2="645" stroke="#5f6368" stroke-width="1.8" marker-end="url(#arrow-gray)"/>
<text x="115" y="649" font-size="11" fill="#5f6368">Transition</text>
<!-- Success arrow -->
<line x1="220" y1="645" x2="255" y2="645" stroke="#34a853" stroke-width="1.8" marker-end="url(#arrow-green)"/>
<text x="265" y="649" font-size="11" fill="#34a853">Success</text>
<!-- Error arrow -->
<line x1="370" y1="645" x2="405" y2="645" stroke="#ea4335" stroke-width="1.8" marker-end="url(#arrow-red)"/>
<text x="415" y="649" font-size="11" fill="#ea4335">Error / Panic</text>
<!-- Dashed arrow -->
<line x1="540" y1="645" x2="575" y2="645" stroke="#9aa0a6" stroke-width="1.5" stroke-dasharray="4,3" marker-end="url(#arrow-gray)"/>
<text x="585" y="649" font-size="11" fill="#9aa0a6">Shutdown path</text>
<!-- Source refs -->
<text x="780" y="649" font-size="10" fill="#9aa0a6">runtime.rs:125-134 · worker.rs:204-219</text>
</svg>

After

Width:  |  Height:  |  Size: 8.4 KiB

View file

@ -0,0 +1,692 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Generated by graphviz version 14.1.1 (20251213.1925)
-->
<!-- Title: architecture Pages: 1 -->
<svg width="2903pt" height="1196pt"
viewBox="0.00 0.00 2903.00 1196.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 1191.85)">
<title>architecture</title>
<polygon fill="white" stroke="none" points="-4,4 -4,-1191.85 2899,-1191.85 2899,4 -4,4"/>
<g id="clust1" class="cluster">
<title>cluster_legend</title>
<path fill="none" stroke="#888888" d="M20,-1048.15C20,-1048.15 234,-1048.15 234,-1048.15 240,-1048.15 246,-1054.15 246,-1060.15 246,-1060.15 246,-1165.35 246,-1165.35 246,-1171.35 240,-1177.35 234,-1177.35 234,-1177.35 20,-1177.35 20,-1177.35 14,-1177.35 8,-1171.35 8,-1165.35 8,-1165.35 8,-1060.15 8,-1060.15 8,-1054.15 14,-1048.15 20,-1048.15"/>
<text xml:space="preserve" text-anchor="middle" x="34.35" y="-1163.45" font-family="Helvetica,sans-Serif" font-size="11.00">Legend</text>
</g>
<g id="clust2" class="cluster">
<title>cluster_runtime</title>
<path fill="#d6eaf8" stroke="#1a5276" d="M748,-560.25C748,-560.25 1014,-560.25 1014,-560.25 1020,-560.25 1026,-566.25 1026,-572.25 1026,-572.25 1026,-1156.85 1026,-1156.85 1026,-1162.85 1020,-1168.85 1014,-1168.85 1014,-1168.85 748,-1168.85 748,-1168.85 742,-1168.85 736,-1162.85 736,-1156.85 736,-1156.85 736,-572.25 736,-572.25 736,-566.25 742,-560.25 748,-560.25"/>
<text xml:space="preserve" text-anchor="middle" x="768.45" y="-1154.95" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#1a5276">runtime.rs</text>
</g>
<g id="clust3" class="cluster">
<title>cluster_actor</title>
<path fill="#d5f5e3" stroke="#1e8449" d="M268,-8C268,-8 716,-8 716,-8 722,-8 728,-14 728,-20 728,-20 728,-1149.61 728,-1149.61 728,-1155.61 722,-1161.61 716,-1161.61 716,-1161.61 268,-1161.61 268,-1161.61 262,-1161.61 256,-1155.61 256,-1149.61 256,-1149.61 256,-20 256,-20 256,-14 262,-8 268,-8"/>
<text xml:space="preserve" text-anchor="middle" x="282.34" y="-1147.71" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#1e8449">actor.rs</text>
</g>
<g id="clust4" class="cluster">
<title>cluster_delivery</title>
<path fill="#e8daef" stroke="#6c3483" d="M1046,-357.88C1046,-357.88 1947,-357.88 1947,-357.88 1953,-357.88 1959,-363.88 1959,-369.88 1959,-369.88 1959,-969.05 1959,-969.05 1959,-975.05 1953,-981.05 1947,-981.05 1947,-981.05 1046,-981.05 1046,-981.05 1040,-981.05 1034,-975.05 1034,-969.05 1034,-969.05 1034,-369.88 1034,-369.88 1034,-363.88 1040,-357.88 1046,-357.88"/>
<text xml:space="preserve" text-anchor="middle" x="1067.06" y="-967.15" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#6c3483">delivery.rs</text>
</g>
<g id="clust5" class="cluster">
<title>cluster_worker</title>
<path fill="#fadbd8" stroke="#922b21" d="M1983,-184.85C1983,-184.85 2223,-184.85 2223,-184.85 2229,-184.85 2235,-190.85 2235,-196.85 2235,-196.85 2235,-1167.85 2235,-1167.85 2235,-1173.85 2229,-1179.85 2223,-1179.85 2223,-1179.85 1983,-1179.85 1983,-1179.85 1977,-1179.85 1971,-1173.85 1971,-1167.85 1971,-1167.85 1971,-196.85 1971,-196.85 1971,-190.85 1977,-184.85 1983,-184.85"/>
<text xml:space="preserve" text-anchor="middle" x="2001.61" y="-1165.95" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#922b21">worker.rs</text>
</g>
<g id="clust6" class="cluster">
<title>cluster_channel</title>
<path fill="#d1f2eb" stroke="#117864" d="M2255,-184.85C2255,-184.85 2489,-184.85 2489,-184.85 2495,-184.85 2501,-190.85 2501,-196.85 2501,-196.85 2501,-649.45 2501,-649.45 2501,-655.45 2495,-661.45 2489,-661.45 2489,-661.45 2255,-661.45 2255,-661.45 2249,-661.45 2243,-655.45 2243,-649.45 2243,-649.45 2243,-196.85 2243,-196.85 2243,-190.85 2249,-184.85 2255,-184.85"/>
<text xml:space="preserve" text-anchor="middle" x="2276.38" y="-647.55" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#117864">channel.rs</text>
</g>
<g id="clust7" class="cluster">
<title>cluster_stats</title>
<path fill="#d1c4e9" stroke="#283593" d="M2699,-339.65C2699,-339.65 2875,-339.65 2875,-339.65 2881,-339.65 2887,-345.65 2887,-351.65 2887,-351.65 2887,-1167.85 2887,-1167.85 2887,-1173.85 2881,-1179.85 2875,-1179.85 2875,-1179.85 2699,-1179.85 2699,-1179.85 2693,-1179.85 2687,-1173.85 2687,-1167.85 2687,-1167.85 2687,-351.65 2687,-351.65 2687,-345.65 2693,-339.65 2699,-339.65"/>
<text xml:space="preserve" text-anchor="middle" x="2712.72" y="-1165.95" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#283593">stats.rs</text>
</g>
<g id="clust8" class="cluster">
<title>cluster_config</title>
<path fill="#fef9e7" stroke="#b7950b" d="M2521,-328.65C2521,-328.65 2667,-328.65 2667,-328.65 2673,-328.65 2679,-334.65 2679,-340.65 2679,-340.65 2679,-682.45 2679,-682.45 2679,-688.45 2673,-694.45 2667,-694.45 2667,-694.45 2521,-694.45 2521,-694.45 2515,-694.45 2509,-688.45 2509,-682.45 2509,-682.45 2509,-340.65 2509,-340.65 2509,-334.65 2515,-328.65 2521,-328.65"/>
<text xml:space="preserve" text-anchor="middle" x="2537.79" y="-680.55" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#b7950b">config.rs</text>
</g>
<!-- legend -->
<g id="node1" class="node">
<title>legend</title>
<polygon fill="#f8f8f8" stroke="none" points="237.66,-1148.15 16.34,-1148.15 16.34,-1056.15 237.66,-1056.15 237.66,-1148.15"/>
<text xml:space="preserve" text-anchor="start" x="30.34" y="-1129.15" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#333333">───────</text>
<text xml:space="preserve" text-anchor="start" x="96.69" y="-1129.15" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#333333">Ownership (solid)</text>
<text xml:space="preserve" text-anchor="start" x="30.34" y="-1110.15" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00" fill="#e67300">━━━━━━━</text>
<text xml:space="preserve" text-anchor="start" x="96.69" y="-1109.15" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e67300">Arc&#45;shared (bold orange)</text>
<text xml:space="preserve" text-anchor="start" x="30.34" y="-1089.15" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#7b2d8b">&#45; &#45; &#45; &#45; &#45; &#45;</text>
<text xml:space="preserve" text-anchor="start" x="96.69" y="-1089.15" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#7b2d8b">Borrow / &amp;&#39;a (dashed purple)</text>
<text xml:space="preserve" text-anchor="start" x="30.34" y="-1069.15" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#2e8b57">· · · · · ·▷</text>
<text xml:space="preserve" text-anchor="start" x="96.69" y="-1069.15" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#2e8b57">Trait impl (dotted green)</text>
</g>
<!-- Runtime -->
<g id="node2" class="node">
<title>Runtime</title>
<polygon fill="white" stroke="black" points="744.32,-756.35 744.32,-984.35 1017.68,-984.35 1017.68,-756.35 744.32,-756.35"/>
<polygon fill="#2980b9" stroke="none" points="752.32,-958.35 752.32,-980.35 1009.68,-980.35 1009.68,-958.35 752.32,-958.35"/>
<polygon fill="none" stroke="black" points="752.32,-958.35 752.32,-980.35 1009.68,-980.35 1009.68,-958.35 752.32,-958.35"/>
<text xml:space="preserve" text-anchor="start" x="861" y="-967.35" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00" fill="white">Runtime</text>
<polygon fill="none" stroke="black" points="752.32,-936.35 752.32,-958.35 1009.68,-958.35 1009.68,-936.35 752.32,-936.35"/>
<text xml:space="preserve" text-anchor="start" x="757.32" y="-944.35" font-family="Helvetica,sans-Serif" font-size="10.00">config: RuntimeConfig</text>
<polygon fill="none" stroke="black" points="752.32,-914.35 752.32,-936.35 1009.68,-936.35 1009.68,-914.35 752.32,-914.35"/>
<text xml:space="preserve" text-anchor="start" x="757.32" y="-922.35" font-family="Helvetica,sans-Serif" font-size="10.00">address_map: Arc&lt;AddressMap&gt;</text>
<polygon fill="none" stroke="black" points="752.32,-892.35 752.32,-914.35 1009.68,-914.35 1009.68,-892.35 752.32,-892.35"/>
<text xml:space="preserve" text-anchor="start" x="757.32" y="-900.35" font-family="Helvetica,sans-Serif" font-size="10.00">inbox_registry: Arc&lt;InboxRegistry&gt;</text>
<polygon fill="none" stroke="black" points="752.32,-870.35 752.32,-892.35 1009.68,-892.35 1009.68,-870.35 752.32,-870.35"/>
<text xml:space="preserve" text-anchor="start" x="757.32" y="-878.35" font-family="Helvetica,sans-Serif" font-size="10.00">transfer_txs: Vec&lt;Sender&lt;Envelope&gt;&gt;</text>
<polygon fill="none" stroke="black" points="752.32,-848.35 752.32,-870.35 1009.68,-870.35 1009.68,-848.35 752.32,-848.35"/>
<text xml:space="preserve" text-anchor="start" x="757.32" y="-856.35" font-family="Helvetica,sans-Serif" font-size="10.00">spawn_txs: Vec&lt;Sender&lt;(Addr, Box&lt;dyn AnyActor&gt;)&gt;&gt;</text>
<polygon fill="none" stroke="black" points="752.32,-826.35 752.32,-848.35 1009.68,-848.35 1009.68,-826.35 752.32,-826.35"/>
<text xml:space="preserve" text-anchor="start" x="757.32" y="-834.35" font-family="Helvetica,sans-Serif" font-size="10.00">placement: Placement</text>
<polygon fill="none" stroke="black" points="752.32,-804.35 752.32,-826.35 1009.68,-826.35 1009.68,-804.35 752.32,-804.35"/>
<text xml:space="preserve" text-anchor="start" x="757.32" y="-812.35" font-family="Helvetica,sans-Serif" font-size="10.00">is_running: AtomicBool</text>
<polygon fill="none" stroke="black" points="752.32,-782.35 752.32,-804.35 1009.68,-804.35 1009.68,-782.35 752.32,-782.35"/>
<text xml:space="preserve" text-anchor="start" x="757.32" y="-790.35" font-family="Helvetica,sans-Serif" font-size="10.00">worker_stats: Vec&lt;Arc&lt;WorkerStats&gt;&gt;</text>
<polygon fill="none" stroke="black" points="752.32,-760.35 752.32,-782.35 1009.68,-782.35 1009.68,-760.35 752.32,-760.35"/>
<text xml:space="preserve" text-anchor="start" x="757.32" y="-768.35" font-family="Helvetica,sans-Serif" font-size="10.00">tick_workers: RefCell&lt;Vec&lt;Worker&gt;&gt;</text>
</g>
<!-- ContextInner -->
<g id="node10" class="node">
<title>ContextInner</title>
<ellipse fill="#a9dfbf" stroke="black" stroke-width="2" cx="631" cy="-605.75" rx="89.49" ry="42.14"/>
<text xml:space="preserve" text-anchor="start" x="583.77" y="-620.55" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00">«trait» ContextInner</text>
<text xml:space="preserve" text-anchor="start" x="578.72" y="-602.45" font-family="Helvetica,sans-Serif" font-size="9.00">fn send_any(addr, msg)</text>
<text xml:space="preserve" text-anchor="start" x="578.72" y="-585.65" font-family="Helvetica,sans-Serif" font-size="9.00">fn spawn_any(addr, actor)</text>
</g>
<!-- Runtime&#45;&gt;ContextInner -->
<g id="edge35" class="edge">
<title>Runtime&#45;&gt;ContextInner</title>
<path fill="none" stroke="#2e8b57" stroke-width="1.5" stroke-dasharray="1,5" d="M784.34,-755.94C784.34,-684.49 784.34,-606 784.34,-606 784.34,-606 732.91,-606 732.91,-606"/>
<polygon fill="none" stroke="#2e8b57" stroke-width="1.5" points="732.91,-602.5 722.91,-606 732.91,-609.5 732.91,-602.5"/>
<text xml:space="preserve" text-anchor="middle" x="794.25" y="-713.35" font-family="Helvetica,sans-Serif" font-size="9.00">impl</text>
</g>
<!-- AddressMap -->
<g id="node14" class="node">
<title>AddressMap</title>
<polygon fill="white" stroke="black" points="1237.17,-579.75 1237.17,-631.75 1498.83,-631.75 1498.83,-579.75 1237.17,-579.75"/>
<polygon fill="#8e44ad" stroke="none" points="1245.17,-605.75 1245.17,-627.75 1490.83,-627.75 1490.83,-605.75 1245.17,-605.75"/>
<polygon fill="none" stroke="black" points="1245.17,-605.75 1245.17,-627.75 1490.83,-627.75 1490.83,-605.75 1245.17,-605.75"/>
<text xml:space="preserve" text-anchor="start" x="1337.99" y="-614.75" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00" fill="white">AddressMap</text>
<polygon fill="none" stroke="black" points="1245.17,-583.75 1245.17,-605.75 1490.83,-605.75 1490.83,-583.75 1245.17,-583.75"/>
<text xml:space="preserve" text-anchor="start" x="1250.17" y="-591.75" font-family="Helvetica,sans-Serif" font-size="10.00">inner: RwLock&lt;HashMap&lt;ActorAddress, WorkerId&gt;&gt;</text>
</g>
<!-- Runtime&#45;&gt;AddressMap -->
<g id="edge19" class="edge">
<title>Runtime&#45;&gt;AddressMap</title>
<path fill="none" stroke="#e67300" stroke-width="2.5" d="M1017.95,-761C1148.2,-761 1324.39,-761 1324.39,-761 1324.39,-761 1324.39,-645.87 1324.39,-645.87"/>
<polygon fill="#e67300" stroke="#e67300" stroke-width="2.5" points="1327.89,-645.87 1324.39,-635.87 1320.89,-645.87 1327.89,-645.87"/>
<text xml:space="preserve" text-anchor="middle" x="982.75" y="-713.35" font-family="Helvetica,sans-Serif" font-size="9.00">Arc</text>
</g>
<!-- Placement -->
<g id="node15" class="node">
<title>Placement</title>
<polygon fill="white" stroke="black" points="1527.54,-568.75 1527.54,-642.75 1642.46,-642.75 1642.46,-568.75 1527.54,-568.75"/>
<polygon fill="#8e44ad" stroke="none" points="1535.54,-616.75 1535.54,-638.75 1634.46,-638.75 1634.46,-616.75 1535.54,-616.75"/>
<polygon fill="none" stroke="black" points="1535.54,-616.75 1535.54,-638.75 1634.46,-638.75 1634.46,-616.75 1535.54,-616.75"/>
<text xml:space="preserve" text-anchor="start" x="1559.99" y="-625.75" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00" fill="white">Placement</text>
<polygon fill="none" stroke="black" points="1535.54,-594.75 1535.54,-616.75 1634.46,-616.75 1634.46,-594.75 1535.54,-594.75"/>
<text xml:space="preserve" text-anchor="start" x="1540.54" y="-602.75" font-family="Helvetica,sans-Serif" font-size="10.00">next: AtomicUsize</text>
<polygon fill="none" stroke="black" points="1535.54,-572.75 1535.54,-594.75 1634.46,-594.75 1634.46,-572.75 1535.54,-572.75"/>
<text xml:space="preserve" text-anchor="start" x="1540.54" y="-580.75" font-family="Helvetica,sans-Serif" font-size="10.00">num_workers: usize</text>
</g>
<!-- Runtime&#45;&gt;Placement -->
<g id="edge1" class="edge">
<title>Runtime&#45;&gt;Placement</title>
<path fill="none" stroke="#333333" d="M1017.99,-765C1222.79,-765 1585,-765 1585,-765 1585,-765 1585,-654.6 1585,-654.6"/>
<polygon fill="#333333" stroke="#333333" points="1588.5,-654.6 1585,-644.6 1581.5,-654.6 1588.5,-654.6"/>
<text xml:space="preserve" text-anchor="middle" x="1202.51" y="-713.35" font-family="Helvetica,sans-Serif" font-size="9.00">owns</text>
</g>
<!-- InboxRegistry -->
<g id="node16" class="node">
<title>InboxRegistry</title>
<polygon fill="white" stroke="black" points="1671.26,-579.75 1671.26,-631.75 1950.74,-631.75 1950.74,-579.75 1671.26,-579.75"/>
<polygon fill="#8e44ad" stroke="none" points="1679.26,-605.75 1679.26,-627.75 1942.74,-627.75 1942.74,-605.75 1679.26,-605.75"/>
<polygon fill="none" stroke="black" points="1679.26,-605.75 1679.26,-627.75 1942.74,-627.75 1942.74,-605.75 1679.26,-605.75"/>
<text xml:space="preserve" text-anchor="start" x="1777.66" y="-614.75" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00" fill="white">InboxRegistry</text>
<polygon fill="none" stroke="black" points="1679.26,-583.75 1679.26,-605.75 1942.74,-605.75 1942.74,-583.75 1679.26,-583.75"/>
<text xml:space="preserve" text-anchor="start" x="1684.26" y="-591.75" font-family="Helvetica,sans-Serif" font-size="10.00">senders: RwLock&lt;HashMap&lt;Addr, Arc&lt;dyn SenderT&gt;&gt;&gt;</text>
</g>
<!-- Runtime&#45;&gt;InboxRegistry -->
<g id="edge20" class="edge">
<title>Runtime&#45;&gt;InboxRegistry</title>
<path fill="none" stroke="#e67300" stroke-width="2.5" d="M1017.84,-770C1253.2,-770 1709.66,-770 1709.66,-770 1709.66,-770 1709.66,-645.92 1709.66,-645.92"/>
<polygon fill="#e67300" stroke="#e67300" stroke-width="2.5" points="1713.16,-645.92 1709.66,-635.92 1706.16,-645.92 1713.16,-645.92"/>
<text xml:space="preserve" text-anchor="middle" x="1337.75" y="-713.35" font-family="Helvetica,sans-Serif" font-size="9.00">Arc</text>
</g>
<!-- Worker -->
<g id="node19" class="node">
<title>Worker</title>
<polygon fill="white" stroke="black" points="1979.39,-535.75 1979.39,-675.75 2226.61,-675.75 2226.61,-535.75 1979.39,-535.75"/>
<polygon fill="#c0392b" stroke="none" points="1987.39,-649.75 1987.39,-671.75 2218.61,-671.75 2218.61,-649.75 1987.39,-649.75"/>
<polygon fill="none" stroke="black" points="1987.39,-649.75 1987.39,-671.75 2218.61,-671.75 2218.61,-649.75 1987.39,-649.75"/>
<text xml:space="preserve" text-anchor="start" x="2085.77" y="-658.75" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00" fill="white">Worker</text>
<polygon fill="none" stroke="black" points="1987.39,-627.75 1987.39,-649.75 2218.61,-649.75 2218.61,-627.75 1987.39,-627.75"/>
<text xml:space="preserve" text-anchor="start" x="1992.39" y="-635.75" font-family="Helvetica,sans-Serif" font-size="10.00">id: WorkerId</text>
<polygon fill="none" stroke="black" points="1987.39,-605.75 1987.39,-627.75 2218.61,-627.75 2218.61,-605.75 1987.39,-605.75"/>
<text xml:space="preserve" text-anchor="start" x="1992.39" y="-613.75" font-family="Helvetica,sans-Serif" font-size="10.00">pool: ActorPool</text>
<polygon fill="none" stroke="black" points="1987.39,-583.75 1987.39,-605.75 2218.61,-605.75 2218.61,-583.75 1987.39,-583.75"/>
<text xml:space="preserve" text-anchor="start" x="1992.39" y="-591.75" font-family="Helvetica,sans-Serif" font-size="10.00">transfer_rx: Receiver&lt;Envelope&gt;</text>
<polygon fill="none" stroke="black" points="1987.39,-561.75 1987.39,-583.75 2218.61,-583.75 2218.61,-561.75 1987.39,-561.75"/>
<text xml:space="preserve" text-anchor="start" x="1992.39" y="-569.75" font-family="Helvetica,sans-Serif" font-size="10.00">spawn_rx: Receiver&lt;(Addr, Box&lt;dyn AnyActor&gt;)&gt;</text>
<polygon fill="none" stroke="black" points="1987.39,-539.75 1987.39,-561.75 2218.61,-561.75 2218.61,-539.75 1987.39,-539.75"/>
<text xml:space="preserve" text-anchor="start" x="1992.39" y="-547.75" font-family="Helvetica,sans-Serif" font-size="10.00">stats: Arc&lt;WorkerStats&gt;</text>
</g>
<!-- Runtime&#45;&gt;Worker -->
<g id="edge38" class="edge">
<title>Runtime&#45;&gt;Worker</title>
<path fill="none" stroke="#333333" d="M977.66,-756.12C977.66,-707.59 977.66,-662 977.66,-662 977.66,-662 1967.57,-662 1967.57,-662"/>
<polygon fill="#333333" stroke="#333333" points="1967.57,-665.5 1977.57,-662 1967.57,-658.5 1967.57,-665.5"/>
<text xml:space="preserve" text-anchor="middle" x="2082.02" y="-713.35" font-family="Helvetica,sans-Serif" font-size="9.00">RefCell&lt;Vec&lt;&gt;&gt;</text>
</g>
<!-- SenderCh -->
<g id="node24" class="node">
<title>SenderCh</title>
<polygon fill="white" stroke="black" points="2266.01,-579.75 2266.01,-631.75 2435.99,-631.75 2435.99,-579.75 2266.01,-579.75"/>
<polygon fill="#148f77" stroke="none" points="2274.01,-605.75 2274.01,-627.75 2427.99,-627.75 2427.99,-605.75 2274.01,-605.75"/>
<polygon fill="none" stroke="black" points="2274.01,-605.75 2274.01,-627.75 2427.99,-627.75 2427.99,-605.75 2274.01,-605.75"/>
<text xml:space="preserve" text-anchor="start" x="2325.16" y="-614.75" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00" fill="white">Sender&lt;T&gt;</text>
<polygon fill="none" stroke="black" points="2274.01,-583.75 2274.01,-605.75 2427.99,-605.75 2427.99,-583.75 2274.01,-583.75"/>
<text xml:space="preserve" text-anchor="start" x="2279.01" y="-591.75" font-family="Helvetica,sans-Serif" font-size="10.00">queue: Arc&lt;HybridChannel&lt;T&gt;&gt;</text>
</g>
<!-- Runtime&#45;&gt;SenderCh -->
<g id="edge15" class="edge">
<title>Runtime&#45;&gt;SenderCh</title>
<path fill="none" stroke="#333333" d="M1017.93,-775C1377.79,-775 2322.67,-775 2322.67,-775 2322.67,-775 2322.67,-643.71 2322.67,-643.71"/>
<polygon fill="#333333" stroke="#333333" points="2326.17,-643.71 2322.67,-633.71 2319.17,-643.71 2326.17,-643.71"/>
<text xml:space="preserve" text-anchor="middle" x="2299.39" y="-718.75" font-family="Helvetica,sans-Serif" font-size="9.00">transfer_txs +</text>
<text xml:space="preserve" text-anchor="middle" x="2299.39" y="-707.95" font-family="Helvetica,sans-Serif" font-size="9.00">spawn_txs</text>
</g>
<!-- WorkerStats -->
<g id="node26" class="node">
<title>WorkerStats</title>
<polygon fill="white" stroke="black" points="2695.13,-348.15 2695.13,-444.15 2872.87,-444.15 2872.87,-348.15 2695.13,-348.15"/>
<polygon fill="#3949ab" stroke="none" points="2703.13,-418.15 2703.13,-440.15 2864.87,-440.15 2864.87,-418.15 2703.13,-418.15"/>
<polygon fill="none" stroke="black" points="2703.13,-418.15 2703.13,-440.15 2864.87,-440.15 2864.87,-418.15 2703.13,-418.15"/>
<text xml:space="preserve" text-anchor="start" x="2754.55" y="-427.15" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00" fill="white">WorkerStats</text>
<polygon fill="none" stroke="black" points="2703.13,-396.15 2703.13,-418.15 2864.87,-418.15 2864.87,-396.15 2703.13,-396.15"/>
<text xml:space="preserve" text-anchor="start" x="2708.13" y="-404.15" font-family="Helvetica,sans-Serif" font-size="10.00">num_actors: AtomicUsize</text>
<polygon fill="none" stroke="black" points="2703.13,-374.15 2703.13,-396.15 2864.87,-396.15 2864.87,-374.15 2703.13,-374.15"/>
<text xml:space="preserve" text-anchor="start" x="2708.13" y="-382.15" font-family="Helvetica,sans-Serif" font-size="10.00">total_mailbox_depth: AtomicUsize</text>
<polygon fill="none" stroke="black" points="2703.13,-352.15 2703.13,-374.15 2864.87,-374.15 2864.87,-352.15 2703.13,-352.15"/>
<text xml:space="preserve" text-anchor="start" x="2708.13" y="-360.15" font-family="Helvetica,sans-Serif" font-size="10.00">messages_processed: AtomicU64</text>
</g>
<!-- Runtime&#45;&gt;WorkerStats -->
<g id="edge21" class="edge">
<title>Runtime&#45;&gt;WorkerStats</title>
<path fill="none" stroke="#e67300" stroke-width="2.5" d="M1017.94,-784C1442.45,-784 2707.95,-784 2707.95,-784 2707.95,-784 2707.95,-458.27 2707.95,-458.27"/>
<polygon fill="#e67300" stroke="#e67300" stroke-width="2.5" points="2711.45,-458.27 2707.95,-448.27 2704.45,-458.27 2711.45,-458.27"/>
<text xml:space="preserve" text-anchor="middle" x="2797.76" y="-603.05" font-family="Helvetica,sans-Serif" font-size="9.00">Vec&lt;Arc&gt;</text>
</g>
<!-- RuntimeConfig -->
<g id="node29" class="node">
<title>RuntimeConfig</title>
<polygon fill="white" stroke="black" points="2516.52,-546.75 2516.52,-664.75 2671.48,-664.75 2671.48,-546.75 2516.52,-546.75"/>
<polygon fill="#d4ac0d" stroke="none" points="2524.52,-638.75 2524.52,-660.75 2663.48,-660.75 2663.48,-638.75 2524.52,-638.75"/>
<polygon fill="none" stroke="black" points="2524.52,-638.75 2524.52,-660.75 2663.48,-660.75 2663.48,-638.75 2524.52,-638.75"/>
<text xml:space="preserve" text-anchor="start" x="2558.17" y="-647.75" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00" fill="white">RuntimeConfig</text>
<polygon fill="none" stroke="black" points="2524.52,-616.75 2524.52,-638.75 2663.48,-638.75 2663.48,-616.75 2524.52,-616.75"/>
<text xml:space="preserve" text-anchor="start" x="2529.52" y="-624.75" font-family="Helvetica,sans-Serif" font-size="10.00">max_actors: usize</text>
<polygon fill="none" stroke="black" points="2524.52,-594.75 2524.52,-616.75 2663.48,-616.75 2663.48,-594.75 2524.52,-594.75"/>
<text xml:space="preserve" text-anchor="start" x="2529.52" y="-602.75" font-family="Helvetica,sans-Serif" font-size="10.00">actor_max_messages: usize</text>
<polygon fill="none" stroke="black" points="2524.52,-572.75 2524.52,-594.75 2663.48,-594.75 2663.48,-572.75 2524.52,-572.75"/>
<text xml:space="preserve" text-anchor="start" x="2529.52" y="-580.75" font-family="Helvetica,sans-Serif" font-size="10.00">num_threads: usize</text>
<polygon fill="none" stroke="black" points="2524.52,-550.75 2524.52,-572.75 2663.48,-572.75 2663.48,-550.75 2524.52,-550.75"/>
<text xml:space="preserve" text-anchor="start" x="2529.52" y="-558.75" font-family="Helvetica,sans-Serif" font-size="10.00">backoff_policy: BackoffPolicy</text>
</g>
<!-- Runtime&#45;&gt;RuntimeConfig -->
<g id="edge2" class="edge">
<title>Runtime&#45;&gt;RuntimeConfig</title>
<path fill="none" stroke="#333333" d="M1018,-779C1419.98,-779 2568.17,-779 2568.17,-779 2568.17,-779 2568.17,-736.04 2568.17,-705.72"/>
<polygon fill="#333333" stroke="#333333" points="2571.68,-705.96 2568.17,-695.96 2564.68,-705.96 2571.68,-705.96"/>
<text xml:space="preserve" text-anchor="middle" x="2487.51" y="-713.35" font-family="Helvetica,sans-Serif" font-size="9.00">owns</text>
</g>
<!-- RuntimeHandle -->
<g id="node3" class="node">
<title>RuntimeHandle</title>
<polygon fill="white" stroke="black" points="799.9,-1065.15 799.9,-1139.15 962.1,-1139.15 962.1,-1065.15 799.9,-1065.15"/>
<polygon fill="#2980b9" stroke="none" points="807.9,-1113.15 807.9,-1135.15 954.1,-1135.15 954.1,-1113.15 807.9,-1113.15"/>
<polygon fill="none" stroke="black" points="807.9,-1113.15 807.9,-1135.15 954.1,-1135.15 954.1,-1113.15 807.9,-1113.15"/>
<text xml:space="preserve" text-anchor="start" x="844.33" y="-1122.15" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00" fill="white">RuntimeHandle</text>
<polygon fill="none" stroke="black" points="807.9,-1091.15 807.9,-1113.15 954.1,-1113.15 954.1,-1091.15 807.9,-1091.15"/>
<text xml:space="preserve" text-anchor="start" x="812.9" y="-1099.15" font-family="Helvetica,sans-Serif" font-size="10.00">runtime: Arc&lt;Runtime&gt;</text>
<polygon fill="none" stroke="black" points="807.9,-1069.15 807.9,-1091.15 954.1,-1091.15 954.1,-1069.15 807.9,-1069.15"/>
<text xml:space="preserve" text-anchor="start" x="812.9" y="-1077.15" font-family="Helvetica,sans-Serif" font-size="10.00">threads: Vec&lt;JoinHandle&lt;()&gt;&gt;</text>
</g>
<!-- RuntimeHandle&#45;&gt;Runtime -->
<g id="edge18" class="edge">
<title>RuntimeHandle&#45;&gt;Runtime</title>
<path fill="none" stroke="#e67300" stroke-width="2.5" d="M881,-1064.9C881,-1064.9 881,-998.45 881,-998.45"/>
<polygon fill="#e67300" stroke="#e67300" stroke-width="2.5" points="884.5,-998.45 881,-988.45 877.5,-998.45 884.5,-998.45"/>
<text xml:space="preserve" text-anchor="middle" x="887.75" y="-1016.55" font-family="Helvetica,sans-Serif" font-size="9.00">Arc</text>
</g>
<!-- Inbox -->
<g id="node4" class="node">
<title>Inbox</title>
<polygon fill="white" stroke="black" points="824.37,-568.75 824.37,-642.75 937.63,-642.75 937.63,-568.75 824.37,-568.75"/>
<polygon fill="#2980b9" stroke="none" points="832.37,-616.75 832.37,-638.75 929.63,-638.75 929.63,-616.75 832.37,-616.75"/>
<polygon fill="none" stroke="black" points="832.37,-616.75 832.37,-638.75 929.63,-638.75 929.63,-616.75 832.37,-616.75"/>
<text xml:space="preserve" text-anchor="start" x="857.66" y="-625.75" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00" fill="white">Inbox&lt;M&gt;</text>
<polygon fill="none" stroke="black" points="832.37,-594.75 832.37,-616.75 929.63,-616.75 929.63,-594.75 832.37,-594.75"/>
<text xml:space="preserve" text-anchor="start" x="837.37" y="-602.75" font-family="Helvetica,sans-Serif" font-size="10.00">addr: ActorAddress</text>
<polygon fill="none" stroke="black" points="832.37,-572.75 832.37,-594.75 929.63,-594.75 929.63,-572.75 832.37,-572.75"/>
<text xml:space="preserve" text-anchor="start" x="837.37" y="-580.75" font-family="Helvetica,sans-Serif" font-size="10.00">inner: Receiver&lt;M&gt;</text>
</g>
<!-- ActorAddress -->
<g id="node5" class="node">
<title>ActorAddress</title>
<polygon fill="white" stroke="black" points="623.98,-359.15 623.98,-433.15 720.02,-433.15 720.02,-359.15 623.98,-359.15"/>
<polygon fill="#27ae60" stroke="none" points="631.98,-407.15 631.98,-429.15 712.02,-429.15 712.02,-407.15 631.98,-407.15"/>
<polygon fill="none" stroke="black" points="631.98,-407.15 631.98,-429.15 712.02,-429.15 712.02,-407.15 631.98,-407.15"/>
<text xml:space="preserve" text-anchor="start" x="638.94" y="-416.15" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00" fill="white">ActorAddress</text>
<polygon fill="none" stroke="black" points="631.98,-385.15 631.98,-407.15 712.02,-407.15 712.02,-385.15 631.98,-385.15"/>
<text xml:space="preserve" text-anchor="start" x="636.98" y="-393.15" font-family="Helvetica,sans-Serif" font-size="10.00">0: [u8; 32]</text>
<polygon fill="none" stroke="black" points="631.98,-363.15 631.98,-385.15 712.02,-385.15 712.02,-363.15 631.98,-363.15"/>
<text xml:space="preserve" text-anchor="start" x="636.98" y="-372.15" font-family="Helvetica,sans-Serif" font-style="italic" font-size="10.00">Copy, Eq, Hash</text>
</g>
<!-- Inbox&#45;&gt;ActorAddress -->
<g id="edge10" class="edge">
<title>Inbox&#45;&gt;ActorAddress</title>
<path fill="none" stroke="#333333" d="M862.12,-568.47C862.12,-517.42 862.12,-432 862.12,-432 862.12,-432 731.98,-432 731.98,-432"/>
<polygon fill="#333333" stroke="#333333" points="731.98,-428.5 721.98,-432 731.98,-435.5 731.98,-428.5"/>
<text xml:space="preserve" text-anchor="middle" x="781.01" y="-492.75" font-family="Helvetica,sans-Serif" font-size="9.00">addr</text>
</g>
<!-- ReceiverCh -->
<g id="node25" class="node">
<title>ReceiverCh</title>
<polygon fill="white" stroke="black" points="2251.01,-370.15 2251.01,-422.15 2420.99,-422.15 2420.99,-370.15 2251.01,-370.15"/>
<polygon fill="#148f77" stroke="none" points="2259.01,-396.15 2259.01,-418.15 2412.99,-418.15 2412.99,-396.15 2259.01,-396.15"/>
<polygon fill="none" stroke="black" points="2259.01,-396.15 2259.01,-418.15 2412.99,-418.15 2412.99,-396.15 2259.01,-396.15"/>
<text xml:space="preserve" text-anchor="start" x="2306.26" y="-405.15" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00" fill="white">Receiver&lt;T&gt;</text>
<polygon fill="none" stroke="black" points="2259.01,-374.15 2259.01,-396.15 2412.99,-396.15 2412.99,-374.15 2259.01,-374.15"/>
<text xml:space="preserve" text-anchor="start" x="2264.01" y="-382.15" font-family="Helvetica,sans-Serif" font-size="10.00">queue: Arc&lt;HybridChannel&lt;T&gt;&gt;</text>
</g>
<!-- Inbox&#45;&gt;ReceiverCh -->
<g id="edge9" class="edge">
<title>Inbox&#45;&gt;ReceiverCh</title>
<path fill="none" stroke="#333333" d="M899.88,-568.46C899.88,-520.98 899.88,-445 899.88,-445 899.88,-445 2256.01,-445 2256.01,-445 2256.01,-445 2256.01,-434.09 2256.01,-434.09"/>
<polygon fill="#333333" stroke="#333333" points="2259.51,-434.09 2256.01,-424.09 2252.51,-434.09 2259.51,-434.09"/>
<text xml:space="preserve" text-anchor="middle" x="2363.51" y="-492.75" font-family="Helvetica,sans-Serif" font-size="9.00">owns</text>
</g>
<!-- Actor -->
<g id="node6" class="node">
<title>Actor</title>
<polygon fill="white" stroke="black" points="402.75,-204.35 402.75,-256.35 563.25,-256.35 563.25,-204.35 402.75,-204.35"/>
<polygon fill="#27ae60" stroke="none" points="410.75,-230.35 410.75,-252.35 555.25,-252.35 555.25,-230.35 410.75,-230.35"/>
<polygon fill="none" stroke="black" points="410.75,-230.35 410.75,-252.35 555.25,-252.35 555.25,-230.35 410.75,-230.35"/>
<text xml:space="preserve" text-anchor="start" x="460.49" y="-239.35" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00" fill="white">Actor&lt;A&gt;</text>
<polygon fill="none" stroke="black" points="410.75,-208.35 410.75,-230.35 555.25,-230.35 555.25,-208.35 410.75,-208.35"/>
<text xml:space="preserve" text-anchor="start" x="415.75" y="-217.35" font-family="Helvetica,sans-Serif" font-size="10.00">0: A &#160;</text>
<text xml:space="preserve" text-anchor="start" x="439.1" y="-217.35" font-family="Helvetica,sans-Serif" font-style="italic" font-size="10.00">(where A: ActorInterface)</text>
</g>
<!-- ActorInterface -->
<g id="node8" class="node">
<title>ActorInterface</title>
<ellipse fill="#a9dfbf" stroke="black" stroke-width="2" cx="364" cy="-70.02" rx="99.73" ry="54.02"/>
<text xml:space="preserve" text-anchor="start" x="313.71" y="-93.22" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00">«trait» ActorInterface</text>
<text xml:space="preserve" text-anchor="start" x="304.48" y="-75.12" font-family="Helvetica,sans-Serif" font-size="9.00">type Incoming: Message</text>
<text xml:space="preserve" text-anchor="start" x="304.48" y="-58.32" font-family="Helvetica,sans-Serif" font-size="9.00">type Response: Message</text>
<text xml:space="preserve" text-anchor="start" x="304.48" y="-41.52" font-family="Helvetica,sans-Serif" font-size="9.00">fn handle(&amp;mut self, ctx, msg)</text>
</g>
<!-- Actor&#45;&gt;ActorInterface -->
<g id="edge34" class="edge">
<title>Actor&#45;&gt;ActorInterface</title>
<path fill="none" stroke="#2e8b57" stroke-width="1.5" stroke-dasharray="1,5" d="M433.24,-203.87C433.24,-203.87 433.24,-121.42 433.24,-121.42"/>
<polygon fill="none" stroke="#2e8b57" stroke-width="1.5" points="436.74,-121.42 433.24,-111.42 429.74,-121.42 436.74,-121.42"/>
<text xml:space="preserve" text-anchor="middle" x="415.01" y="-155.75" font-family="Helvetica,sans-Serif" font-size="9.00">wraps A: impl</text>
</g>
<!-- AnyActor -->
<g id="node9" class="node">
<title>AnyActor</title>
<ellipse fill="#a9dfbf" stroke="black" stroke-width="2" cx="606" cy="-70.02" rx="113.53" ry="30.26"/>
<text xml:space="preserve" text-anchor="start" x="567.11" y="-76.42" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00">«trait» AnyActor</text>
<text xml:space="preserve" text-anchor="start" x="536.72" y="-58.32" font-family="Helvetica,sans-Serif" font-size="9.00">fn handle_any(&amp;mut self, ctx, msg)</text>
</g>
<!-- Actor&#45;&gt;AnyActor -->
<g id="edge33" class="edge">
<title>Actor&#45;&gt;AnyActor</title>
<path fill="none" stroke="#2e8b57" stroke-width="1.5" stroke-dasharray="1,5" d="M527.86,-203.87C527.86,-203.87 527.86,-104.66 527.86,-104.66"/>
<polygon fill="none" stroke="#2e8b57" stroke-width="1.5" points="531.36,-104.66 527.86,-94.66 524.36,-104.66 531.36,-104.66"/>
<text xml:space="preserve" text-anchor="middle" x="564.25" y="-155.75" font-family="Helvetica,sans-Serif" font-size="9.00">impl</text>
</g>
<!-- Ctx -->
<g id="node7" class="node">
<title>Ctx</title>
<polygon fill="white" stroke="black" points="447.57,-833.35 447.57,-907.35 594.43,-907.35 594.43,-833.35 447.57,-833.35"/>
<polygon fill="#27ae60" stroke="none" points="455.57,-881.35 455.57,-903.35 586.43,-903.35 586.43,-881.35 455.57,-881.35"/>
<polygon fill="none" stroke="black" points="455.57,-881.35 455.57,-903.35 586.43,-903.35 586.43,-881.35 455.57,-881.35"/>
<text xml:space="preserve" text-anchor="start" x="503.13" y="-890.35" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00" fill="white">Ctx&lt;&#39;a&gt;</text>
<polygon fill="none" stroke="black" points="455.57,-859.35 455.57,-881.35 586.43,-881.35 586.43,-859.35 455.57,-859.35"/>
<text xml:space="preserve" text-anchor="start" x="460.57" y="-867.35" font-family="Helvetica,sans-Serif" font-size="10.00">inner: &amp;&#39;a dyn ContextInner</text>
<polygon fill="none" stroke="black" points="455.57,-837.35 455.57,-859.35 586.43,-859.35 586.43,-837.35 455.57,-837.35"/>
<text xml:space="preserve" text-anchor="start" x="460.57" y="-845.35" font-family="Helvetica,sans-Serif" font-size="10.00">self_addr: ActorAddress</text>
</g>
<!-- Ctx&#45;&gt;ActorAddress -->
<g id="edge11" class="edge">
<title>Ctx&#45;&gt;ActorAddress</title>
<path fill="none" stroke="#333333" d="M494.54,-833.12C494.54,-721.3 494.54,-396 494.54,-396 494.54,-396 611.99,-396 611.99,-396"/>
<polygon fill="#333333" stroke="#333333" points="611.99,-399.5 621.99,-396 611.99,-392.5 611.99,-399.5"/>
<text xml:space="preserve" text-anchor="middle" x="493.51" y="-603.05" font-family="Helvetica,sans-Serif" font-size="9.00">self_addr</text>
</g>
<!-- Ctx&#45;&gt;ContextInner -->
<g id="edge26" class="edge">
<title>Ctx&#45;&gt;ContextInner</title>
<path fill="none" stroke="#7b2d8b" stroke-width="1.5" stroke-dasharray="5,2" d="M567.97,-833C567.97,-833 567.97,-648.2 567.97,-648.2"/>
<polygon fill="#7b2d8b" stroke="#7b2d8b" stroke-width="1.5" points="571.47,-648.2 567.97,-638.2 564.47,-648.2 571.47,-648.2"/>
<text xml:space="preserve" text-anchor="middle" x="590.87" y="-713.35" font-family="Helvetica,sans-Serif" font-size="9.00">&amp;&#39;a dyn</text>
</g>
<!-- MessageTrait -->
<g id="node11" class="node">
<title>MessageTrait</title>
<ellipse fill="#a9dfbf" stroke="black" stroke-width="2" cx="493" cy="-1102.15" rx="99.02" ry="30.26"/>
<text xml:space="preserve" text-anchor="start" x="455.49" y="-1108.55" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00">«trait» Message</text>
<text xml:space="preserve" text-anchor="start" x="433.98" y="-1090.45" font-family="Helvetica,sans-Serif" font-size="9.00">&#39;static + Clone + Send + Sync</text>
</g>
<!-- Envelope -->
<g id="node12" class="node">
<title>Envelope</title>
<polygon fill="white" stroke="black" points="1041.81,-568.75 1041.81,-642.75 1208.19,-642.75 1208.19,-568.75 1041.81,-568.75"/>
<polygon fill="#8e44ad" stroke="none" points="1049.81,-616.75 1049.81,-638.75 1200.19,-638.75 1200.19,-616.75 1049.81,-616.75"/>
<polygon fill="none" stroke="black" points="1049.81,-616.75 1049.81,-638.75 1200.19,-638.75 1200.19,-616.75 1049.81,-616.75"/>
<text xml:space="preserve" text-anchor="start" x="1102.77" y="-625.75" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00" fill="white">Envelope</text>
<polygon fill="none" stroke="black" points="1049.81,-594.75 1049.81,-616.75 1200.19,-616.75 1200.19,-594.75 1049.81,-594.75"/>
<text xml:space="preserve" text-anchor="start" x="1054.81" y="-602.75" font-family="Helvetica,sans-Serif" font-size="10.00">dest: ActorAddress</text>
<polygon fill="none" stroke="black" points="1049.81,-572.75 1049.81,-594.75 1200.19,-594.75 1200.19,-572.75 1049.81,-572.75"/>
<text xml:space="preserve" text-anchor="start" x="1054.81" y="-580.75" font-family="Helvetica,sans-Serif" font-size="10.00">payload: Box&lt;dyn Any + Send&gt;</text>
</g>
<!-- Envelope&#45;&gt;ActorAddress -->
<g id="edge12" class="edge">
<title>Envelope&#45;&gt;ActorAddress</title>
<path fill="none" stroke="#333333" d="M1125,-568.47C1125,-516.89 1125,-430 1125,-430 1125,-430 731.99,-430 731.99,-430"/>
<polygon fill="#333333" stroke="#333333" points="731.99,-426.5 721.99,-430 731.99,-433.5 731.99,-426.5"/>
<text xml:space="preserve" text-anchor="middle" x="1032.51" y="-492.75" font-family="Helvetica,sans-Serif" font-size="9.00">dest</text>
</g>
<!-- WorkerId -->
<g id="node13" class="node">
<title>WorkerId</title>
<polygon fill="white" stroke="black" points="1470.33,-370.15 1470.33,-422.15 1539.67,-422.15 1539.67,-370.15 1470.33,-370.15"/>
<polygon fill="#8e44ad" stroke="none" points="1478.33,-396.15 1478.33,-418.15 1531.67,-418.15 1531.67,-396.15 1478.33,-396.15"/>
<polygon fill="none" stroke="black" points="1478.33,-396.15 1478.33,-418.15 1531.67,-418.15 1531.67,-396.15 1478.33,-396.15"/>
<text xml:space="preserve" text-anchor="start" x="1483.33" y="-405.15" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00" fill="white">WorkerId</text>
<polygon fill="none" stroke="black" points="1478.33,-374.15 1478.33,-396.15 1531.67,-396.15 1531.67,-374.15 1478.33,-374.15"/>
<text xml:space="preserve" text-anchor="start" x="1483.33" y="-382.15" font-family="Helvetica,sans-Serif" font-size="10.00">0: usize</text>
</g>
<!-- AddressMap&#45;&gt;ActorAddress -->
<g id="edge14" class="edge">
<title>AddressMap&#45;&gt;ActorAddress</title>
<path fill="none" stroke="#333333" d="M1353.75,-579.52C1353.75,-530.08 1353.75,-428 1353.75,-428 1353.75,-428 732,-428 732,-428"/>
<polygon fill="#333333" stroke="#333333" points="732,-424.5 722,-428 732,-431.5 732,-424.5"/>
<text xml:space="preserve" text-anchor="middle" x="1254.51" y="-492.75" font-family="Helvetica,sans-Serif" font-size="9.00">HashMap key</text>
</g>
<!-- AddressMap&#45;&gt;WorkerId -->
<g id="edge13" class="edge">
<title>AddressMap&#45;&gt;WorkerId</title>
<path fill="none" stroke="#333333" d="M1484.58,-579.62C1484.58,-579.62 1484.58,-434.11 1484.58,-434.11"/>
<polygon fill="#333333" stroke="#333333" points="1488.08,-434.11 1484.58,-424.11 1481.08,-434.11 1488.08,-434.11"/>
<text xml:space="preserve" text-anchor="middle" x="1462.26" y="-492.75" font-family="Helvetica,sans-Serif" font-size="9.00">HashMap val</text>
</g>
<!-- SenderT -->
<g id="node18" class="node">
<title>SenderT</title>
<ellipse fill="#d2b4de" stroke="black" stroke-width="2" cx="1824" cy="-396.15" rx="122.03" ry="30.26"/>
<text xml:space="preserve" text-anchor="start" x="1787.6" y="-402.55" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00">«trait» SenderT</text>
<text xml:space="preserve" text-anchor="start" x="1748.71" y="-384.45" font-family="Helvetica,sans-Serif" font-size="9.00">fn try_send_any(msg: Box&lt;dyn Any&gt;)</text>
</g>
<!-- InboxRegistry&#45;&gt;SenderT -->
<g id="edge25" class="edge">
<title>InboxRegistry&#45;&gt;SenderT</title>
<path fill="none" stroke="#e67300" stroke-width="2.5" d="M1783.32,-579.62C1783.32,-579.62 1783.32,-438.74 1783.32,-438.74"/>
<polygon fill="#e67300" stroke="#e67300" stroke-width="2.5" points="1786.82,-438.74 1783.32,-428.74 1779.82,-438.74 1786.82,-438.74"/>
<text xml:space="preserve" text-anchor="middle" x="1834.26" y="-492.75" font-family="Helvetica,sans-Serif" font-size="9.00">Arc&lt;dyn&gt;</text>
</g>
<!-- TickContext -->
<g id="node17" class="node">
<title>TickContext</title>
<polygon fill="white" stroke="black" points="1748.05,-789.35 1748.05,-951.35 1939.95,-951.35 1939.95,-789.35 1748.05,-789.35"/>
<polygon fill="#8e44ad" stroke="none" points="1756.05,-925.35 1756.05,-947.35 1931.95,-947.35 1931.95,-925.35 1756.05,-925.35"/>
<polygon fill="none" stroke="black" points="1756.05,-925.35 1756.05,-947.35 1931.95,-947.35 1931.95,-925.35 1756.05,-925.35"/>
<text xml:space="preserve" text-anchor="start" x="1805.57" y="-934.35" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00" fill="white">TickContext&lt;&#39;a&gt;</text>
<polygon fill="none" stroke="black" points="1756.05,-903.35 1756.05,-925.35 1931.95,-925.35 1931.95,-903.35 1756.05,-903.35"/>
<text xml:space="preserve" text-anchor="start" x="1761.05" y="-911.35" font-family="Helvetica,sans-Serif" font-size="10.00">address_map: &amp;&#39;a AddressMap</text>
<polygon fill="none" stroke="black" points="1756.05,-881.35 1756.05,-903.35 1931.95,-903.35 1931.95,-881.35 1756.05,-881.35"/>
<text xml:space="preserve" text-anchor="start" x="1761.05" y="-889.35" font-family="Helvetica,sans-Serif" font-size="10.00">transfer_txs: &amp;&#39;a [Sender&lt;Envelope&gt;]</text>
<polygon fill="none" stroke="black" points="1756.05,-859.35 1756.05,-881.35 1931.95,-881.35 1931.95,-859.35 1756.05,-859.35"/>
<text xml:space="preserve" text-anchor="start" x="1761.05" y="-867.35" font-family="Helvetica,sans-Serif" font-size="10.00">spawn_txs: &amp;&#39;a [Sender&lt;...&gt;]</text>
<polygon fill="none" stroke="black" points="1756.05,-837.35 1756.05,-859.35 1931.95,-859.35 1931.95,-837.35 1756.05,-837.35"/>
<text xml:space="preserve" text-anchor="start" x="1761.05" y="-845.35" font-family="Helvetica,sans-Serif" font-size="10.00">placement: &amp;&#39;a Placement</text>
<polygon fill="none" stroke="black" points="1756.05,-815.35 1756.05,-837.35 1931.95,-837.35 1931.95,-815.35 1756.05,-815.35"/>
<text xml:space="preserve" text-anchor="start" x="1761.05" y="-823.35" font-family="Helvetica,sans-Serif" font-size="10.00">inbox_registry: &amp;&#39;a InboxRegistry</text>
<polygon fill="none" stroke="black" points="1756.05,-793.35 1756.05,-815.35 1931.95,-815.35 1931.95,-793.35 1756.05,-793.35"/>
<text xml:space="preserve" text-anchor="start" x="1761.05" y="-801.35" font-family="Helvetica,sans-Serif" font-size="10.00">config: &amp;&#39;a RuntimeConfig</text>
</g>
<!-- TickContext&#45;&gt;AddressMap -->
<g id="edge27" class="edge">
<title>TickContext&#45;&gt;AddressMap</title>
<path fill="none" stroke="#7b2d8b" stroke-width="1.5" stroke-dasharray="5,2" d="M1747.77,-870C1620.36,-870 1411.61,-870 1411.61,-870 1411.61,-870 1411.61,-644.32 1411.61,-644.32"/>
<polygon fill="#7b2d8b" stroke="#7b2d8b" stroke-width="1.5" points="1415.11,-644.32 1411.61,-634.32 1408.11,-644.32 1415.11,-644.32"/>
<text xml:space="preserve" text-anchor="middle" x="1509.36" y="-713.35" font-family="Helvetica,sans-Serif" font-size="9.00">&amp;&#39;a</text>
</g>
<!-- TickContext&#45;&gt;Placement -->
<g id="edge29" class="edge">
<title>TickContext&#45;&gt;Placement</title>
<path fill="none" stroke="#7b2d8b" stroke-width="1.5" stroke-dasharray="5,2" d="M1812.02,-789.08C1812.02,-722.08 1812.02,-638 1812.02,-638 1812.02,-638 1655.14,-638 1655.14,-638"/>
<polygon fill="#7b2d8b" stroke="#7b2d8b" stroke-width="1.5" points="1655.14,-634.5 1645.14,-638 1655.14,-641.5 1655.14,-634.5"/>
<text xml:space="preserve" text-anchor="middle" x="1691.36" y="-713.35" font-family="Helvetica,sans-Serif" font-size="9.00">&amp;&#39;a</text>
</g>
<!-- TickContext&#45;&gt;InboxRegistry -->
<g id="edge30" class="edge">
<title>TickContext&#45;&gt;InboxRegistry</title>
<path fill="none" stroke="#7b2d8b" stroke-width="1.5" stroke-dasharray="5,2" d="M1875.98,-789.16C1875.98,-789.16 1875.98,-644.27 1875.98,-644.27"/>
<polygon fill="#7b2d8b" stroke="#7b2d8b" stroke-width="1.5" points="1879.48,-644.27 1875.98,-634.27 1872.48,-644.27 1879.48,-644.27"/>
<text xml:space="preserve" text-anchor="middle" x="1839.36" y="-713.35" font-family="Helvetica,sans-Serif" font-size="9.00">&amp;&#39;a</text>
</g>
<!-- TickContext&#45;&gt;SenderCh -->
<g id="edge28" class="edge">
<title>TickContext&#45;&gt;SenderCh</title>
<path fill="none" stroke="#7b2d8b" stroke-width="1.5" stroke-dasharray="5,2" d="M1940.32,-796C2094.45,-796 2379.33,-796 2379.33,-796 2379.33,-796 2379.33,-644.47 2379.33,-644.47"/>
<polygon fill="#7b2d8b" stroke="#7b2d8b" stroke-width="1.5" points="2382.83,-644.47 2379.33,-634.47 2375.83,-644.47 2382.83,-644.47"/>
<text xml:space="preserve" text-anchor="middle" x="2371.11" y="-713.35" font-family="Helvetica,sans-Serif" font-size="9.00">&amp;&#39;a []</text>
</g>
<!-- TickContext&#45;&gt;RuntimeConfig -->
<g id="edge31" class="edge">
<title>TickContext&#45;&gt;RuntimeConfig</title>
<path fill="none" stroke="#7b2d8b" stroke-width="1.5" stroke-dasharray="5,2" d="M1940.3,-804C2148.07,-804 2619.83,-804 2619.83,-804 2619.83,-804 2619.83,-677.5 2619.83,-677.5"/>
<polygon fill="#7b2d8b" stroke="#7b2d8b" stroke-width="1.5" points="2623.33,-677.5 2619.83,-667.5 2616.33,-677.5 2623.33,-677.5"/>
<text xml:space="preserve" text-anchor="middle" x="2569.36" y="-713.35" font-family="Helvetica,sans-Serif" font-size="9.00">&amp;&#39;a</text>
</g>
<!-- Worker&#45;&gt;WorkerId -->
<g id="edge6" class="edge">
<title>Worker&#45;&gt;WorkerId</title>
<path fill="none" stroke="#333333" d="M1979.1,-549C1811.6,-549 1533.6,-549 1533.6,-549 1533.6,-549 1533.6,-433.94 1533.6,-433.94"/>
<polygon fill="#333333" stroke="#333333" points="1537.1,-433.94 1533.6,-423.94 1530.1,-433.94 1537.1,-433.94"/>
<text xml:space="preserve" text-anchor="middle" x="1974.5" y="-492.75" font-family="Helvetica,sans-Serif" font-size="9.00">id</text>
</g>
<!-- ActorPool -->
<g id="node21" class="node">
<title>ActorPool</title>
<polygon fill="white" stroke="black" points="1991.29,-370.15 1991.29,-422.15 2210.71,-422.15 2210.71,-370.15 1991.29,-370.15"/>
<polygon fill="#c0392b" stroke="none" points="1999.29,-396.15 1999.29,-418.15 2202.71,-418.15 2202.71,-396.15 1999.29,-396.15"/>
<polygon fill="none" stroke="black" points="1999.29,-396.15 1999.29,-418.15 2202.71,-418.15 2202.71,-396.15 1999.29,-396.15"/>
<text xml:space="preserve" text-anchor="start" x="2077.11" y="-405.15" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00" fill="white">ActorPool</text>
<polygon fill="none" stroke="black" points="1999.29,-374.15 1999.29,-396.15 2202.71,-396.15 2202.71,-374.15 1999.29,-374.15"/>
<text xml:space="preserve" text-anchor="start" x="2004.29" y="-382.15" font-family="Helvetica,sans-Serif" font-size="10.00">actors: HashMap&lt;ActorAddress, ActorSlot&gt;</text>
</g>
<!-- Worker&#45;&gt;ActorPool -->
<g id="edge5" class="edge">
<title>Worker&#45;&gt;ActorPool</title>
<path fill="none" stroke="#333333" d="M2101,-535.37C2101,-535.37 2101,-434.08 2101,-434.08"/>
<polygon fill="#333333" stroke="#333333" points="2104.5,-434.08 2101,-424.08 2097.5,-434.08 2104.5,-434.08"/>
<text xml:space="preserve" text-anchor="middle" x="2112.51" y="-492.75" font-family="Helvetica,sans-Serif" font-size="9.00">owns</text>
</g>
<!-- Worker&#45;&gt;ReceiverCh -->
<g id="edge16" class="edge">
<title>Worker&#45;&gt;ReceiverCh</title>
<path fill="none" stroke="#333333" d="M2226.72,-539C2246.72,-539 2261.01,-539 2261.01,-539 2261.01,-539 2261.01,-433.86 2261.01,-433.86"/>
<polygon fill="#333333" stroke="#333333" points="2264.51,-433.86 2261.01,-423.86 2257.51,-433.86 2264.51,-433.86"/>
<text xml:space="preserve" text-anchor="middle" x="2433.39" y="-498.15" font-family="Helvetica,sans-Serif" font-size="9.00">transfer_rx +</text>
<text xml:space="preserve" text-anchor="middle" x="2433.39" y="-487.35" font-family="Helvetica,sans-Serif" font-size="9.00">spawn_rx</text>
</g>
<!-- Worker&#45;&gt;WorkerStats -->
<g id="edge22" class="edge">
<title>Worker&#45;&gt;WorkerStats</title>
<path fill="none" stroke="#e67300" stroke-width="2.5" d="M2227.04,-543C2402.39,-543 2701.65,-543 2701.65,-543 2701.65,-543 2701.65,-458.42 2701.65,-458.42"/>
<polygon fill="#e67300" stroke="#e67300" stroke-width="2.5" points="2705.15,-458.42 2701.65,-448.42 2698.15,-458.42 2705.15,-458.42"/>
<text xml:space="preserve" text-anchor="middle" x="2728.75" y="-492.75" font-family="Helvetica,sans-Serif" font-size="9.00">Arc</text>
</g>
<!-- WorkerContext -->
<g id="node20" class="node">
<title>WorkerContext</title>
<polygon fill="white" stroke="black" points="1979.05,-1054.15 1979.05,-1150.15 2170.95,-1150.15 2170.95,-1054.15 1979.05,-1054.15"/>
<polygon fill="#c0392b" stroke="none" points="1987.05,-1124.15 1987.05,-1146.15 2162.95,-1146.15 2162.95,-1124.15 1987.05,-1124.15"/>
<polygon fill="none" stroke="black" points="1987.05,-1124.15 1987.05,-1146.15 2162.95,-1146.15 2162.95,-1124.15 1987.05,-1124.15"/>
<text xml:space="preserve" text-anchor="start" x="2029.35" y="-1133.15" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00" fill="white">WorkerContext&lt;&#39;a&gt;</text>
<polygon fill="none" stroke="black" points="1987.05,-1102.15 1987.05,-1124.15 2162.95,-1124.15 2162.95,-1102.15 1987.05,-1102.15"/>
<text xml:space="preserve" text-anchor="start" x="1992.05" y="-1110.15" font-family="Helvetica,sans-Serif" font-size="10.00">worker_id: WorkerId</text>
<polygon fill="none" stroke="black" points="1987.05,-1080.15 1987.05,-1102.15 2162.95,-1102.15 2162.95,-1080.15 1987.05,-1080.15"/>
<text xml:space="preserve" text-anchor="start" x="1992.05" y="-1088.15" font-family="Helvetica,sans-Serif" font-size="10.00">tc: &amp;&#39;a TickContext&lt;&#39;a&gt;</text>
<polygon fill="none" stroke="black" points="1987.05,-1058.15 1987.05,-1080.15 2162.95,-1080.15 2162.95,-1058.15 1987.05,-1058.15"/>
<text xml:space="preserve" text-anchor="start" x="1992.05" y="-1066.15" font-family="Helvetica,sans-Serif" font-size="10.00">pending_local: &amp;&#39;a RefCell&lt;Vec&lt;...&gt;&gt;</text>
</g>
<!-- WorkerContext&#45;&gt;ContextInner -->
<g id="edge36" class="edge">
<title>WorkerContext&#45;&gt;ContextInner</title>
<path fill="none" stroke="#2e8b57" stroke-width="1.5" stroke-dasharray="1,5" d="M1978.68,-1062C1659.63,-1062 657.46,-1062 657.46,-1062 657.46,-1062 657.46,-658.51 657.46,-658.51"/>
<polygon fill="none" stroke="#2e8b57" stroke-width="1.5" points="660.96,-658.51 657.46,-648.51 653.96,-658.51 660.96,-658.51"/>
<text xml:space="preserve" text-anchor="middle" x="707.25" y="-867.65" font-family="Helvetica,sans-Serif" font-size="9.00">impl</text>
</g>
<!-- WorkerContext&#45;&gt;TickContext -->
<g id="edge32" class="edge">
<title>WorkerContext&#45;&gt;TickContext</title>
<path fill="none" stroke="#7b2d8b" stroke-width="1.5" stroke-dasharray="5,2" d="M1978.71,-1059C1915.32,-1059 1844,-1059 1844,-1059 1844,-1059 1844,-963.99 1844,-963.99"/>
<polygon fill="#7b2d8b" stroke="#7b2d8b" stroke-width="1.5" points="1847.5,-963.99 1844,-953.99 1840.5,-963.99 1847.5,-963.99"/>
<text xml:space="preserve" text-anchor="middle" x="1965.36" y="-1016.55" font-family="Helvetica,sans-Serif" font-size="9.00">&amp;&#39;a</text>
</g>
<!-- ActorSlot -->
<g id="node22" class="node">
<title>ActorSlot</title>
<polygon fill="white" stroke="black" points="1981.91,-193.35 1981.91,-267.35 2206.09,-267.35 2206.09,-193.35 1981.91,-193.35"/>
<polygon fill="#c0392b" stroke="none" points="1989.91,-241.35 1989.91,-263.35 2198.09,-263.35 2198.09,-241.35 1989.91,-241.35"/>
<polygon fill="none" stroke="black" points="1989.91,-241.35 1989.91,-263.35 2198.09,-263.35 2198.09,-241.35 1989.91,-241.35"/>
<text xml:space="preserve" text-anchor="start" x="2071.5" y="-250.35" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00" fill="white">ActorSlot</text>
<polygon fill="none" stroke="black" points="1989.91,-219.35 1989.91,-241.35 2198.09,-241.35 2198.09,-219.35 1989.91,-219.35"/>
<text xml:space="preserve" text-anchor="start" x="1994.91" y="-227.35" font-family="Helvetica,sans-Serif" font-size="10.00">mailbox: VecDeque&lt;Box&lt;dyn Any + Send&gt;&gt;</text>
<polygon fill="none" stroke="black" points="1989.91,-197.35 1989.91,-219.35 2198.09,-219.35 2198.09,-197.35 1989.91,-197.35"/>
<text xml:space="preserve" text-anchor="start" x="1994.91" y="-205.35" font-family="Helvetica,sans-Serif" font-size="10.00">actor: Box&lt;dyn AnyActor&gt;</text>
</g>
<!-- ActorPool&#45;&gt;ActorSlot -->
<g id="edge7" class="edge">
<title>ActorPool&#45;&gt;ActorSlot</title>
<path fill="none" stroke="#333333" d="M2098.69,-369.87C2098.69,-369.87 2098.69,-279.11 2098.69,-279.11"/>
<polygon fill="#333333" stroke="#333333" points="2102.19,-279.11 2098.69,-269.11 2095.19,-279.11 2102.19,-279.11"/>
<text xml:space="preserve" text-anchor="middle" x="2134.02" y="-299.55" font-family="Helvetica,sans-Serif" font-size="9.00">HashMap&lt;Addr, _&gt;</text>
</g>
<!-- ActorSlot&#45;&gt;AnyActor -->
<g id="edge8" class="edge">
<title>ActorSlot&#45;&gt;AnyActor</title>
<path fill="none" stroke="#333333" d="M1981.51,-198C1645.39,-198 671.76,-198 671.76,-198 671.76,-198 671.76,-106.52 671.76,-106.52"/>
<polygon fill="#333333" stroke="#333333" points="675.26,-106.52 671.76,-96.52 668.26,-106.52 675.26,-106.52"/>
<text xml:space="preserve" text-anchor="middle" x="675.27" y="-155.75" font-family="Helvetica,sans-Serif" font-size="9.00">Box&lt;dyn&gt;</text>
</g>
<!-- HybridChannel -->
<g id="node23" class="node">
<title>HybridChannel</title>
<polygon fill="white" stroke="black" points="2258.8,-193.35 2258.8,-267.35 2393.2,-267.35 2393.2,-193.35 2258.8,-193.35"/>
<polygon fill="#148f77" stroke="none" points="2266.8,-241.35 2266.8,-263.35 2385.2,-263.35 2385.2,-241.35 2266.8,-241.35"/>
<polygon fill="none" stroke="black" points="2266.8,-241.35 2266.8,-263.35 2385.2,-263.35 2385.2,-241.35 2266.8,-241.35"/>
<text xml:space="preserve" text-anchor="start" x="2281.55" y="-250.35" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00" fill="white">HybridChannel&lt;T&gt;</text>
<polygon fill="none" stroke="black" points="2266.8,-219.35 2266.8,-241.35 2385.2,-241.35 2385.2,-219.35 2266.8,-219.35"/>
<text xml:space="preserve" text-anchor="start" x="2271.8" y="-227.35" font-family="Helvetica,sans-Serif" font-size="10.00">ring: ArrayQueue&lt;T&gt;</text>
<polygon fill="none" stroke="black" points="2266.8,-197.35 2266.8,-219.35 2385.2,-219.35 2385.2,-197.35 2266.8,-197.35"/>
<text xml:space="preserve" text-anchor="start" x="2271.8" y="-205.35" font-family="Helvetica,sans-Serif" font-size="10.00">overflow: SegQueue&lt;T&gt;</text>
</g>
<!-- HybridChannel&#45;&gt;HybridChannel -->
<g id="edge17" class="edge">
<title>HybridChannel&#45;&gt;HybridChannel</title>
<path fill="none" stroke="#333333" d="M2303.6,-267.43C2303.6,-284.92 2303.6,-302 2303.6,-302 2303.6,-302 2254.91,-302 2254.91,-302 2254.91,-302 2254.91,-158 2254.91,-158 2254.91,-158 2326,-158 2326,-158 2326,-158 2326,-175.25 2326,-192.9"/>
</g>
<!-- SenderCh&#45;&gt;SenderT -->
<g id="edge37" class="edge">
<title>SenderCh&#45;&gt;SenderT</title>
<path fill="none" stroke="#2e8b57" stroke-width="1.5" stroke-dasharray="1,5" d="M2343.5,-579.5C2343.5,-546.75 2343.5,-495 2343.5,-495 2343.5,-495 1864.68,-495 1864.68,-495 1864.68,-495 1864.68,-437.27 1864.68,-437.27"/>
<polygon fill="none" stroke="#2e8b57" stroke-width="1.5" points="1868.18,-437.27 1864.68,-427.27 1861.18,-437.27 1868.18,-437.27"/>
<text xml:space="preserve" text-anchor="middle" x="2295.52" y="-498.15" font-family="Helvetica,sans-Serif" font-size="9.00">impl for</text>
<text xml:space="preserve" text-anchor="middle" x="2295.52" y="-487.35" font-family="Helvetica,sans-Serif" font-size="9.00">Sender&lt;M&gt;</text>
</g>
<!-- SenderCh&#45;&gt;HybridChannel -->
<g id="edge23" class="edge">
<title>SenderCh&#45;&gt;HybridChannel</title>
<path fill="none" stroke="#e67300" stroke-width="2.5" d="M2428.49,-579.32C2428.49,-493.8 2428.49,-230 2428.49,-230 2428.49,-230 2407.29,-230 2407.29,-230"/>
<polygon fill="#e67300" stroke="#e67300" stroke-width="2.5" points="2407.29,-226.5 2397.29,-230 2407.29,-233.5 2407.29,-226.5"/>
<text xml:space="preserve" text-anchor="middle" x="2485.75" y="-393.45" font-family="Helvetica,sans-Serif" font-size="9.00">Arc</text>
</g>
<!-- ReceiverCh&#45;&gt;HybridChannel -->
<g id="edge24" class="edge">
<title>ReceiverCh&#45;&gt;HybridChannel</title>
<path fill="none" stroke="#e67300" stroke-width="2.5" d="M2348.4,-369.87C2348.4,-369.87 2348.4,-281.38 2348.4,-281.38"/>
<polygon fill="#e67300" stroke="#e67300" stroke-width="2.5" points="2351.9,-281.38 2348.4,-271.38 2344.9,-281.38 2351.9,-281.38"/>
<text xml:space="preserve" text-anchor="middle" x="2337.75" y="-299.55" font-family="Helvetica,sans-Serif" font-size="9.00">Arc</text>
</g>
<!-- WorkerInfo -->
<g id="node27" class="node">
<title>WorkerInfo</title>
<polygon fill="white" stroke="black" points="2714.24,-811.35 2714.24,-929.35 2859.76,-929.35 2859.76,-811.35 2714.24,-811.35"/>
<polygon fill="#3949ab" stroke="none" points="2722.24,-903.35 2722.24,-925.35 2851.76,-925.35 2851.76,-903.35 2722.24,-903.35"/>
<polygon fill="none" stroke="black" points="2722.24,-903.35 2722.24,-925.35 2851.76,-925.35 2851.76,-903.35 2722.24,-903.35"/>
<text xml:space="preserve" text-anchor="start" x="2760.61" y="-912.35" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00" fill="white">WorkerInfo</text>
<polygon fill="none" stroke="black" points="2722.24,-881.35 2722.24,-903.35 2851.76,-903.35 2851.76,-881.35 2722.24,-881.35"/>
<text xml:space="preserve" text-anchor="start" x="2727.24" y="-889.35" font-family="Helvetica,sans-Serif" font-size="10.00">id: usize</text>
<polygon fill="none" stroke="black" points="2722.24,-859.35 2722.24,-881.35 2851.76,-881.35 2851.76,-859.35 2722.24,-859.35"/>
<text xml:space="preserve" text-anchor="start" x="2727.24" y="-867.35" font-family="Helvetica,sans-Serif" font-size="10.00">num_actors: usize</text>
<polygon fill="none" stroke="black" points="2722.24,-837.35 2722.24,-859.35 2851.76,-859.35 2851.76,-837.35 2722.24,-837.35"/>
<text xml:space="preserve" text-anchor="start" x="2727.24" y="-845.35" font-family="Helvetica,sans-Serif" font-size="10.00">mailbox_depth: usize</text>
<polygon fill="none" stroke="black" points="2722.24,-815.35 2722.24,-837.35 2851.76,-837.35 2851.76,-815.35 2722.24,-815.35"/>
<text xml:space="preserve" text-anchor="start" x="2727.24" y="-823.35" font-family="Helvetica,sans-Serif" font-size="10.00">messages_processed: u64</text>
</g>
<!-- RuntimeStats -->
<g id="node28" class="node">
<title>RuntimeStats</title>
<polygon fill="white" stroke="black" points="2695.36,-1054.15 2695.36,-1150.15 2878.64,-1150.15 2878.64,-1054.15 2695.36,-1054.15"/>
<polygon fill="#3949ab" stroke="none" points="2703.36,-1124.15 2703.36,-1146.15 2870.64,-1146.15 2870.64,-1124.15 2703.36,-1124.15"/>
<polygon fill="none" stroke="black" points="2703.36,-1124.15 2703.36,-1146.15 2870.64,-1146.15 2870.64,-1124.15 2703.36,-1124.15"/>
<text xml:space="preserve" text-anchor="start" x="2754.77" y="-1133.15" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00" fill="white">RuntimeStats</text>
<polygon fill="none" stroke="black" points="2703.36,-1102.15 2703.36,-1124.15 2870.64,-1124.15 2870.64,-1102.15 2703.36,-1102.15"/>
<text xml:space="preserve" text-anchor="start" x="2708.36" y="-1110.15" font-family="Helvetica,sans-Serif" font-size="10.00">num_workers: usize</text>
<polygon fill="none" stroke="black" points="2703.36,-1080.15 2703.36,-1102.15 2870.64,-1102.15 2870.64,-1080.15 2703.36,-1080.15"/>
<text xml:space="preserve" text-anchor="start" x="2708.36" y="-1088.15" font-family="Helvetica,sans-Serif" font-size="10.00">actors: Vec&lt;(ActorAddress, usize)&gt;</text>
<polygon fill="none" stroke="black" points="2703.36,-1058.15 2703.36,-1080.15 2870.64,-1080.15 2870.64,-1058.15 2703.36,-1058.15"/>
<text xml:space="preserve" text-anchor="start" x="2708.36" y="-1066.15" font-family="Helvetica,sans-Serif" font-size="10.00">workers: Vec&lt;WorkerInfo&gt;</text>
</g>
<!-- RuntimeStats&#45;&gt;WorkerInfo -->
<g id="edge4" class="edge">
<title>RuntimeStats&#45;&gt;WorkerInfo</title>
<path fill="none" stroke="#333333" d="M2787,-1053.84C2787,-1053.84 2787,-941.22 2787,-941.22"/>
<polygon fill="#333333" stroke="#333333" points="2790.5,-941.22 2787,-931.22 2783.5,-941.22 2790.5,-941.22"/>
<text xml:space="preserve" text-anchor="middle" x="2800.01" y="-1016.55" font-family="Helvetica,sans-Serif" font-size="9.00">Vec&lt;&gt;</text>
</g>
<!-- BackoffPolicy -->
<g id="node30" class="node">
<title>BackoffPolicy</title>
<polygon fill="white" stroke="black" points="2525.13,-337.15 2525.13,-455.15 2662.87,-455.15 2662.87,-337.15 2525.13,-337.15"/>
<polygon fill="#d4ac0d" stroke="none" points="2533.13,-429.15 2533.13,-451.15 2654.87,-451.15 2654.87,-429.15 2533.13,-429.15"/>
<polygon fill="none" stroke="black" points="2533.13,-429.15 2533.13,-451.15 2654.87,-451.15 2654.87,-429.15 2533.13,-429.15"/>
<text xml:space="preserve" text-anchor="start" x="2560.93" y="-438.15" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00" fill="white">BackoffPolicy</text>
<polygon fill="none" stroke="black" points="2533.13,-407.15 2533.13,-429.15 2654.87,-429.15 2654.87,-407.15 2533.13,-407.15"/>
<text xml:space="preserve" text-anchor="start" x="2538.13" y="-415.15" font-family="Helvetica,sans-Serif" font-size="10.00">spin_threshold: u32</text>
<polygon fill="none" stroke="black" points="2533.13,-385.15 2533.13,-407.15 2654.87,-407.15 2654.87,-385.15 2533.13,-385.15"/>
<text xml:space="preserve" text-anchor="start" x="2538.13" y="-393.15" font-family="Helvetica,sans-Serif" font-size="10.00">yield_threshold: u32</text>
<polygon fill="none" stroke="black" points="2533.13,-363.15 2533.13,-385.15 2654.87,-385.15 2654.87,-363.15 2533.13,-363.15"/>
<text xml:space="preserve" text-anchor="start" x="2538.13" y="-371.15" font-family="Helvetica,sans-Serif" font-size="10.00">sleep_increment_us: u64</text>
<polygon fill="none" stroke="black" points="2533.13,-341.15 2533.13,-363.15 2654.87,-363.15 2654.87,-341.15 2533.13,-341.15"/>
<text xml:space="preserve" text-anchor="start" x="2538.13" y="-349.15" font-family="Helvetica,sans-Serif" font-size="10.00">sleep_max_us: u64</text>
</g>
<!-- RuntimeConfig&#45;&gt;BackoffPolicy -->
<g id="edge3" class="edge">
<title>RuntimeConfig&#45;&gt;BackoffPolicy</title>
<path fill="none" stroke="#333333" d="M2594,-546.52C2594,-546.52 2594,-467.1 2594,-467.1"/>
<polygon fill="#333333" stroke="#333333" points="2597.5,-467.1 2594,-457.1 2590.5,-467.1 2597.5,-467.1"/>
<text xml:space="preserve" text-anchor="middle" x="2604.51" y="-492.75" font-family="Helvetica,sans-Serif" font-size="9.00">owns</text>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 62 KiB

View file

@ -0,0 +1,806 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Generated by graphviz version 14.1.1 (20251213.1925)
-->
<!-- Title: dataflow Pages: 1 -->
<svg width="2635pt" height="973pt"
viewBox="0.00 0.00 2635.00 973.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 969)">
<title>dataflow</title>
<polygon fill="white" stroke="none" points="-4,4 -4,-969 2630.68,-969 2630.68,4 -4,4"/>
<g id="clust1" class="cluster">
<title>cluster_legend</title>
<path fill="none" stroke="#888888" d="M20,-8C20,-8 251.44,-8 251.44,-8 257.44,-8 263.44,-14 263.44,-20 263.44,-20 263.44,-165 263.44,-165 263.44,-171 257.44,-177 251.44,-177 251.44,-177 20,-177 20,-177 14,-177 8,-171 8,-165 8,-165 8,-20 8,-20 8,-14 14,-8 20,-8"/>
<text xml:space="preserve" text-anchor="middle" x="53.9" y="-163.1" font-family="Helvetica,sans-Serif" font-size="11.00">Data on Arrows</text>
</g>
<g id="clust2" class="cluster">
<title>cluster_flow_a</title>
<path fill="#fdedec" stroke="#c0392b" d="M112.72,-185C112.72,-185 2422.02,-185 2422.02,-185 2428.02,-185 2434.02,-191 2434.02,-197 2434.02,-197 2434.02,-254 2434.02,-254 2434.02,-260 2428.02,-266 2422.02,-266 2422.02,-266 112.72,-266 112.72,-266 106.72,-266 100.72,-260 100.72,-254 100.72,-254 100.72,-197 100.72,-197 100.72,-191 106.72,-185 112.72,-185"/>
<text xml:space="preserve" text-anchor="start" x="108.72" y="-252.1" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#922b21">FLOW A — Cross&#45;Worker Message Send</text>
</g>
<g id="clust3" class="cluster">
<title>cluster_flow_b</title>
<path fill="#ebf5fb" stroke="#2980b9" d="M81.25,-274C81.25,-274 1999.84,-274 1999.84,-274 2005.84,-274 2011.84,-280 2011.84,-286 2011.84,-286 2011.84,-355 2011.84,-355 2011.84,-361 2005.84,-367 1999.84,-367 1999.84,-367 81.25,-367 81.25,-367 75.25,-367 69.25,-361 69.25,-355 69.25,-355 69.25,-286 69.25,-286 69.25,-280 75.25,-274 81.25,-274"/>
<text xml:space="preserve" text-anchor="start" x="77.25" y="-353.1" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#1a5276">FLOW B — Same&#45;Worker Message Send (during tick)</text>
</g>
<g id="clust4" class="cluster">
<title>cluster_flow_c</title>
<path fill="#eafaf1" stroke="#27ae60" d="M92.26,-375C92.26,-375 2606.68,-375 2606.68,-375 2612.68,-375 2618.68,-381 2618.68,-387 2618.68,-387 2618.68,-446 2618.68,-446 2618.68,-452 2612.68,-458 2606.68,-458 2606.68,-458 92.26,-458 92.26,-458 86.26,-458 80.26,-452 80.26,-446 80.26,-446 80.26,-387 80.26,-387 80.26,-381 86.26,-375 92.26,-375"/>
<text xml:space="preserve" text-anchor="start" x="88.26" y="-444.1" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#1e8449">FLOW C — Actor Spawn</text>
</g>
<g id="clust5" class="cluster">
<title>cluster_flow_d</title>
<path fill="#e8f8f5" stroke="#117864" d="M93.09,-466C93.09,-466 1687.49,-466 1687.49,-466 1693.49,-466 1699.49,-472 1699.49,-478 1699.49,-478 1699.49,-651 1699.49,-651 1699.49,-657 1693.49,-663 1687.49,-663 1687.49,-663 93.09,-663 93.09,-663 87.09,-663 81.09,-657 81.09,-651 81.09,-651 81.09,-478 81.09,-478 81.09,-472 87.09,-466 93.09,-466"/>
<text xml:space="preserve" text-anchor="start" x="89.09" y="-649.1" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#0e6655">FLOW D — External Inbox</text>
</g>
<g id="clust6" class="cluster">
<title>cluster_flow_e</title>
<path fill="#f4ecf7" stroke="#6c3483" d="M95.31,-671C95.31,-671 2253.94,-671 2253.94,-671 2259.94,-671 2265.94,-677 2265.94,-683 2265.94,-683 2265.94,-945 2265.94,-945 2265.94,-951 2259.94,-957 2253.94,-957 2253.94,-957 95.31,-957 95.31,-957 89.31,-957 83.31,-951 83.31,-945 83.31,-945 83.31,-683 83.31,-683 83.31,-677 89.31,-671 95.31,-671"/>
<text xml:space="preserve" text-anchor="start" x="91.31" y="-943.1" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#6c3483">FLOW E — The 6&#45;Phase Tick Cycle (Worker::tick_once)</text>
</g>
<!-- dlegend -->
<g id="node1" class="node">
<title>dlegend</title>
<text xml:space="preserve" text-anchor="start" x="30" y="-130" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00" fill="#c0392b">━━━</text>
<text xml:space="preserve" text-anchor="start" x="63" y="-129" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#c0392b">Envelope (dest + payload)</text>
<text xml:space="preserve" text-anchor="start" x="30" y="-110" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00" fill="#27ae60">━━━</text>
<text xml:space="preserve" text-anchor="start" x="63" y="-109" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#27ae60">Spawn tuple (addr, Box&lt;dyn AnyActor&gt;)</text>
<text xml:space="preserve" text-anchor="start" x="30" y="-90" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00" fill="#2980b9">━━━</text>
<text xml:space="preserve" text-anchor="start" x="63" y="-89" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#2980b9">Pending local (addr, Box&lt;dyn Any&gt;)</text>
<text xml:space="preserve" text-anchor="start" x="30" y="-70" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00" fill="#8e44ad">━━━</text>
<text xml:space="preserve" text-anchor="start" x="63" y="-69" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#8e44ad">Box&lt;dyn Any + Send&gt; (raw message)</text>
<text xml:space="preserve" text-anchor="start" x="30" y="-50" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00" fill="#e67300">━━━</text>
<text xml:space="preserve" text-anchor="start" x="63" y="-49" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e67300">Stats (atomics)</text>
<text xml:space="preserve" text-anchor="start" x="30" y="-30" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00" fill="#117864">━━━</text>
<text xml:space="preserve" text-anchor="start" x="63" y="-29" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#117864">WorkerId (lookup result)</text>
</g>
<!-- a_caller -->
<g id="node2" class="node">
<title>a_caller</title>
<ellipse fill="#f5b7b1" stroke="black" cx="135.72" cy="-215" rx="27" ry="18"/>
<text xml:space="preserve" text-anchor="middle" x="135.72" y="-212" font-family="Helvetica,sans-Serif" font-size="10.00">Caller</text>
</g>
<!-- a_send -->
<g id="node3" class="node">
<title>a_send</title>
<path fill="white" stroke="black" d="M494.57,-233C494.57,-233 410.31,-233 410.31,-233 404.31,-233 398.31,-227 398.31,-221 398.31,-221 398.31,-209 398.31,-209 398.31,-203 404.31,-197 410.31,-197 410.31,-197 494.57,-197 494.57,-197 500.57,-197 506.57,-203 506.57,-209 506.57,-209 506.57,-221 506.57,-221 506.57,-227 500.57,-233 494.57,-233"/>
<text xml:space="preserve" text-anchor="middle" x="452.44" y="-218" font-family="Helvetica,sans-Serif" font-size="10.00">Runtime</text>
<text xml:space="preserve" text-anchor="middle" x="452.44" y="-206" font-family="Helvetica,sans-Serif" font-size="10.00">::send_to(addr, msg)</text>
</g>
<!-- a_caller&#45;&gt;a_send -->
<g id="edge1" class="edge">
<title>a_caller&#45;&gt;a_send</title>
<path fill="none" stroke="#333333" d="M163.01,-215C211.85,-215 317.87,-215 386.87,-215"/>
<polygon fill="#333333" stroke="#333333" points="386.52,-218.5 396.52,-215 386.52,-211.5 386.52,-218.5"/>
</g>
<!-- a_lookup -->
<g id="node4" class="node">
<title>a_lookup</title>
<path fill="#e8daef" stroke="black" d="M750.59,-233C750.59,-233 696.89,-233 696.89,-233 690.89,-233 684.89,-227 684.89,-221 684.89,-221 684.89,-209 684.89,-209 684.89,-203 690.89,-197 696.89,-197 696.89,-197 750.59,-197 750.59,-197 756.59,-197 762.59,-203 762.59,-209 762.59,-209 762.59,-221 762.59,-221 762.59,-227 756.59,-233 750.59,-233"/>
<text xml:space="preserve" text-anchor="middle" x="723.74" y="-218" font-family="Helvetica,sans-Serif" font-size="10.00">AddressMap</text>
<text xml:space="preserve" text-anchor="middle" x="723.74" y="-206" font-family="Helvetica,sans-Serif" font-size="10.00">::lookup(addr)</text>
</g>
<!-- a_send&#45;&gt;a_lookup -->
<g id="edge2" class="edge">
<title>a_send&#45;&gt;a_lookup</title>
<path fill="none" stroke="#333333" d="M506.97,-215C555.33,-215 625.95,-215 673.08,-215"/>
<polygon fill="#333333" stroke="#333333" points="673.01,-218.5 683.01,-215 673.01,-211.5 673.01,-218.5"/>
</g>
<!-- a_box -->
<g id="node5" class="node">
<title>a_box</title>
<path fill="white" stroke="black" d="M1065.68,-233C1065.68,-233 1007,-233 1007,-233 1001,-233 995,-227 995,-221 995,-221 995,-209 995,-209 995,-203 1001,-197 1007,-197 1007,-197 1065.68,-197 1065.68,-197 1071.68,-197 1077.68,-203 1077.68,-209 1077.68,-209 1077.68,-221 1077.68,-221 1077.68,-227 1071.68,-233 1065.68,-233"/>
<text xml:space="preserve" text-anchor="middle" x="1036.34" y="-218" font-family="Helvetica,sans-Serif" font-size="10.00">Box::new(msg)</text>
<text xml:space="preserve" text-anchor="middle" x="1036.34" y="-206" font-family="Helvetica,sans-Serif" font-size="10.00">→ Envelope</text>
</g>
<!-- a_lookup&#45;&gt;a_box -->
<g id="edge3" class="edge">
<title>a_lookup&#45;&gt;a_box</title>
<path fill="none" stroke="#117864" d="M762.83,-215C818.26,-215 921.33,-215 983.59,-215"/>
<polygon fill="#117864" stroke="#117864" points="983.21,-218.5 993.21,-215 983.21,-211.5 983.21,-218.5"/>
<text xml:space="preserve" text-anchor="middle" x="869.07" y="-217.7" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#117864">WorkerId</text>
</g>
<!-- a_tx -->
<g id="node6" class="node">
<title>a_tx</title>
<path fill="#d1f2eb" stroke="black" d="M1347.65,-237C1347.65,-237 1303.41,-237 1303.41,-237 1297.41,-237 1291.41,-231 1291.41,-225 1291.41,-225 1291.41,-205 1291.41,-205 1291.41,-199 1297.41,-193 1303.41,-193 1303.41,-193 1347.65,-193 1347.65,-193 1353.65,-193 1359.65,-199 1359.65,-205 1359.65,-205 1359.65,-225 1359.65,-225 1359.65,-231 1353.65,-237 1347.65,-237"/>
<text xml:space="preserve" text-anchor="middle" x="1325.53" y="-224" font-family="Helvetica,sans-Serif" font-size="10.00">transfer_txs</text>
<text xml:space="preserve" text-anchor="middle" x="1325.53" y="-212" font-family="Helvetica,sans-Serif" font-size="10.00">[worker_id]</text>
<text xml:space="preserve" text-anchor="middle" x="1325.53" y="-200" font-family="Helvetica,sans-Serif" font-size="10.00">.try_send()</text>
</g>
<!-- a_box&#45;&gt;a_tx -->
<g id="edge4" class="edge">
<title>a_box&#45;&gt;a_tx</title>
<path fill="none" stroke="#c0392b" stroke-width="2" d="M1077.88,-215C1130.73,-215 1222.95,-215 1278.41,-215"/>
<polygon fill="#c0392b" stroke="#c0392b" stroke-width="2" points="1278.31,-218.5 1288.31,-215 1278.31,-211.5 1278.31,-218.5"/>
<text xml:space="preserve" text-anchor="middle" x="1195.12" y="-217.7" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#c0392b">Envelope</text>
</g>
<!-- a_hybrid -->
<g id="node7" class="node">
<title>a_hybrid</title>
<path fill="#d1f2eb" stroke="black" d="M1682.32,-233C1682.32,-235.21 1663.54,-237 1640.42,-237 1617.3,-237 1598.51,-235.21 1598.51,-233 1598.51,-233 1598.51,-197 1598.51,-197 1598.51,-194.79 1617.3,-193 1640.42,-193 1663.54,-193 1682.32,-194.79 1682.32,-197 1682.32,-197 1682.32,-233 1682.32,-233"/>
<path fill="none" stroke="black" d="M1682.32,-233C1682.32,-230.79 1663.54,-229 1640.42,-229 1617.3,-229 1598.51,-230.79 1598.51,-233"/>
<text xml:space="preserve" text-anchor="middle" x="1640.42" y="-218" font-family="Helvetica,sans-Serif" font-size="10.00">HybridChannel</text>
<text xml:space="preserve" text-anchor="middle" x="1640.42" y="-206" font-family="Helvetica,sans-Serif" font-size="10.00">ring → overflow</text>
</g>
<!-- a_tx&#45;&gt;a_hybrid -->
<g id="edge5" class="edge">
<title>a_tx&#45;&gt;a_hybrid</title>
<path fill="none" stroke="#c0392b" stroke-width="2" d="M1360.02,-215C1413.92,-215 1520.45,-215 1585.26,-215"/>
<polygon fill="#c0392b" stroke="#c0392b" stroke-width="2" points="1585.01,-218.5 1595.01,-215 1585.01,-211.5 1585.01,-218.5"/>
<text xml:space="preserve" text-anchor="middle" x="1471.7" y="-217.7" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#c0392b">Envelope</text>
</g>
<!-- a_rx -->
<g id="node8" class="node">
<title>a_rx</title>
<path fill="#fadbd8" stroke="black" d="M1986.84,-237C1986.84,-237 1947.04,-237 1947.04,-237 1941.04,-237 1935.04,-231 1935.04,-225 1935.04,-225 1935.04,-205 1935.04,-205 1935.04,-199 1941.04,-193 1947.04,-193 1947.04,-193 1986.84,-193 1986.84,-193 1992.84,-193 1998.84,-199 1998.84,-205 1998.84,-205 1998.84,-225 1998.84,-225 1998.84,-231 1992.84,-237 1986.84,-237"/>
<text xml:space="preserve" text-anchor="middle" x="1966.94" y="-224" font-family="Helvetica,sans-Serif" font-size="10.00">Worker</text>
<text xml:space="preserve" text-anchor="middle" x="1966.94" y="-212" font-family="Helvetica,sans-Serif" font-size="10.00">transfer_rx</text>
<text xml:space="preserve" text-anchor="middle" x="1966.94" y="-200" font-family="Helvetica,sans-Serif" font-size="10.00">.try_recv()</text>
</g>
<!-- a_hybrid&#45;&gt;a_rx -->
<g id="edge6" class="edge">
<title>a_hybrid&#45;&gt;a_rx</title>
<path fill="none" stroke="#c0392b" stroke-width="2" d="M1682.52,-215C1743.85,-215 1858.85,-215 1921.87,-215"/>
<polygon fill="#c0392b" stroke="#c0392b" stroke-width="2" points="1921.83,-218.5 1931.83,-215 1921.83,-211.5 1921.83,-218.5"/>
<text xml:space="preserve" text-anchor="middle" x="1805.63" y="-217.7" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#c0392b">Envelope</text>
</g>
<!-- a_deliver -->
<g id="node9" class="node">
<title>a_deliver</title>
<path fill="#fadbd8" stroke="black" d="M2245.95,-233C2245.95,-233 2192.26,-233 2192.26,-233 2186.26,-233 2180.26,-227 2180.26,-221 2180.26,-221 2180.26,-209 2180.26,-209 2180.26,-203 2186.26,-197 2192.26,-197 2192.26,-197 2245.95,-197 2245.95,-197 2251.95,-197 2257.95,-203 2257.95,-209 2257.95,-209 2257.95,-221 2257.95,-221 2257.95,-227 2251.95,-233 2245.95,-233"/>
<text xml:space="preserve" text-anchor="middle" x="2219.1" y="-218" font-family="Helvetica,sans-Serif" font-size="10.00">ActorPool</text>
<text xml:space="preserve" text-anchor="middle" x="2219.1" y="-206" font-family="Helvetica,sans-Serif" font-size="10.00">::deliver(addr)</text>
</g>
<!-- a_rx&#45;&gt;a_deliver -->
<g id="edge7" class="edge">
<title>a_rx&#45;&gt;a_deliver</title>
<path fill="none" stroke="#c0392b" stroke-width="2" d="M1999.12,-215C2041.2,-215 2116.7,-215 2167.11,-215"/>
<polygon fill="#c0392b" stroke="#c0392b" stroke-width="2" points="2166.95,-218.5 2176.95,-215 2166.95,-211.5 2166.95,-218.5"/>
<text xml:space="preserve" text-anchor="middle" x="2125.01" y="-217.7" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#c0392b">Envelope</text>
</g>
<!-- a_mailbox -->
<g id="node10" class="node">
<title>a_mailbox</title>
<path fill="#fadbd8" stroke="black" d="M2414.02,-237C2414.02,-237 2364.21,-237 2364.21,-237 2358.21,-237 2352.21,-231 2352.21,-225 2352.21,-225 2352.21,-205 2352.21,-205 2352.21,-199 2358.21,-193 2364.21,-193 2364.21,-193 2414.02,-193 2414.02,-193 2420.02,-193 2426.02,-199 2426.02,-205 2426.02,-205 2426.02,-225 2426.02,-225 2426.02,-231 2420.02,-237 2414.02,-237"/>
<text xml:space="preserve" text-anchor="middle" x="2389.11" y="-224" font-family="Helvetica,sans-Serif" font-size="10.00">ActorSlot</text>
<text xml:space="preserve" text-anchor="middle" x="2389.11" y="-212" font-family="Helvetica,sans-Serif" font-size="10.00">mailbox</text>
<text xml:space="preserve" text-anchor="middle" x="2389.11" y="-200" font-family="Helvetica,sans-Serif" font-size="10.00">.push_back()</text>
</g>
<!-- a_deliver&#45;&gt;a_mailbox -->
<g id="edge8" class="edge">
<title>a_deliver&#45;&gt;a_mailbox</title>
<path fill="none" stroke="#8e44ad" stroke-width="1.5" d="M2258.23,-215C2282.36,-215 2313.77,-215 2339.66,-215"/>
<polygon fill="#8e44ad" stroke="#8e44ad" stroke-width="1.5" points="2339.6,-218.5 2349.6,-215 2339.6,-211.5 2339.6,-218.5"/>
<text xml:space="preserve" text-anchor="middle" x="2306.19" y="-217.7" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#8e44ad">payload</text>
</g>
<!-- b_actor -->
<g id="node11" class="node">
<title>b_actor</title>
<ellipse fill="#aed6f1" stroke="black" cx="135.72" cy="-310" rx="58.47" ry="22.63"/>
<text xml:space="preserve" text-anchor="middle" x="135.72" y="-313" font-family="Helvetica,sans-Serif" font-size="10.00">Actor::handle()</text>
<text xml:space="preserve" text-anchor="middle" x="135.72" y="-301" font-family="Helvetica,sans-Serif" font-size="10.00">calls ctx.send()</text>
</g>
<!-- b_wctx -->
<g id="node12" class="node">
<title>b_wctx</title>
<path fill="#fadbd8" stroke="black" d="M481.78,-328C481.78,-328 423.1,-328 423.1,-328 417.1,-328 411.1,-322 411.1,-316 411.1,-316 411.1,-304 411.1,-304 411.1,-298 417.1,-292 423.1,-292 423.1,-292 481.78,-292 481.78,-292 487.78,-292 493.78,-298 493.78,-304 493.78,-304 493.78,-316 493.78,-316 493.78,-322 487.78,-328 481.78,-328"/>
<text xml:space="preserve" text-anchor="middle" x="452.44" y="-313" font-family="Helvetica,sans-Serif" font-size="10.00">WorkerContext</text>
<text xml:space="preserve" text-anchor="middle" x="452.44" y="-301" font-family="Helvetica,sans-Serif" font-size="10.00">::send_any()</text>
</g>
<!-- b_actor&#45;&gt;b_wctx -->
<g id="edge9" class="edge">
<title>b_actor&#45;&gt;b_wctx</title>
<path fill="none" stroke="#333333" d="M194.44,-310C253.05,-310 342.97,-310 399.43,-310"/>
<polygon fill="#333333" stroke="#333333" points="399.09,-313.5 409.09,-310 399.09,-306.5 399.09,-313.5"/>
</g>
<!-- b_lookup -->
<g id="node13" class="node">
<title>b_lookup</title>
<path fill="#e8daef" stroke="black" d="M750.59,-328C750.59,-328 696.89,-328 696.89,-328 690.89,-328 684.89,-322 684.89,-316 684.89,-316 684.89,-304 684.89,-304 684.89,-298 690.89,-292 696.89,-292 696.89,-292 750.59,-292 750.59,-292 756.59,-292 762.59,-298 762.59,-304 762.59,-304 762.59,-316 762.59,-316 762.59,-322 756.59,-328 750.59,-328"/>
<text xml:space="preserve" text-anchor="middle" x="723.74" y="-313" font-family="Helvetica,sans-Serif" font-size="10.00">AddressMap</text>
<text xml:space="preserve" text-anchor="middle" x="723.74" y="-301" font-family="Helvetica,sans-Serif" font-size="10.00">::lookup(addr)</text>
</g>
<!-- b_wctx&#45;&gt;b_lookup -->
<g id="edge10" class="edge">
<title>b_wctx&#45;&gt;b_lookup</title>
<path fill="none" stroke="#333333" d="M494.05,-310C541.9,-310 621.39,-310 672.92,-310"/>
<polygon fill="#333333" stroke="#333333" points="672.91,-313.5 682.91,-310 672.91,-306.5 672.91,-313.5"/>
</g>
<!-- b_pending -->
<g id="node14" class="node">
<title>b_pending</title>
<path fill="#d6eaf8" stroke="black" d="M1073.46,-332C1073.46,-332 999.22,-332 999.22,-332 993.22,-332 987.22,-326 987.22,-320 987.22,-320 987.22,-300 987.22,-300 987.22,-294 993.22,-288 999.22,-288 999.22,-288 1073.46,-288 1073.46,-288 1079.46,-288 1085.46,-294 1085.46,-300 1085.46,-300 1085.46,-320 1085.46,-320 1085.46,-326 1079.46,-332 1073.46,-332"/>
<text xml:space="preserve" text-anchor="middle" x="1036.34" y="-319" font-family="Helvetica,sans-Serif" font-size="10.00">pending_local</text>
<text xml:space="preserve" text-anchor="middle" x="1036.34" y="-307" font-family="Helvetica,sans-Serif" font-size="10.00">.borrow_mut()</text>
<text xml:space="preserve" text-anchor="middle" x="1036.34" y="-295" font-family="Helvetica,sans-Serif" font-size="10.00">.push((addr, msg))</text>
</g>
<!-- b_lookup&#45;&gt;b_pending -->
<g id="edge11" class="edge">
<title>b_lookup&#45;&gt;b_pending</title>
<path fill="none" stroke="#117864" d="M762.83,-310C815.9,-310 912.63,-310 975.41,-310"/>
<polygon fill="#117864" stroke="#117864" points="975.21,-313.5 985.21,-310 975.21,-306.5 975.21,-313.5"/>
<text xml:space="preserve" text-anchor="middle" x="869.07" y="-312.7" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#117864">same WorkerId</text>
</g>
<!-- b_phase5 -->
<g id="node15" class="node">
<title>b_phase5</title>
<polygon fill="#f9e79f" stroke="black" stroke-width="2" points="1356.31,-338 1294.75,-338 1294.75,-282 1356.31,-282 1356.31,-338"/>
<text xml:space="preserve" text-anchor="middle" x="1325.53" y="-325" font-family="Helvetica,sans-Serif" font-size="10.00">Phase 5:</text>
<text xml:space="preserve" text-anchor="middle" x="1325.53" y="-313" font-family="Helvetica,sans-Serif" font-size="10.00">DELIVER</text>
<text xml:space="preserve" text-anchor="middle" x="1325.53" y="-301" font-family="Helvetica,sans-Serif" font-size="10.00">PENDING</text>
<text xml:space="preserve" text-anchor="middle" x="1325.53" y="-289" font-family="Helvetica,sans-Serif" font-size="10.00">LOCAL</text>
</g>
<!-- b_pending&#45;&gt;b_phase5 -->
<g id="edge12" class="edge">
<title>b_pending&#45;&gt;b_phase5</title>
<path fill="none" stroke="#2980b9" stroke-width="1.5" stroke-dasharray="5,2" d="M1085.73,-310C1140.82,-310 1229.92,-310 1282.3,-310"/>
<polygon fill="#2980b9" stroke="#2980b9" stroke-width="1.5" points="1282.2,-313.5 1292.2,-310 1282.2,-306.5 1282.2,-313.5"/>
<text xml:space="preserve" text-anchor="middle" x="1195.12" y="-312.7" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#2980b9">buffered</text>
</g>
<!-- b_deliver -->
<g id="node16" class="node">
<title>b_deliver</title>
<path fill="#fadbd8" stroke="black" d="M1667.26,-328C1667.26,-328 1613.57,-328 1613.57,-328 1607.57,-328 1601.57,-322 1601.57,-316 1601.57,-316 1601.57,-304 1601.57,-304 1601.57,-298 1607.57,-292 1613.57,-292 1613.57,-292 1667.26,-292 1667.26,-292 1673.26,-292 1679.26,-298 1679.26,-304 1679.26,-304 1679.26,-316 1679.26,-316 1679.26,-322 1673.26,-328 1667.26,-328"/>
<text xml:space="preserve" text-anchor="middle" x="1640.42" y="-313" font-family="Helvetica,sans-Serif" font-size="10.00">ActorPool</text>
<text xml:space="preserve" text-anchor="middle" x="1640.42" y="-301" font-family="Helvetica,sans-Serif" font-size="10.00">::deliver(addr)</text>
</g>
<!-- b_phase5&#45;&gt;b_deliver -->
<g id="edge13" class="edge">
<title>b_phase5&#45;&gt;b_deliver</title>
<path fill="none" stroke="#2980b9" stroke-width="2" d="M1356.53,-310C1410.05,-310 1522.53,-310 1588.4,-310"/>
<polygon fill="#2980b9" stroke="#2980b9" stroke-width="2" points="1588.28,-313.5 1598.28,-310 1588.28,-306.5 1588.28,-313.5"/>
<text xml:space="preserve" text-anchor="middle" x="1471.7" y="-312.7" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#2980b9">(addr, payload)</text>
</g>
<!-- b_mailbox -->
<g id="node17" class="node">
<title>b_mailbox</title>
<path fill="#fadbd8" stroke="black" d="M1991.84,-332C1991.84,-332 1942.04,-332 1942.04,-332 1936.04,-332 1930.04,-326 1930.04,-320 1930.04,-320 1930.04,-300 1930.04,-300 1930.04,-294 1936.04,-288 1942.04,-288 1942.04,-288 1991.84,-288 1991.84,-288 1997.84,-288 2003.84,-294 2003.84,-300 2003.84,-300 2003.84,-320 2003.84,-320 2003.84,-326 1997.84,-332 1991.84,-332"/>
<text xml:space="preserve" text-anchor="middle" x="1966.94" y="-319" font-family="Helvetica,sans-Serif" font-size="10.00">ActorSlot</text>
<text xml:space="preserve" text-anchor="middle" x="1966.94" y="-307" font-family="Helvetica,sans-Serif" font-size="10.00">mailbox</text>
<text xml:space="preserve" text-anchor="middle" x="1966.94" y="-295" font-family="Helvetica,sans-Serif" font-size="10.00">.push_back()</text>
</g>
<!-- b_deliver&#45;&gt;b_mailbox -->
<g id="edge14" class="edge">
<title>b_deliver&#45;&gt;b_mailbox</title>
<path fill="none" stroke="#8e44ad" stroke-width="1.5" d="M1679.6,-310C1738.94,-310 1853.2,-310 1918.01,-310"/>
<polygon fill="#8e44ad" stroke="#8e44ad" stroke-width="1.5" points="1917.62,-313.5 1927.62,-310 1917.62,-306.5 1917.62,-313.5"/>
<text xml:space="preserve" text-anchor="middle" x="1805.63" y="-312.7" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#8e44ad">payload</text>
</g>
<!-- c_caller -->
<g id="node18" class="node">
<title>c_caller</title>
<ellipse fill="#a9dfbf" stroke="black" cx="135.72" cy="-406" rx="47.46" ry="22.63"/>
<text xml:space="preserve" text-anchor="middle" x="135.72" y="-409" font-family="Helvetica,sans-Serif" font-size="10.00">Caller /</text>
<text xml:space="preserve" text-anchor="middle" x="135.72" y="-397" font-family="Helvetica,sans-Serif" font-size="10.00">ctx.spawn()</text>
</g>
<!-- c_spawn -->
<g id="node19" class="node">
<title>c_spawn</title>
<path fill="white" stroke="black" d="M480.12,-424C480.12,-424 424.76,-424 424.76,-424 418.76,-424 412.76,-418 412.76,-412 412.76,-412 412.76,-400 412.76,-400 412.76,-394 418.76,-388 424.76,-388 424.76,-388 480.12,-388 480.12,-388 486.12,-388 492.12,-394 492.12,-400 492.12,-400 492.12,-412 492.12,-412 492.12,-418 486.12,-424 480.12,-424"/>
<text xml:space="preserve" text-anchor="middle" x="452.44" y="-409" font-family="Helvetica,sans-Serif" font-size="10.00">Runtime</text>
<text xml:space="preserve" text-anchor="middle" x="452.44" y="-397" font-family="Helvetica,sans-Serif" font-size="10.00">::spawn(actor)</text>
</g>
<!-- c_caller&#45;&gt;c_spawn -->
<g id="edge15" class="edge">
<title>c_caller&#45;&gt;c_spawn</title>
<path fill="none" stroke="#333333" d="M183.5,-406C241.77,-406 340.95,-406 400.92,-406"/>
<polygon fill="#333333" stroke="#333333" points="400.85,-409.5 410.85,-406 400.85,-402.5 400.85,-409.5"/>
</g>
<!-- c_addr -->
<g id="node20" class="node">
<title>c_addr</title>
<path fill="#d5f5e3" stroke="black" d="M754.76,-424C754.76,-424 692.73,-424 692.73,-424 686.73,-424 680.73,-418 680.73,-412 680.73,-412 680.73,-400 680.73,-400 680.73,-394 686.73,-388 692.73,-388 692.73,-388 754.76,-388 754.76,-388 760.76,-388 766.76,-394 766.76,-400 766.76,-400 766.76,-412 766.76,-412 766.76,-418 760.76,-424 754.76,-424"/>
<text xml:space="preserve" text-anchor="middle" x="723.74" y="-409" font-family="Helvetica,sans-Serif" font-size="10.00">ActorAddress</text>
<text xml:space="preserve" text-anchor="middle" x="723.74" y="-397" font-family="Helvetica,sans-Serif" font-size="10.00">::new_random()</text>
</g>
<!-- c_spawn&#45;&gt;c_addr -->
<g id="edge16" class="edge">
<title>c_spawn&#45;&gt;c_addr</title>
<path fill="none" stroke="#333333" d="M492.6,-406C539.12,-406 616.97,-406 669.07,-406"/>
<polygon fill="#333333" stroke="#333333" points="668.91,-409.5 678.91,-406 668.91,-402.5 668.91,-409.5"/>
</g>
<!-- c_place -->
<g id="node21" class="node">
<title>c_place</title>
<path fill="#e8daef" stroke="black" d="M1065.68,-428C1065.68,-428 1007,-428 1007,-428 1001,-428 995,-422 995,-416 995,-416 995,-396 995,-396 995,-390 1001,-384 1007,-384 1007,-384 1065.68,-384 1065.68,-384 1071.68,-384 1077.68,-390 1077.68,-396 1077.68,-396 1077.68,-416 1077.68,-416 1077.68,-422 1071.68,-428 1065.68,-428"/>
<text xml:space="preserve" text-anchor="middle" x="1036.34" y="-415" font-family="Helvetica,sans-Serif" font-size="10.00">Placement</text>
<text xml:space="preserve" text-anchor="middle" x="1036.34" y="-403" font-family="Helvetica,sans-Serif" font-size="10.00">::next_worker()</text>
<text xml:space="preserve" text-anchor="middle" x="1036.34" y="-391" font-family="Helvetica,sans-Serif" font-size="10.00">round&#45;robin</text>
</g>
<!-- c_addr&#45;&gt;c_place -->
<g id="edge17" class="edge">
<title>c_addr&#45;&gt;c_place</title>
<path fill="none" stroke="#333333" d="M766.96,-406C823.21,-406 922.69,-406 983.46,-406"/>
<polygon fill="#333333" stroke="#333333" points="983.19,-409.5 993.19,-406 983.19,-402.5 983.19,-409.5"/>
</g>
<!-- c_addrmap -->
<g id="node22" class="node">
<title>c_addrmap</title>
<path fill="#e8daef" stroke="black" d="M1360.16,-424C1360.16,-424 1290.91,-424 1290.91,-424 1284.91,-424 1278.91,-418 1278.91,-412 1278.91,-412 1278.91,-400 1278.91,-400 1278.91,-394 1284.91,-388 1290.91,-388 1290.91,-388 1360.16,-388 1360.16,-388 1366.16,-388 1372.16,-394 1372.16,-400 1372.16,-400 1372.16,-412 1372.16,-412 1372.16,-418 1366.16,-424 1360.16,-424"/>
<text xml:space="preserve" text-anchor="middle" x="1325.53" y="-409" font-family="Helvetica,sans-Serif" font-size="10.00">AddressMap</text>
<text xml:space="preserve" text-anchor="middle" x="1325.53" y="-397" font-family="Helvetica,sans-Serif" font-size="10.00">::insert(addr, wid)</text>
</g>
<!-- c_place&#45;&gt;c_addrmap -->
<g id="edge18" class="edge">
<title>c_place&#45;&gt;c_addrmap</title>
<path fill="none" stroke="#117864" d="M1077.88,-406C1127.25,-406 1210.96,-406 1266.99,-406"/>
<polygon fill="#117864" stroke="#117864" points="1266.99,-409.5 1276.99,-406 1266.99,-402.5 1266.99,-409.5"/>
<text xml:space="preserve" text-anchor="middle" x="1195.12" y="-408.7" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#117864">WorkerId</text>
</g>
<!-- c_box -->
<g id="node23" class="node">
<title>c_box</title>
<path fill="white" stroke="black" d="M1667.81,-424C1667.81,-424 1613.02,-424 1613.02,-424 1607.02,-424 1601.02,-418 1601.02,-412 1601.02,-412 1601.02,-400 1601.02,-400 1601.02,-394 1607.02,-388 1613.02,-388 1613.02,-388 1667.81,-388 1667.81,-388 1673.81,-388 1679.81,-394 1679.81,-400 1679.81,-400 1679.81,-412 1679.81,-412 1679.81,-418 1673.81,-424 1667.81,-424"/>
<text xml:space="preserve" text-anchor="middle" x="1640.42" y="-409" font-family="Helvetica,sans-Serif" font-size="10.00">Box::new(</text>
<text xml:space="preserve" text-anchor="middle" x="1640.42" y="-397" font-family="Helvetica,sans-Serif" font-size="10.00">Actor::new(a))</text>
</g>
<!-- c_addrmap&#45;&gt;c_box -->
<g id="edge19" class="edge">
<title>c_addrmap&#45;&gt;c_box</title>
<path fill="none" stroke="#333333" d="M1372.36,-406C1430.29,-406 1529.63,-406 1589.45,-406"/>
<polygon fill="#333333" stroke="#333333" points="1589.35,-409.5 1599.35,-406 1589.35,-402.5 1589.35,-409.5"/>
</g>
<!-- c_tx -->
<g id="node24" class="node">
<title>c_tx</title>
<path fill="#d1f2eb" stroke="black" d="M1987.39,-428C1987.39,-428 1946.49,-428 1946.49,-428 1940.49,-428 1934.49,-422 1934.49,-416 1934.49,-416 1934.49,-396 1934.49,-396 1934.49,-390 1940.49,-384 1946.49,-384 1946.49,-384 1987.39,-384 1987.39,-384 1993.39,-384 1999.39,-390 1999.39,-396 1999.39,-396 1999.39,-416 1999.39,-416 1999.39,-422 1993.39,-428 1987.39,-428"/>
<text xml:space="preserve" text-anchor="middle" x="1966.94" y="-415" font-family="Helvetica,sans-Serif" font-size="10.00">spawn_txs</text>
<text xml:space="preserve" text-anchor="middle" x="1966.94" y="-403" font-family="Helvetica,sans-Serif" font-size="10.00">[worker_id]</text>
<text xml:space="preserve" text-anchor="middle" x="1966.94" y="-391" font-family="Helvetica,sans-Serif" font-size="10.00">.try_send()</text>
</g>
<!-- c_box&#45;&gt;c_tx -->
<g id="edge20" class="edge">
<title>c_box&#45;&gt;c_tx</title>
<path fill="none" stroke="#27ae60" stroke-width="2" d="M1680.24,-406C1740.87,-406 1857.68,-406 1921.56,-406"/>
<polygon fill="#27ae60" stroke="#27ae60" stroke-width="2" points="1921.32,-409.5 1931.32,-406 1921.32,-402.5 1921.32,-409.5"/>
<text xml:space="preserve" text-anchor="middle" x="1805.63" y="-408.7" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#27ae60">(addr, actor_box)</text>
</g>
<!-- c_hybrid -->
<g id="node25" class="node">
<title>c_hybrid</title>
<path fill="#d1f2eb" stroke="black" d="M2260.18,-420.73C2260.18,-422.53 2241.77,-424 2219.1,-424 2196.44,-424 2178.03,-422.53 2178.03,-420.73 2178.03,-420.73 2178.03,-391.27 2178.03,-391.27 2178.03,-389.47 2196.44,-388 2219.1,-388 2241.77,-388 2260.18,-389.47 2260.18,-391.27 2260.18,-391.27 2260.18,-420.73 2260.18,-420.73"/>
<path fill="none" stroke="black" d="M2260.18,-420.73C2260.18,-418.92 2241.77,-417.45 2219.1,-417.45 2196.44,-417.45 2178.03,-418.92 2178.03,-420.73"/>
<text xml:space="preserve" text-anchor="middle" x="2219.1" y="-403" font-family="Helvetica,sans-Serif" font-size="10.00">HybridChannel</text>
</g>
<!-- c_tx&#45;&gt;c_hybrid -->
<g id="edge21" class="edge">
<title>c_tx&#45;&gt;c_hybrid</title>
<path fill="none" stroke="#27ae60" stroke-width="2" d="M1999.88,-406C2041.47,-406 2114.77,-406 2164.84,-406"/>
<polygon fill="#27ae60" stroke="#27ae60" stroke-width="2" points="2164.65,-409.5 2174.65,-406 2164.65,-402.5 2164.65,-409.5"/>
<text xml:space="preserve" text-anchor="middle" x="2125.01" y="-408.7" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#27ae60">spawn tuple</text>
</g>
<!-- c_rx -->
<g id="node26" class="node">
<title>c_rx</title>
<path fill="#fadbd8" stroke="black" d="M2407.61,-428C2407.61,-428 2370.61,-428 2370.61,-428 2364.61,-428 2358.61,-422 2358.61,-416 2358.61,-416 2358.61,-396 2358.61,-396 2358.61,-390 2364.61,-384 2370.61,-384 2370.61,-384 2407.61,-384 2407.61,-384 2413.61,-384 2419.61,-390 2419.61,-396 2419.61,-396 2419.61,-416 2419.61,-416 2419.61,-422 2413.61,-428 2407.61,-428"/>
<text xml:space="preserve" text-anchor="middle" x="2389.11" y="-415" font-family="Helvetica,sans-Serif" font-size="10.00">Worker</text>
<text xml:space="preserve" text-anchor="middle" x="2389.11" y="-403" font-family="Helvetica,sans-Serif" font-size="10.00">spawn_rx</text>
<text xml:space="preserve" text-anchor="middle" x="2389.11" y="-391" font-family="Helvetica,sans-Serif" font-size="10.00">.try_recv()</text>
</g>
<!-- c_hybrid&#45;&gt;c_rx -->
<g id="edge22" class="edge">
<title>c_hybrid&#45;&gt;c_rx</title>
<path fill="none" stroke="#27ae60" stroke-width="2" d="M2260.35,-406C2285.97,-406 2319.14,-406 2345.26,-406"/>
<polygon fill="#27ae60" stroke="#27ae60" stroke-width="2" points="2345.17,-409.5 2355.17,-406 2345.17,-402.5 2345.17,-409.5"/>
<text xml:space="preserve" text-anchor="middle" x="2306.19" y="-408.7" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#27ae60">spawn tuple</text>
</g>
<!-- c_pool -->
<g id="node27" class="node">
<title>c_pool</title>
<path fill="#fadbd8" stroke="black" d="M2598.68,-428C2598.68,-428 2550.55,-428 2550.55,-428 2544.55,-428 2538.55,-422 2538.55,-416 2538.55,-416 2538.55,-396 2538.55,-396 2538.55,-390 2544.55,-384 2550.55,-384 2550.55,-384 2598.68,-384 2598.68,-384 2604.68,-384 2610.68,-390 2610.68,-396 2610.68,-396 2610.68,-416 2610.68,-416 2610.68,-422 2604.68,-428 2598.68,-428"/>
<text xml:space="preserve" text-anchor="middle" x="2574.62" y="-415" font-family="Helvetica,sans-Serif" font-size="10.00">ActorPool</text>
<text xml:space="preserve" text-anchor="middle" x="2574.62" y="-403" font-family="Helvetica,sans-Serif" font-size="10.00">::insert(addr,</text>
<text xml:space="preserve" text-anchor="middle" x="2574.62" y="-391" font-family="Helvetica,sans-Serif" font-size="10.00">actor_box)</text>
</g>
<!-- c_rx&#45;&gt;c_pool -->
<g id="edge23" class="edge">
<title>c_rx&#45;&gt;c_pool</title>
<path fill="none" stroke="#27ae60" stroke-width="2" d="M2420.01,-406C2448.52,-406 2491.94,-406 2525.36,-406"/>
<polygon fill="#27ae60" stroke="#27ae60" stroke-width="2" points="2525.05,-409.5 2535.05,-406 2525.05,-402.5 2525.05,-409.5"/>
<text xml:space="preserve" text-anchor="middle" x="2482.28" y="-408.7" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#27ae60">(addr, actor_box)</text>
</g>
<!-- d_caller -->
<g id="node28" class="node">
<title>d_caller</title>
<ellipse fill="#a3e4d7" stroke="black" cx="135.72" cy="-522" rx="27" ry="18"/>
<text xml:space="preserve" text-anchor="middle" x="135.72" y="-519" font-family="Helvetica,sans-Serif" font-size="10.00">Caller</text>
</g>
<!-- d_new -->
<g id="node29" class="node">
<title>d_new</title>
<path fill="white" stroke="black" d="M491.24,-540C491.24,-540 413.64,-540 413.64,-540 407.64,-540 401.64,-534 401.64,-528 401.64,-528 401.64,-516 401.64,-516 401.64,-510 407.64,-504 413.64,-504 413.64,-504 491.24,-504 491.24,-504 497.24,-504 503.24,-510 503.24,-516 503.24,-516 503.24,-528 503.24,-528 503.24,-534 497.24,-540 491.24,-540"/>
<text xml:space="preserve" text-anchor="middle" x="452.44" y="-525" font-family="Helvetica,sans-Serif" font-size="10.00">Runtime</text>
<text xml:space="preserve" text-anchor="middle" x="452.44" y="-513" font-family="Helvetica,sans-Serif" font-size="10.00">::new_inbox::&lt;M&gt;()</text>
</g>
<!-- d_caller&#45;&gt;d_new -->
<g id="edge24" class="edge">
<title>d_caller&#45;&gt;d_new</title>
<path fill="none" stroke="#333333" d="M163.01,-522C212.62,-522 321.23,-522 390.1,-522"/>
<polygon fill="#333333" stroke="#333333" points="389.71,-525.5 399.71,-522 389.71,-518.5 389.71,-525.5"/>
</g>
<!-- d_addr -->
<g id="node30" class="node">
<title>d_addr</title>
<path fill="#d5f5e3" stroke="black" d="M754.76,-540C754.76,-540 692.73,-540 692.73,-540 686.73,-540 680.73,-534 680.73,-528 680.73,-528 680.73,-516 680.73,-516 680.73,-510 686.73,-504 692.73,-504 692.73,-504 754.76,-504 754.76,-504 760.76,-504 766.76,-510 766.76,-516 766.76,-516 766.76,-528 766.76,-528 766.76,-534 760.76,-540 754.76,-540"/>
<text xml:space="preserve" text-anchor="middle" x="723.74" y="-525" font-family="Helvetica,sans-Serif" font-size="10.00">ActorAddress</text>
<text xml:space="preserve" text-anchor="middle" x="723.74" y="-513" font-family="Helvetica,sans-Serif" font-size="10.00">::new_random()</text>
</g>
<!-- d_new&#45;&gt;d_addr -->
<g id="edge25" class="edge">
<title>d_new&#45;&gt;d_addr</title>
<path fill="none" stroke="#333333" d="M503.46,-522C550.54,-522 620.82,-522 669.06,-522"/>
<polygon fill="#333333" stroke="#333333" points="669,-525.5 679,-522 669,-518.5 669,-525.5"/>
</g>
<!-- d_recv -->
<g id="node31" class="node">
<title>d_recv</title>
<path fill="#d1f2eb" stroke="black" d="M1065.96,-540C1065.96,-540 1006.72,-540 1006.72,-540 1000.72,-540 994.72,-534 994.72,-528 994.72,-528 994.72,-516 994.72,-516 994.72,-510 1000.72,-504 1006.72,-504 1006.72,-504 1065.96,-504 1065.96,-504 1071.96,-504 1077.96,-510 1077.96,-516 1077.96,-516 1077.96,-528 1077.96,-528 1077.96,-534 1071.96,-540 1065.96,-540"/>
<text xml:space="preserve" text-anchor="middle" x="1036.34" y="-525" font-family="Helvetica,sans-Serif" font-size="10.00">Receiver&lt;M&gt;</text>
<text xml:space="preserve" text-anchor="middle" x="1036.34" y="-513" font-family="Helvetica,sans-Serif" font-size="10.00">::new(capacity)</text>
</g>
<!-- d_addr&#45;&gt;d_recv -->
<g id="edge26" class="edge">
<title>d_addr&#45;&gt;d_recv</title>
<path fill="none" stroke="#333333" d="M766.96,-522C823.21,-522 922.69,-522 983.46,-522"/>
<polygon fill="#333333" stroke="#333333" points="983.19,-525.5 993.19,-522 983.19,-518.5 983.19,-525.5"/>
</g>
<!-- d_sender -->
<g id="node32" class="node">
<title>d_sender</title>
<path fill="#d1f2eb" stroke="black" d="M1367.25,-510C1367.25,-510 1283.82,-510 1283.82,-510 1277.82,-510 1271.82,-504 1271.82,-498 1271.82,-498 1271.82,-486 1271.82,-486 1271.82,-480 1277.82,-474 1283.82,-474 1283.82,-474 1367.25,-474 1367.25,-474 1373.25,-474 1379.25,-480 1379.25,-486 1379.25,-486 1379.25,-498 1379.25,-498 1379.25,-504 1373.25,-510 1367.25,-510"/>
<text xml:space="preserve" text-anchor="middle" x="1325.53" y="-495" font-family="Helvetica,sans-Serif" font-size="10.00">Sender&lt;M&gt;</text>
<text xml:space="preserve" text-anchor="middle" x="1325.53" y="-483" font-family="Helvetica,sans-Serif" font-size="10.00">= recv.new_sender()</text>
</g>
<!-- d_recv&#45;&gt;d_sender -->
<g id="edge27" class="edge">
<title>d_recv&#45;&gt;d_sender</title>
<path fill="none" stroke="#e67300" d="M1078.31,-516.99C1105.24,-513.76 1141.25,-509.55 1173.11,-506.2 1201.72,-503.19 1233.4,-500.18 1260.35,-497.7"/>
<polygon fill="#e67300" stroke="#e67300" points="1260.45,-501.2 1270.09,-496.81 1259.81,-494.23 1260.45,-501.2"/>
<text xml:space="preserve" text-anchor="middle" x="1195.12" y="-508.9" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#e67300">clone Arc</text>
</g>
<!-- d_inbox -->
<g id="node34" class="node">
<title>d_inbox</title>
<path fill="#d6eaf8" stroke="black" d="M1357.66,-576C1357.66,-576 1293.4,-576 1293.4,-576 1287.4,-576 1281.4,-570 1281.4,-564 1281.4,-564 1281.4,-544 1281.4,-544 1281.4,-538 1287.4,-532 1293.4,-532 1293.4,-532 1357.66,-532 1357.66,-532 1363.66,-532 1369.66,-538 1369.66,-544 1369.66,-544 1369.66,-564 1369.66,-564 1369.66,-570 1363.66,-576 1357.66,-576"/>
<text xml:space="preserve" text-anchor="middle" x="1325.53" y="-563" font-family="Helvetica,sans-Serif" font-size="10.00">return Inbox {</text>
<text xml:space="preserve" text-anchor="middle" x="1325.53" y="-551" font-family="Helvetica,sans-Serif" font-size="10.00">addr, inner: recv</text>
<text xml:space="preserve" text-anchor="middle" x="1325.53" y="-539" font-family="Helvetica,sans-Serif" font-size="10.00">}</text>
</g>
<!-- d_recv&#45;&gt;d_inbox -->
<g id="edge29" class="edge">
<title>d_recv&#45;&gt;d_inbox</title>
<path fill="none" stroke="#333333" d="M1078.18,-526.55C1128.46,-532.15 1214.02,-541.69 1269.9,-547.91"/>
<polygon fill="#333333" stroke="#333333" points="1269.18,-551.35 1279.51,-548.98 1269.96,-544.4 1269.18,-551.35"/>
<text xml:space="preserve" text-anchor="middle" x="1195.12" y="-544.51" font-family="Helvetica,sans-Serif" font-size="9.00">Receiver</text>
</g>
<!-- d_reg -->
<g id="node33" class="node">
<title>d_reg</title>
<path fill="#e8daef" stroke="black" d="M1679.49,-518C1679.49,-518 1601.34,-518 1601.34,-518 1595.34,-518 1589.34,-512 1589.34,-506 1589.34,-506 1589.34,-486 1589.34,-486 1589.34,-480 1595.34,-474 1601.34,-474 1601.34,-474 1679.49,-474 1679.49,-474 1685.49,-474 1691.49,-480 1691.49,-486 1691.49,-486 1691.49,-506 1691.49,-506 1691.49,-512 1685.49,-518 1679.49,-518"/>
<text xml:space="preserve" text-anchor="middle" x="1640.42" y="-505" font-family="Helvetica,sans-Serif" font-size="10.00">InboxRegistry</text>
<text xml:space="preserve" text-anchor="middle" x="1640.42" y="-493" font-family="Helvetica,sans-Serif" font-size="10.00">::register(</text>
<text xml:space="preserve" text-anchor="middle" x="1640.42" y="-481" font-family="Helvetica,sans-Serif" font-size="10.00">addr, Arc&lt;sender&gt;)</text>
</g>
<!-- d_sender&#45;&gt;d_reg -->
<g id="edge28" class="edge">
<title>d_sender&#45;&gt;d_reg</title>
<path fill="none" stroke="#e67300" d="M1379.61,-492.68C1434.45,-493.38 1519.96,-494.47 1577.76,-495.21"/>
<polygon fill="#e67300" stroke="#e67300" points="1577.38,-498.71 1587.42,-495.34 1577.46,-491.71 1577.38,-498.71"/>
<text xml:space="preserve" text-anchor="middle" x="1471.7" y="-496.94" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#e67300">Arc&lt;dyn SenderT&gt;</text>
</g>
<!-- d_send -->
<g id="node35" class="node">
<title>d_send</title>
<path fill="white" stroke="black" d="M170.35,-634C170.35,-634 101.09,-634 101.09,-634 95.09,-634 89.09,-628 89.09,-622 89.09,-622 89.09,-602 89.09,-602 89.09,-596 95.09,-590 101.09,-590 101.09,-590 170.35,-590 170.35,-590 176.35,-590 182.35,-596 182.35,-602 182.35,-602 182.35,-622 182.35,-622 182.35,-628 176.35,-634 170.35,-634"/>
<text xml:space="preserve" text-anchor="middle" x="135.72" y="-621" font-family="Helvetica,sans-Serif" font-size="10.00">Runtime</text>
<text xml:space="preserve" text-anchor="middle" x="135.72" y="-609" font-family="Helvetica,sans-Serif" font-size="10.00">::send_to(</text>
<text xml:space="preserve" text-anchor="middle" x="135.72" y="-597" font-family="Helvetica,sans-Serif" font-size="10.00">inbox_addr, msg)</text>
</g>
<!-- d_miss -->
<g id="node36" class="node">
<title>d_miss</title>
<path fill="#e8daef" stroke="black" d="M483.17,-630C483.17,-630 421.71,-630 421.71,-630 415.71,-630 409.71,-624 409.71,-618 409.71,-618 409.71,-606 409.71,-606 409.71,-600 415.71,-594 421.71,-594 421.71,-594 483.17,-594 483.17,-594 489.17,-594 495.17,-600 495.17,-606 495.17,-606 495.17,-618 495.17,-618 495.17,-624 489.17,-630 483.17,-630"/>
<text xml:space="preserve" text-anchor="middle" x="452.44" y="-615" font-family="Helvetica,sans-Serif" font-size="10.00">AddressMap</text>
<text xml:space="preserve" text-anchor="middle" x="452.44" y="-603" font-family="Helvetica,sans-Serif" font-size="10.00">::lookup → miss</text>
</g>
<!-- d_send&#45;&gt;d_miss -->
<g id="edge30" class="edge">
<title>d_send&#45;&gt;d_miss</title>
<path fill="none" stroke="#333333" d="M182.82,-612C240.08,-612 337.59,-612 398.04,-612"/>
<polygon fill="#333333" stroke="#333333" points="397.74,-615.5 407.74,-612 397.74,-608.5 397.74,-615.5"/>
</g>
<!-- d_try -->
<g id="node37" class="node">
<title>d_try</title>
<path fill="#e8daef" stroke="black" d="M755.59,-634C755.59,-634 691.9,-634 691.9,-634 685.9,-634 679.9,-628 679.9,-622 679.9,-622 679.9,-602 679.9,-602 679.9,-596 685.9,-590 691.9,-590 691.9,-590 755.59,-590 755.59,-590 761.59,-590 767.59,-596 767.59,-602 767.59,-602 767.59,-622 767.59,-622 767.59,-628 761.59,-634 755.59,-634"/>
<text xml:space="preserve" text-anchor="middle" x="723.74" y="-621" font-family="Helvetica,sans-Serif" font-size="10.00">InboxRegistry</text>
<text xml:space="preserve" text-anchor="middle" x="723.74" y="-609" font-family="Helvetica,sans-Serif" font-size="10.00">::try_deliver(</text>
<text xml:space="preserve" text-anchor="middle" x="723.74" y="-597" font-family="Helvetica,sans-Serif" font-size="10.00">addr, Box(msg))</text>
</g>
<!-- d_miss&#45;&gt;d_try -->
<g id="edge31" class="edge">
<title>d_miss&#45;&gt;d_try</title>
<path fill="none" stroke="#333333" d="M495.52,-612C542.04,-612 617.15,-612 668.17,-612"/>
<polygon fill="#333333" stroke="#333333" points="668.11,-615.5 678.11,-612 668.11,-608.5 668.11,-615.5"/>
<text xml:space="preserve" text-anchor="middle" x="583.92" y="-625.5" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#666666">not a worker</text>
<text xml:space="preserve" text-anchor="middle" x="583.92" y="-614.7" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#666666">actor</text>
</g>
<!-- d_dyn -->
<g id="node38" class="node">
<title>d_dyn</title>
<path fill="#d1f2eb" stroke="black" d="M1086.54,-634C1086.54,-634 986.14,-634 986.14,-634 980.14,-634 974.14,-628 974.14,-622 974.14,-622 974.14,-602 974.14,-602 974.14,-596 980.14,-590 986.14,-590 986.14,-590 1086.54,-590 1086.54,-590 1092.54,-590 1098.54,-596 1098.54,-602 1098.54,-602 1098.54,-622 1098.54,-622 1098.54,-628 1092.54,-634 1086.54,-634"/>
<text xml:space="preserve" text-anchor="middle" x="1036.34" y="-621" font-family="Helvetica,sans-Serif" font-size="10.00">Arc&lt;dyn SenderT&gt;</text>
<text xml:space="preserve" text-anchor="middle" x="1036.34" y="-609" font-family="Helvetica,sans-Serif" font-size="10.00">::try_send_any()</text>
<text xml:space="preserve" text-anchor="middle" x="1036.34" y="-597" font-family="Helvetica,sans-Serif" font-size="10.00">downcast → Sender&lt;M&gt;</text>
</g>
<!-- d_try&#45;&gt;d_dyn -->
<g id="edge32" class="edge">
<title>d_try&#45;&gt;d_dyn</title>
<path fill="none" stroke="#8e44ad" stroke-width="1.5" d="M767.93,-612C817.99,-612 901.32,-612 961.67,-612"/>
<polygon fill="#8e44ad" stroke="#8e44ad" stroke-width="1.5" points="961.53,-615.5 971.53,-612 961.53,-608.5 961.53,-615.5"/>
<text xml:space="preserve" text-anchor="middle" x="869.07" y="-614.7" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#8e44ad">Box&lt;dyn Any&gt;</text>
</g>
<!-- d_hybrid -->
<g id="node39" class="node">
<title>d_hybrid</title>
<path fill="#d1f2eb" stroke="black" d="M1366.61,-630.73C1366.61,-632.53 1348.2,-634 1325.53,-634 1302.87,-634 1284.46,-632.53 1284.46,-630.73 1284.46,-630.73 1284.46,-601.27 1284.46,-601.27 1284.46,-599.47 1302.87,-598 1325.53,-598 1348.2,-598 1366.61,-599.47 1366.61,-601.27 1366.61,-601.27 1366.61,-630.73 1366.61,-630.73"/>
<path fill="none" stroke="black" d="M1366.61,-630.73C1366.61,-628.92 1348.2,-627.45 1325.53,-627.45 1302.87,-627.45 1284.46,-628.92 1284.46,-630.73"/>
<text xml:space="preserve" text-anchor="middle" x="1325.53" y="-613" font-family="Helvetica,sans-Serif" font-size="10.00">HybridChannel</text>
</g>
<!-- d_dyn&#45;&gt;d_hybrid -->
<g id="edge33" class="edge">
<title>d_dyn&#45;&gt;d_hybrid</title>
<path fill="none" stroke="#c0392b" stroke-width="1.5" d="M1098.95,-612.86C1150.5,-613.58 1223.25,-614.59 1272.06,-615.27"/>
<polygon fill="#c0392b" stroke="#c0392b" stroke-width="1.5" points="1271.92,-618.77 1281.97,-615.41 1272.02,-611.77 1271.92,-618.77"/>
<text xml:space="preserve" text-anchor="middle" x="1195.12" y="-617.18" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#c0392b">M (typed)</text>
</g>
<!-- d_tryrecv -->
<g id="node40" class="node">
<title>d_tryrecv</title>
<path fill="#d6eaf8" stroke="black" d="M1660.31,-634C1660.31,-634 1620.53,-634 1620.53,-634 1614.53,-634 1608.53,-628 1608.53,-622 1608.53,-622 1608.53,-610 1608.53,-610 1608.53,-604 1614.53,-598 1620.53,-598 1620.53,-598 1660.31,-598 1660.31,-598 1666.31,-598 1672.31,-604 1672.31,-610 1672.31,-610 1672.31,-622 1672.31,-622 1672.31,-628 1666.31,-634 1660.31,-634"/>
<text xml:space="preserve" text-anchor="middle" x="1640.42" y="-619" font-family="Helvetica,sans-Serif" font-size="10.00">Inbox</text>
<text xml:space="preserve" text-anchor="middle" x="1640.42" y="-607" font-family="Helvetica,sans-Serif" font-size="10.00">::try_recv()</text>
</g>
<!-- d_hybrid&#45;&gt;d_tryrecv -->
<g id="edge34" class="edge">
<title>d_hybrid&#45;&gt;d_tryrecv</title>
<path fill="none" stroke="#c0392b" stroke-width="1.5" d="M1366.8,-616C1425.81,-616 1535.36,-616 1596.08,-616"/>
<polygon fill="#c0392b" stroke="#c0392b" stroke-width="1.5" points="1595.99,-619.5 1605.99,-616 1595.99,-612.5 1595.99,-619.5"/>
<text xml:space="preserve" text-anchor="middle" x="1471.7" y="-618.7" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#c0392b">M</text>
</g>
<!-- e_phase1 -->
<g id="node41" class="node">
<title>e_phase1</title>
<polygon fill="#27ae60" stroke="none" points="375.97,-887 375.97,-913 528.91,-913 528.91,-887 375.97,-887"/>
<polygon fill="none" stroke="black" points="375.97,-887 375.97,-913 528.91,-913 528.91,-887 375.97,-887"/>
<text xml:space="preserve" text-anchor="start" x="433.54" y="-898" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00" fill="white">Phase 1</text>
<polygon fill="none" stroke="black" points="375.97,-833 375.97,-887 528.91,-887 528.91,-833 375.97,-833"/>
<text xml:space="preserve" text-anchor="start" x="397.99" y="-872" font-family="Helvetica,sans-Serif" font-size="10.00">DRAIN SPAWN QUEUE</text>
<text xml:space="preserve" text-anchor="start" x="408.54" y="-862" font-family="Helvetica,sans-Serif" font-size="10.00">spawn_rx.try_recv()</text>
<text xml:space="preserve" text-anchor="start" x="382.97" y="-852" font-family="Helvetica,sans-Serif" font-size="10.00">→ ActorPool::insert(addr, actor)</text>
<text xml:space="preserve" text-anchor="start" x="412.7" y="-842" font-family="Helvetica,sans-Serif" font-style="italic" font-size="10.00">repeat until empty</text>
</g>
<!-- e_phase2 -->
<g id="node42" class="node">
<title>e_phase2</title>
<polygon fill="#c0392b" stroke="none" points="638.93,-852 638.93,-878 808.56,-878 808.56,-852 638.93,-852"/>
<polygon fill="none" stroke="black" points="638.93,-852 638.93,-878 808.56,-878 808.56,-852 638.93,-852"/>
<text xml:space="preserve" text-anchor="start" x="704.84" y="-863" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00" fill="white">Phase 2</text>
<polygon fill="none" stroke="black" points="638.93,-798 638.93,-852 808.56,-852 808.56,-798 638.93,-798"/>
<text xml:space="preserve" text-anchor="start" x="660.68" y="-837" font-family="Helvetica,sans-Serif" font-size="10.00">DRAIN TRANSFER QUEUE</text>
<text xml:space="preserve" text-anchor="start" x="649.55" y="-827" font-family="Helvetica,sans-Serif" font-size="10.00">transfer_rx.try_recv() → Envelope</text>
<text xml:space="preserve" text-anchor="start" x="645.93" y="-817" font-family="Helvetica,sans-Serif" font-size="10.00">→ ActorPool::deliver(dest, payload)</text>
<text xml:space="preserve" text-anchor="start" x="662.04" y="-807" font-family="Helvetica,sans-Serif" font-style="italic" font-size="10.00">repeat up to max(len&gt;&gt;1, 1)</text>
</g>
<!-- e_phase1&#45;&gt;e_phase2 -->
<g id="edge35" class="edge">
<title>e_phase1&#45;&gt;e_phase2</title>
<path fill="none" stroke="#333333" stroke-width="2" d="M536.81,-862.17C562.68,-858.8 591.52,-855.05 618.62,-851.53"/>
<polygon fill="#333333" stroke="#333333" stroke-width="2" points="618.88,-855.03 628.34,-850.27 617.97,-848.09 618.88,-855.03"/>
</g>
<!-- e_pool -->
<g id="node49" class="node">
<title>e_pool</title>
<polygon fill="#fadbd8" stroke="black" points="2012.88,-870 1925.01,-870 1921.01,-866 1921.01,-826 2008.88,-826 2012.88,-830 2012.88,-870"/>
<polyline fill="none" stroke="black" points="2008.88,-866 1921.01,-866"/>
<polyline fill="none" stroke="black" points="2008.88,-866 2008.88,-826"/>
<polyline fill="none" stroke="black" points="2008.88,-866 2012.88,-870"/>
<text xml:space="preserve" text-anchor="middle" x="1966.94" y="-857" font-family="Helvetica,sans-Serif" font-size="10.00">ActorPool</text>
<text xml:space="preserve" text-anchor="middle" x="1966.94" y="-845" font-family="Helvetica,sans-Serif" font-size="10.00">(HashMap&lt;Addr,</text>
<text xml:space="preserve" text-anchor="middle" x="1966.94" y="-833" font-family="Helvetica,sans-Serif" font-size="10.00">ActorSlot&gt;)</text>
</g>
<!-- e_phase1&#45;&gt;e_pool -->
<g id="edge42" class="edge">
<title>e_phase1&#45;&gt;e_pool</title>
<path fill="none" stroke="#27ae60" stroke-dasharray="5,2" d="M536.82,-884C566.47,-887.49 600.14,-890.97 630.93,-893 1126.95,-925.77 1254.73,-931.59 1749.36,-882 1804.11,-876.51 1866.1,-866.38 1909.69,-858.6"/>
<polygon fill="#27ae60" stroke="#27ae60" points="1910.21,-862.06 1919.43,-856.84 1908.96,-855.17 1910.21,-862.06"/>
<text xml:space="preserve" text-anchor="middle" x="1195.12" y="-921.11" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#27ae60">insert</text>
</g>
<!-- e_phase3 -->
<g id="node43" class="node">
<title>e_phase3</title>
<polygon fill="#8e44ad" stroke="none" points="929.57,-833 929.57,-859 1143.11,-859 1143.11,-833 929.57,-833"/>
<polygon fill="none" stroke="black" points="929.57,-833 929.57,-859 1143.11,-859 1143.11,-833 929.57,-833"/>
<text xml:space="preserve" text-anchor="start" x="1017.44" y="-844" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00" fill="white">Phase 3</text>
<polygon fill="none" stroke="black" points="929.57,-769 929.57,-833 1143.11,-833 1143.11,-769 929.57,-769"/>
<text xml:space="preserve" text-anchor="start" x="992.44" y="-818" font-family="Helvetica,sans-Serif" font-size="10.00">TICK ALL ACTORS</text>
<text xml:space="preserve" text-anchor="start" x="987.71" y="-808" font-family="Helvetica,sans-Serif" font-size="10.00">for (addr, slot) in pool:</text>
<text xml:space="preserve" text-anchor="start" x="961.17" y="-798" font-family="Helvetica,sans-Serif" font-size="10.00"> &#160;while msg = mailbox.pop_front():</text>
<text xml:space="preserve" text-anchor="start" x="936.57" y="-788" font-family="Helvetica,sans-Serif" font-size="10.00"> &#160;&#160;&#160;catch_unwind(actor.handle_any(ctx, msg))</text>
<text xml:space="preserve" text-anchor="start" x="956.59" y="-778" font-family="Helvetica,sans-Serif" font-style="italic" font-size="10.00">actors may call ctx.send / ctx.spawn</text>
</g>
<!-- e_phase2&#45;&gt;e_phase3 -->
<g id="edge36" class="edge">
<title>e_phase2&#45;&gt;e_phase3</title>
<path fill="none" stroke="#333333" stroke-width="2" d="M816.25,-830.93C845.27,-828.69 877.88,-826.17 908.9,-823.77"/>
<polygon fill="#333333" stroke="#333333" stroke-width="2" points="908.9,-827.28 918.6,-823.02 908.36,-820.3 908.9,-827.28"/>
</g>
<!-- e_phase2&#45;&gt;e_pool -->
<g id="edge43" class="edge">
<title>e_phase2&#45;&gt;e_pool</title>
<path fill="none" stroke="#c0392b" stroke-dasharray="5,2" d="M816.42,-858.09C849.4,-864.44 887,-870.66 921.57,-874 1297.37,-910.3 1752.56,-870.38 1909.54,-854.19"/>
<polygon fill="#c0392b" stroke="#c0392b" points="1909.67,-857.69 1919.25,-853.17 1908.94,-850.73 1909.67,-857.69"/>
<text xml:space="preserve" text-anchor="middle" x="1325.53" y="-891.49" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#c0392b">deliver</text>
</g>
<!-- e_phase4 -->
<g id="node44" class="node">
<title>e_phase4</title>
<polygon fill="#27ae60" stroke="none" points="1247.13,-737 1247.13,-763 1403.93,-763 1403.93,-737 1247.13,-737"/>
<polygon fill="none" stroke="black" points="1247.13,-737 1247.13,-763 1403.93,-763 1403.93,-737 1247.13,-737"/>
<text xml:space="preserve" text-anchor="start" x="1306.63" y="-748" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00" fill="white">Phase 4</text>
<polygon fill="none" stroke="black" points="1247.13,-683 1247.13,-737 1403.93,-737 1403.93,-683 1247.13,-683"/>
<text xml:space="preserve" text-anchor="start" x="1254.13" y="-722" font-family="Helvetica,sans-Serif" font-size="10.00">DRAIN SPAWN QUEUE AGAIN</text>
<text xml:space="preserve" text-anchor="start" x="1254.95" y="-712" font-family="Helvetica,sans-Serif" font-style="italic" font-size="10.00">(picks up spawns from phase 3)</text>
<text xml:space="preserve" text-anchor="start" x="1281.63" y="-702" font-family="Helvetica,sans-Serif" font-size="10.00">spawn_rx.try_recv()</text>
<text xml:space="preserve" text-anchor="start" x="1279.96" y="-692" font-family="Helvetica,sans-Serif" font-size="10.00">→ ActorPool::insert()</text>
</g>
<!-- e_phase3&#45;&gt;e_phase4 -->
<g id="edge37" class="edge">
<title>e_phase3&#45;&gt;e_phase4</title>
<path fill="none" stroke="#333333" stroke-width="2" d="M1150.85,-778.03C1176.06,-770.04 1202.57,-761.64 1227.08,-753.88"/>
<polygon fill="#333333" stroke="#333333" stroke-width="2" points="1227.95,-757.27 1236.43,-750.92 1225.84,-750.6 1227.95,-757.27"/>
</g>
<!-- e_spawn_rx -->
<g id="node47" class="node">
<title>e_spawn_rx</title>
<path fill="#d1f2eb" stroke="black" d="M180.12,-765C180.12,-767.21 160.22,-769 135.72,-769 111.22,-769 91.31,-767.21 91.31,-765 91.31,-765 91.31,-729 91.31,-729 91.31,-726.79 111.22,-725 135.72,-725 160.22,-725 180.12,-726.79 180.12,-729 180.12,-729 180.12,-765 180.12,-765"/>
<path fill="none" stroke="black" d="M180.12,-765C180.12,-762.79 160.22,-761 135.72,-761 111.22,-761 91.31,-762.79 91.31,-765"/>
<text xml:space="preserve" text-anchor="middle" x="135.72" y="-750" font-family="Helvetica,sans-Serif" font-size="10.00">spawn_rx</text>
<text xml:space="preserve" text-anchor="middle" x="135.72" y="-738" font-family="Helvetica,sans-Serif" font-size="10.00">(HybridChannel)</text>
</g>
<!-- e_phase3&#45;&gt;e_spawn_rx -->
<g id="edge46" class="edge">
<title>e_phase3&#45;&gt;e_spawn_rx</title>
<path fill="none" stroke="#27ae60" stroke-dasharray="5,2" d="M958.11,-765.07C917.63,-742.28 866.25,-717.67 816.56,-706 589.3,-652.62 308.24,-705.96 191.51,-733.16"/>
<polygon fill="#27ae60" stroke="#27ae60" points="190.81,-729.73 181.88,-735.44 192.42,-736.54 190.81,-729.73"/>
<text xml:space="preserve" text-anchor="middle" x="583.92" y="-697.12" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#27ae60">ctx.spawn →</text>
<text xml:space="preserve" text-anchor="middle" x="583.92" y="-686.32" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#27ae60">spawn_txs</text>
</g>
<!-- e_phase3&#45;&gt;e_pool -->
<g id="edge44" class="edge">
<title>e_phase3&#45;&gt;e_pool</title>
<path fill="none" stroke="#8e44ad" stroke-dasharray="5,2" d="M1162.09,-835.89C1165.8,-836.3 1169.48,-836.67 1173.11,-837 1448.24,-862.06 1779.25,-854.59 1909.44,-850.19"/>
<polygon fill="#8e44ad" stroke="#8e44ad" points="1162.73,-832.44 1152.39,-834.74 1161.91,-839.39 1162.73,-832.44"/>
<polygon fill="#8e44ad" stroke="#8e44ad" points="1909.29,-853.7 1919.16,-849.85 1909.04,-846.7 1909.29,-853.7"/>
<text xml:space="preserve" text-anchor="middle" x="1471.7" y="-867.13" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#8e44ad">pop mailbox +</text>
<text xml:space="preserve" text-anchor="middle" x="1471.7" y="-856.33" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#8e44ad">handle_any</text>
</g>
<!-- e_pending -->
<g id="node50" class="node">
<title>e_pending</title>
<path fill="#d6eaf8" stroke="black" d="M1355.71,-825C1355.71,-825 1295.35,-825 1295.35,-825 1289.35,-825 1283.35,-819 1283.35,-813 1283.35,-813 1283.35,-801 1283.35,-801 1283.35,-795 1289.35,-789 1295.35,-789 1295.35,-789 1355.71,-789 1355.71,-789 1361.71,-789 1367.71,-795 1367.71,-801 1367.71,-801 1367.71,-813 1367.71,-813 1367.71,-819 1361.71,-825 1355.71,-825"/>
<text xml:space="preserve" text-anchor="middle" x="1325.53" y="-810" font-family="Helvetica,sans-Serif" font-size="10.00">pending_local</text>
<text xml:space="preserve" text-anchor="middle" x="1325.53" y="-798" font-family="Helvetica,sans-Serif" font-size="10.00">(RefCell&lt;Vec&gt;)</text>
</g>
<!-- e_phase3&#45;&gt;e_pending -->
<g id="edge45" class="edge">
<title>e_phase3&#45;&gt;e_pending</title>
<path fill="none" stroke="#2980b9" stroke-dasharray="5,2" d="M1150.85,-811.23C1192.5,-810.22 1237.69,-809.12 1271.53,-808.29"/>
<polygon fill="#2980b9" stroke="#2980b9" points="1271.58,-811.79 1281.49,-808.05 1271.41,-804.79 1271.58,-811.79"/>
<text xml:space="preserve" text-anchor="middle" x="1195.12" y="-824" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#2980b9">ctx.send →</text>
<text xml:space="preserve" text-anchor="middle" x="1195.12" y="-813.2" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#2980b9">buffer local</text>
</g>
<!-- e_phase5 -->
<g id="node45" class="node">
<title>e_phase5</title>
<polygon fill="#2980b9" stroke="none" points="1539.47,-737 1539.47,-763 1741.36,-763 1741.36,-737 1539.47,-737"/>
<polygon fill="none" stroke="black" points="1539.47,-737 1539.47,-763 1741.36,-763 1741.36,-737 1539.47,-737"/>
<text xml:space="preserve" text-anchor="start" x="1621.51" y="-748" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00" fill="white">Phase 5</text>
<polygon fill="none" stroke="black" points="1539.47,-683 1539.47,-737 1741.36,-737 1741.36,-683 1539.47,-683"/>
<text xml:space="preserve" text-anchor="start" x="1577.06" y="-722" font-family="Helvetica,sans-Serif" font-size="10.00">DELIVER PENDING LOCAL</text>
<text xml:space="preserve" text-anchor="start" x="1546.47" y="-712" font-family="Helvetica,sans-Serif" font-size="10.00">for (addr, payload) in pending_local.take():</text>
<text xml:space="preserve" text-anchor="start" x="1564.82" y="-702" font-family="Helvetica,sans-Serif" font-size="10.00"> &#160;ActorPool::deliver(addr, payload)</text>
<text xml:space="preserve" text-anchor="start" x="1556.77" y="-692" font-family="Helvetica,sans-Serif" font-style="italic" font-size="10.00">same&#45;worker messages from phase 3</text>
</g>
<!-- e_phase4&#45;&gt;e_phase5 -->
<g id="edge38" class="edge">
<title>e_phase4&#45;&gt;e_phase5</title>
<path fill="none" stroke="#333333" stroke-width="2" d="M1411.62,-723C1444.57,-723 1482.91,-723 1518.63,-723"/>
<polygon fill="#333333" stroke="#333333" stroke-width="2" points="1518.63,-726.5 1528.63,-723 1518.63,-719.5 1518.63,-726.5"/>
</g>
<!-- e_phase4&#45;&gt;e_pool -->
<g id="edge48" class="edge">
<title>e_phase4&#45;&gt;e_pool</title>
<path fill="none" stroke="#27ae60" stroke-dasharray="5,2" d="M1411.81,-747.98C1448.44,-758.17 1491.85,-769.51 1531.47,-778 1665.97,-806.8 1826.17,-829.71 1909.4,-840.78"/>
<polygon fill="#27ae60" stroke="#27ae60" points="1908.75,-844.22 1919.12,-842.06 1909.67,-837.28 1908.75,-844.22"/>
<text xml:space="preserve" text-anchor="middle" x="1640.42" y="-820.1" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#27ae60">insert</text>
</g>
<!-- e_phase6 -->
<g id="node46" class="node">
<title>e_phase6</title>
<polygon fill="#e67300" stroke="none" points="1869.89,-745 1869.89,-771 2063.99,-771 2063.99,-745 1869.89,-745"/>
<polygon fill="none" stroke="black" points="1869.89,-745 1869.89,-771 2063.99,-771 2063.99,-745 1869.89,-745"/>
<text xml:space="preserve" text-anchor="start" x="1948.04" y="-756" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00" fill="white">Phase 6</text>
<polygon fill="none" stroke="black" points="1869.89,-683 1869.89,-745 2063.99,-745 2063.99,-683 1869.89,-683"/>
<text xml:space="preserve" text-anchor="start" x="1929.16" y="-729" font-family="Helvetica,sans-Serif" font-size="10.00">UPDATE STATS</text>
<text xml:space="preserve" text-anchor="start" x="1892.19" y="-717" font-family="Helvetica,sans-Serif" font-size="10.00">stats.num_actors.store(pool.len())</text>
<text xml:space="preserve" text-anchor="start" x="1890.24" y="-705" font-family="Helvetica,sans-Serif" font-size="10.00">stats.total_mailbox_depth.store(...)</text>
<text xml:space="preserve" text-anchor="start" x="1876.89" y="-693" font-family="Helvetica,sans-Serif" font-size="10.00">stats.messages_processed.fetch_add(n)</text>
</g>
<!-- e_phase5&#45;&gt;e_phase6 -->
<g id="edge39" class="edge">
<title>e_phase5&#45;&gt;e_phase6</title>
<path fill="none" stroke="#333333" stroke-width="2" d="M1749.03,-724.33C1781.12,-724.72 1816.43,-725.16 1849.08,-725.56"/>
<polygon fill="#333333" stroke="#333333" stroke-width="2" points="1848.86,-729.06 1858.9,-725.68 1848.94,-722.06 1848.86,-729.06"/>
</g>
<!-- e_phase5&#45;&gt;e_pool -->
<g id="edge50" class="edge">
<title>e_phase5&#45;&gt;e_pool</title>
<path fill="none" stroke="#2980b9" stroke-dasharray="5,2" d="M1749.23,-760.37C1778.76,-770.96 1810.67,-782.73 1839.89,-794.2 1863.11,-803.31 1888.46,-813.98 1910.29,-823.41"/>
<polygon fill="#2980b9" stroke="#2980b9" points="1908.74,-826.56 1919.31,-827.32 1911.53,-820.13 1908.74,-826.56"/>
<text xml:space="preserve" text-anchor="middle" x="1805.63" y="-796.9" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#2980b9">deliver</text>
</g>
<!-- e_stats -->
<g id="node51" class="node">
<title>e_stats</title>
<path fill="#d1c4e9" stroke="black" d="M2245.94,-745C2245.94,-745 2192.27,-745 2192.27,-745 2186.27,-745 2180.27,-739 2180.27,-733 2180.27,-733 2180.27,-721 2180.27,-721 2180.27,-715 2186.27,-709 2192.27,-709 2192.27,-709 2245.94,-709 2245.94,-709 2251.94,-709 2257.94,-715 2257.94,-721 2257.94,-721 2257.94,-733 2257.94,-733 2257.94,-739 2251.94,-745 2245.94,-745"/>
<text xml:space="preserve" text-anchor="middle" x="2219.1" y="-730" font-family="Helvetica,sans-Serif" font-size="10.00">WorkerStats</text>
<text xml:space="preserve" text-anchor="middle" x="2219.1" y="-718" font-family="Helvetica,sans-Serif" font-size="10.00">(Arc, atomics)</text>
</g>
<!-- e_phase6&#45;&gt;e_stats -->
<g id="edge51" class="edge">
<title>e_phase6&#45;&gt;e_stats</title>
<path fill="none" stroke="#e67300" stroke-width="1.5" d="M2071.86,-727C2105.04,-727 2140.26,-727 2167.83,-727"/>
<polygon fill="#e67300" stroke="#e67300" stroke-width="1.5" points="2167.65,-730.5 2177.65,-727 2167.65,-723.5 2167.65,-730.5"/>
<text xml:space="preserve" text-anchor="middle" x="2125.01" y="-729.7" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#e67300">store/fetch_add</text>
</g>
<!-- e_spawn_rx&#45;&gt;e_phase1 -->
<g id="edge40" class="edge">
<title>e_spawn_rx&#45;&gt;e_phase1</title>
<path fill="none" stroke="#27ae60" stroke-width="1.5" d="M180.48,-764.52C225.94,-782.72 298.48,-811.76 357.07,-835.22"/>
<polygon fill="#27ae60" stroke="#27ae60" stroke-width="1.5" points="355.52,-838.37 366.11,-838.84 358.13,-831.87 355.52,-838.37"/>
<text xml:space="preserve" text-anchor="middle" x="311.7" y="-831.35" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#27ae60">(addr, actor_box)</text>
</g>
<!-- e_spawn_rx&#45;&gt;e_phase4 -->
<g id="edge47" class="edge">
<title>e_spawn_rx&#45;&gt;e_phase4</title>
<path fill="none" stroke="#27ae60" stroke-width="1.5" d="M180.48,-743.82C241.24,-739.69 354.63,-733 451.44,-733 451.44,-733 451.44,-733 1037.34,-733 1100.67,-733 1171.8,-730.54 1227.44,-728.06"/>
<polygon fill="#27ae60" stroke="#27ae60" stroke-width="1.5" points="1227.32,-731.57 1237.15,-727.62 1227,-724.58 1227.32,-731.57"/>
<text xml:space="preserve" text-anchor="middle" x="723.74" y="-735.7" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#27ae60">(addr, actor_box)</text>
</g>
<!-- e_transfer_rx -->
<g id="node48" class="node">
<title>e_transfer_rx</title>
<path fill="#d1f2eb" stroke="black" d="M496.84,-803C496.84,-805.21 476.94,-807 452.44,-807 427.94,-807 408.04,-805.21 408.04,-803 408.04,-803 408.04,-767 408.04,-767 408.04,-764.79 427.94,-763 452.44,-763 476.94,-763 496.84,-764.79 496.84,-767 496.84,-767 496.84,-803 496.84,-803"/>
<path fill="none" stroke="black" d="M496.84,-803C496.84,-800.79 476.94,-799 452.44,-799 427.94,-799 408.04,-800.79 408.04,-803"/>
<text xml:space="preserve" text-anchor="middle" x="452.44" y="-788" font-family="Helvetica,sans-Serif" font-size="10.00">transfer_rx</text>
<text xml:space="preserve" text-anchor="middle" x="452.44" y="-776" font-family="Helvetica,sans-Serif" font-size="10.00">(HybridChannel)</text>
</g>
<!-- e_transfer_rx&#45;&gt;e_phase2 -->
<g id="edge41" class="edge">
<title>e_transfer_rx&#45;&gt;e_phase2</title>
<path fill="none" stroke="#c0392b" stroke-width="1.5" d="M497.31,-793.63C530.35,-800.14 577.03,-809.32 619.35,-817.65"/>
<polygon fill="#c0392b" stroke="#c0392b" stroke-width="1.5" points="618.44,-821.04 628.93,-819.54 619.8,-814.17 618.44,-821.04"/>
<text xml:space="preserve" text-anchor="middle" x="583.92" y="-817.37" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#c0392b">Envelope</text>
</g>
<!-- e_pending&#45;&gt;e_phase5 -->
<g id="edge49" class="edge">
<title>e_pending&#45;&gt;e_phase5</title>
<path fill="none" stroke="#2980b9" stroke-width="1.5" d="M1368.08,-795.85C1406.8,-785.45 1466.44,-769.44 1519.96,-755.07"/>
<polygon fill="#2980b9" stroke="#2980b9" stroke-width="1.5" points="1520.68,-758.5 1529.43,-752.53 1518.87,-751.74 1520.68,-758.5"/>
<text xml:space="preserve" text-anchor="middle" x="1471.7" y="-780.13" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#2980b9">(addr, payload)</text>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 68 KiB

View file

@ -0,0 +1,155 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1100 800" font-family="system-ui, sans-serif">
<defs>
<filter id="shadow" x="-4%" y="-4%" width="108%" height="108%">
<feDropShadow dx="1" dy="2" stdDeviation="3" flood-color="#000" flood-opacity="0.10"/>
</filter>
<marker id="arrow-gray" viewBox="0 0 10 7" refX="10" refY="3.5"
markerWidth="10" markerHeight="7" orient="auto-start-reverse" fill="#5f6368">
<polygon points="0 0, 10 3.5, 0 7"/>
</marker>
<marker id="arrow-green" viewBox="0 0 10 7" refX="10" refY="3.5"
markerWidth="10" markerHeight="7" orient="auto-start-reverse" fill="#34a853">
<polygon points="0 0, 10 3.5, 0 7"/>
</marker>
<marker id="arrow-red" viewBox="0 0 10 7" refX="10" refY="3.5"
markerWidth="10" markerHeight="7" orient="auto-start-reverse" fill="#ea4335">
<polygon points="0 0, 10 3.5, 0 7"/>
</marker>
<marker id="arrow-blue" viewBox="0 0 10 7" refX="10" refY="3.5"
markerWidth="10" markerHeight="7" orient="auto-start-reverse" fill="#4285f4">
<polygon points="0 0, 10 3.5, 0 7"/>
</marker>
</defs>
<!-- Background -->
<rect width="1100" height="800" rx="8" fill="#f8f9fb" stroke="#e0e3e8" stroke-width="1.5"/>
<!-- Title -->
<text x="550" y="40" text-anchor="middle" font-size="20" font-weight="bold" fill="#202124">Message Lifecycle</text>
<text x="550" y="60" text-anchor="middle" font-size="12" fill="#5f6368">src/worker.rs · src/delivery.rs · src/channel.rs · src/actor.rs</text>
<!-- ============================================ -->
<!-- SWIM LANE: ORIGIN -->
<!-- ============================================ -->
<rect x="30" y="80" width="1040" height="120" rx="6" fill="#e8f0fe" fill-opacity="0.25" stroke="#4285f4" stroke-width="1" stroke-opacity="0.4"/>
<text x="50" y="100" font-size="13" font-weight="700" fill="#4285f4" letter-spacing="1">ORIGIN</text>
<!-- Entry 1: runtime.send_to -->
<rect x="150" y="110" width="200" height="52" rx="12" fill="#e8f0fe" stroke="#4285f4" stroke-width="1.8" filter="url(#shadow)"/>
<text x="250" y="133" text-anchor="middle" font-size="13" font-weight="600" fill="#1a73e8">runtime.send_to(</text>
<text x="250" y="150" text-anchor="middle" font-size="13" font-weight="600" fill="#1a73e8">addr, msg)</text>
<!-- Entry 2: ctx.send -->
<rect x="700" y="110" width="200" height="52" rx="12" fill="#e8f0fe" stroke="#4285f4" stroke-width="1.8" filter="url(#shadow)"/>
<text x="800" y="133" text-anchor="middle" font-size="13" font-weight="600" fill="#1a73e8">ctx.send(</text>
<text x="800" y="150" text-anchor="middle" font-size="13" font-weight="600" fill="#1a73e8">addr, msg)</text>
<text x="160" y="187" font-size="10" fill="#5f6368">external caller</text>
<text x="710" y="187" font-size="10" fill="#5f6368">from actor handler</text>
<!-- Arrows from both entries down into routing -->
<line x1="250" y1="162" x2="250" y2="220" stroke="#5f6368" stroke-width="1.8" marker-end="url(#arrow-gray)"/>
<line x1="800" y1="162" x2="800" y2="220" stroke="#5f6368" stroke-width="1.8" marker-end="url(#arrow-gray)"/>
<!-- Converge arrows to decision diamond -->
<path d="M 250 220 Q 250 240 460 255" fill="none" stroke="#5f6368" stroke-width="1.8"/>
<path d="M 800 220 Q 800 240 590 255" fill="none" stroke="#5f6368" stroke-width="1.8"/>
<!-- ============================================ -->
<!-- SWIM LANE: ROUTING -->
<!-- ============================================ -->
<rect x="30" y="210" width="1040" height="260" rx="6" fill="#fef7e0" fill-opacity="0.2" stroke="#f9ab00" stroke-width="1" stroke-opacity="0.4"/>
<text x="50" y="232" font-size="13" font-weight="700" fill="#e37400" letter-spacing="1">ROUTING</text>
<!-- Decision diamond: AddressMap::lookup -->
<polygon points="525,250 620,290 525,330 430,290" fill="#fef7e0" stroke="#f9ab00" stroke-width="1.8" filter="url(#shadow)"/>
<text x="525" y="286" text-anchor="middle" font-size="11" font-weight="600" fill="#e37400">AddressMap</text>
<text x="525" y="300" text-anchor="middle" font-size="11" font-weight="600" fill="#e37400">::lookup()</text>
<!-- Branch LEFT: Not found → InboxRegistry -->
<line x1="430" y1="290" x2="265" y2="290" stroke="#5f6368" stroke-width="1.8" marker-end="url(#arrow-gray)"/>
<text x="350" y="282" text-anchor="middle" font-size="11" fill="#5f6368" font-weight="600">not found</text>
<rect x="100" y="265" width="165" height="50" rx="12" fill="#f1f3f4" stroke="#9aa0a6" stroke-width="1.5" filter="url(#shadow)"/>
<text x="182" y="286" text-anchor="middle" font-size="12" font-weight="600" fill="#5f6368">InboxRegistry</text>
<text x="182" y="302" text-anchor="middle" font-size="10" fill="#9aa0a6">::try_deliver()</text>
<!-- InboxRegistry → External Receiver -->
<line x1="182" y1="315" x2="182" y2="365" stroke="#5f6368" stroke-width="1.5" marker-end="url(#arrow-gray)"/>
<rect x="100" y="370" width="165" height="44" rx="10" fill="#e6f4ea" stroke="#34a853" stroke-width="1.5" filter="url(#shadow)"/>
<text x="182" y="390" text-anchor="middle" font-size="12" font-weight="600" fill="#137333">Receiver&lt;M&gt;</text>
<text x="182" y="405" text-anchor="middle" font-size="10" fill="#5f6368">external consumer</text>
<!-- Branch DOWN: Same worker → pending_local -->
<line x1="525" y1="330" x2="525" y2="385" stroke="#5f6368" stroke-width="1.8" marker-end="url(#arrow-gray)"/>
<text x="536" y="355" font-size="11" fill="#5f6368" font-weight="600">same</text>
<text x="536" y="368" font-size="10" fill="#5f6368">worker</text>
<rect x="435" y="390" width="180" height="50" rx="12" fill="#fef7e0" stroke="#f9ab00" stroke-width="1.8" filter="url(#shadow)"/>
<text x="525" y="412" text-anchor="middle" font-size="12" font-weight="600" fill="#e37400">pending_local</text>
<text x="525" y="428" text-anchor="middle" font-size="10" fill="#5f6368">RefCell&lt;Vec&gt; buffer</text>
<text x="525" y="458" text-anchor="middle" font-size="10" fill="#f9ab00" font-weight="600">delivered in Phase 5</text>
<!-- Branch RIGHT: Different worker → HybridChannel -->
<line x1="620" y1="290" x2="750" y2="290" stroke="#5f6368" stroke-width="1.8" marker-end="url(#arrow-gray)"/>
<text x="685" y="282" text-anchor="middle" font-size="11" fill="#5f6368" font-weight="600">different</text>
<text x="685" y="296" text-anchor="middle" font-size="10" fill="#5f6368">worker</text>
<rect x="752" y="260" width="185" height="60" rx="12" fill="#e8f0fe" stroke="#4285f4" stroke-width="1.8" filter="url(#shadow)"/>
<text x="844" y="281" text-anchor="middle" font-size="12" font-weight="600" fill="#1a73e8">Envelope::new()</text>
<text x="844" y="298" text-anchor="middle" font-size="10" fill="#5f6368">→ HybridChannel</text>
<text x="844" y="312" text-anchor="middle" font-size="9" fill="#9aa0a6">ArrayQueue + SegQueue</text>
<text x="844" y="340" text-anchor="middle" font-size="10" fill="#4285f4" font-weight="600">drained in Phase 2</text>
<!-- Arrows from pending_local and HybridChannel converge down -->
<path d="M 525 440 L 525 500" fill="none" stroke="#5f6368" stroke-width="1.8" marker-end="url(#arrow-gray)"/>
<path d="M 844 320 Q 844 460 660 500" fill="none" stroke="#5f6368" stroke-width="1.8" marker-end="url(#arrow-gray)"/>
<!-- ============================================ -->
<!-- SWIM LANE: DELIVERY -->
<!-- ============================================ -->
<rect x="30" y="480" width="1040" height="290" rx="6" fill="#e6f4ea" fill-opacity="0.2" stroke="#34a853" stroke-width="1" stroke-opacity="0.4"/>
<text x="50" y="500" font-size="13" font-weight="700" fill="#34a853" letter-spacing="1">DELIVERY</text>
<!-- pool.deliver -->
<rect x="430" y="510" width="200" height="50" rx="12" fill="#e6f4ea" stroke="#34a853" stroke-width="1.8" filter="url(#shadow)"/>
<text x="530" y="532" text-anchor="middle" font-size="12" font-weight="600" fill="#137333">pool.deliver(addr, payload)</text>
<text x="530" y="548" text-anchor="middle" font-size="10" fill="#5f6368">push to VecDeque mailbox</text>
<!-- Arrow down to Phase 3 -->
<line x1="530" y1="560" x2="530" y2="600" stroke="#5f6368" stroke-width="1.8" marker-end="url(#arrow-gray)"/>
<text x="542" y="584" font-size="10" fill="#5f6368">Phase 3</text>
<!-- mailbox.pop_front -->
<rect x="420" y="605" width="220" height="48" rx="12" fill="#e8f0fe" stroke="#4285f4" stroke-width="1.8" filter="url(#shadow)"/>
<text x="530" y="625" text-anchor="middle" font-size="12" font-weight="600" fill="#1a73e8">mailbox.pop_front()</text>
<text x="530" y="641" text-anchor="middle" font-size="10" fill="#5f6368">→ AnyActor::handle_any()</text>
<!-- Arrow down to downcast diamond -->
<line x1="530" y1="653" x2="530" y2="685" stroke="#5f6368" stroke-width="1.8" marker-end="url(#arrow-gray)"/>
<!-- Downcast diamond -->
<polygon points="530,688 590,715 530,742 470,715" fill="#fef7e0" stroke="#f9ab00" stroke-width="1.5" filter="url(#shadow)"/>
<text x="530" y="718" text-anchor="middle" font-size="10" font-weight="600" fill="#e37400">downcast</text>
<!-- Success branch (left) -->
<line x1="470" y1="715" x2="350" y2="715" stroke="#34a853" stroke-width="1.8" marker-end="url(#arrow-green)"/>
<rect x="200" y="695" width="150" height="40" rx="10" fill="#e6f4ea" stroke="#34a853" stroke-width="1.5" filter="url(#shadow)"/>
<text x="275" y="712" text-anchor="middle" font-size="12" font-weight="600" fill="#137333">User Handler</text>
<text x="275" y="726" text-anchor="middle" font-size="10" fill="#34a853">called</text>
<!-- Failure branch (right) -->
<line x1="590" y1="715" x2="710" y2="715" stroke="#ea4335" stroke-width="1.8" marker-end="url(#arrow-red)"/>
<rect x="710" y="695" width="150" height="40" rx="10" fill="#fce8e6" stroke="#ea4335" stroke-width="1.5" filter="url(#shadow)"/>
<text x="785" y="712" text-anchor="middle" font-size="12" font-weight="600" fill="#c5221f">Dropped</text>
<text x="785" y="726" text-anchor="middle" font-size="10" fill="#ea4335">type mismatch</text>
<!-- Labels on branches -->
<text x="410" y="708" text-anchor="middle" font-size="10" fill="#34a853" font-weight="600">OK</text>
<text x="650" y="708" text-anchor="middle" font-size="10" fill="#ea4335" font-weight="600">fail</text>
<!-- Source refs bottom -->
<text x="550" y="780" text-anchor="middle" font-size="10" fill="#9aa0a6">worker.rs:134-151 · delivery.rs:92-116 · channel.rs · actor.rs:53-59</text>
</svg>

After

Width:  |  Height:  |  Size: 10 KiB

View file

@ -0,0 +1,81 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
DOCS_DIR="$SCRIPT_DIR"
OUT_DIR="$DOCS_DIR"
TOOLS_DIR="$(cd "$SCRIPT_DIR/../../../tools" && pwd)"
mkdir -p "$OUT_DIR"
# Collect .dot sources
dots=("$DOCS_DIR"/*.dot)
if [ ${#dots[@]} -eq 0 ]; then
echo "No .dot files found in $DOCS_DIR"
exit 0
fi
# Pick a renderer: prefer graphviz `dot`, fall back to @viz-js/viz via Node
render_with_dot() {
for src in "${dots[@]}"; do
name="$(basename "$src" .dot)"
echo " dot: $name.dot -> generated/$name.svg"
dot -Tsvg "$src" -o "$OUT_DIR/$name.svg"
done
}
render_with_vizjs() {
local tmpfile
tmpfile="$(mktemp "${TMPDIR:-/tmp}/render_docs.XXXXXX.mjs")"
trap 'rm -f "$tmpfile"' RETURN
cat > "$tmpfile" <<NODEJS
import { createRequire } from "module";
import { readFileSync, writeFileSync, readdirSync } from "fs";
import { join, basename } from "path";
const require = createRequire("$TOOLS_DIR/package.json");
const { instance } = require("@viz-js/viz");
const docsDir = "$DOCS_DIR";
const outDir = "$OUT_DIR";
const viz = await instance();
const dots = readdirSync(docsDir).filter(f => f.endsWith(".dot"));
for (const file of dots) {
const src = readFileSync(join(docsDir, file), "utf-8");
const name = basename(file, ".dot");
const svg = viz.renderString(src, { format: "svg" });
writeFileSync(join(outDir, \`\${name}.svg\`), svg);
console.log(\` viz-js: \${file} -> generated/\${name}.svg\`);
}
NODEJS
node "$tmpfile"
}
echo "Rendering DOT diagrams..."
if command -v dot &>/dev/null; then
render_with_dot
elif command -v node &>/dev/null; then
# Ensure @viz-js/viz is available
if [ -f "$TOOLS_DIR/package.json" ]; then
if ! [ -d "$TOOLS_DIR/node_modules/@viz-js/viz" ]; then
echo "Installing @viz-js/viz..."
(cd "$TOOLS_DIR" && npm install --silent)
fi
else
echo "Error: tools/package.json not found at $TOOLS_DIR" >&2
exit 1
fi
render_with_vizjs
else
echo "Error: No renderer available." >&2
echo "Install graphviz (apt install graphviz) or Node.js." >&2
exit 1
fi
echo "Done. Output in ${OUT_DIR}"

View file

@ -0,0 +1,229 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 900" font-family="system-ui, sans-serif">
<defs>
<filter id="shadow" x="-4%" y="-4%" width="108%" height="108%">
<feDropShadow dx="1" dy="2" stdDeviation="3" flood-color="#000" flood-opacity="0.10"/>
</filter>
<marker id="arrow-gray" viewBox="0 0 10 7" refX="10" refY="3.5"
markerWidth="10" markerHeight="7" orient="auto-start-reverse" fill="#5f6368">
<polygon points="0 0, 10 3.5, 0 7"/>
</marker>
<marker id="arrow-green" viewBox="0 0 10 7" refX="10" refY="3.5"
markerWidth="10" markerHeight="7" orient="auto-start-reverse" fill="#34a853">
<polygon points="0 0, 10 3.5, 0 7"/>
</marker>
<marker id="arrow-red" viewBox="0 0 10 7" refX="10" refY="3.5"
markerWidth="10" markerHeight="7" orient="auto-start-reverse" fill="#ea4335">
<polygon points="0 0, 10 3.5, 0 7"/>
</marker>
<marker id="arrow-blue" viewBox="0 0 10 7" refX="10" refY="3.5"
markerWidth="10" markerHeight="7" orient="auto-start-reverse" fill="#4285f4">
<polygon points="0 0, 10 3.5, 0 7"/>
</marker>
</defs>
<!-- Background -->
<rect width="1200" height="900" rx="8" fill="#f8f9fb" stroke="#e0e3e8" stroke-width="1.5"/>
<!-- Title -->
<text x="600" y="40" text-anchor="middle" font-size="20" font-weight="bold" fill="#202124">Runtime Lifecycle</text>
<text x="600" y="60" text-anchor="middle" font-size="12" fill="#5f6368">src/runtime.rs · src/worker.rs · src/config.rs</text>
<!-- ============================================ -->
<!-- SECTION 1: CONSTRUCTION -->
<!-- ============================================ -->
<rect x="30" y="80" width="1140" height="145" rx="6" fill="#e8f0fe" fill-opacity="0.18" stroke="#4285f4" stroke-width="1" stroke-opacity="0.4"/>
<text x="50" y="102" font-size="13" font-weight="700" fill="#4285f4" letter-spacing="1">CONSTRUCTION</text>
<text x="1140" y="102" text-anchor="end" font-size="10" fill="#9aa0a6">runtime.rs:80-122</text>
<!-- Step 1: Runtime::new -->
<rect x="55" y="118" width="175" height="50" rx="12" fill="#e8f0fe" stroke="#4285f4" stroke-width="1.8" filter="url(#shadow)"/>
<text x="142" y="140" text-anchor="middle" font-size="12" font-weight="600" fill="#1a73e8">Runtime::new(config)</text>
<text x="142" y="156" text-anchor="middle" font-size="10" fill="#5f6368">parse RuntimeConfig</text>
<line x1="230" y1="143" x2="285" y2="143" stroke="#5f6368" stroke-width="1.8" marker-end="url(#arrow-gray)"/>
<!-- Step 2: Create Workers -->
<rect x="290" y="118" width="175" height="50" rx="12" fill="#e6f4ea" stroke="#34a853" stroke-width="1.8" filter="url(#shadow)"/>
<text x="377" y="140" text-anchor="middle" font-size="12" font-weight="600" fill="#137333">Create Workers</text>
<text x="377" y="156" text-anchor="middle" font-size="10" fill="#5f6368">N per config.workers</text>
<line x1="465" y1="143" x2="520" y2="143" stroke="#5f6368" stroke-width="1.8" marker-end="url(#arrow-gray)"/>
<!-- Step 3: Allocate Channels -->
<rect x="525" y="118" width="195" height="50" rx="12" fill="#fef7e0" stroke="#f9ab00" stroke-width="1.8" filter="url(#shadow)"/>
<text x="622" y="138" text-anchor="middle" font-size="12" font-weight="600" fill="#e37400">Allocate Channels</text>
<text x="622" y="154" text-anchor="middle" font-size="10" fill="#5f6368">transfer_tx/rx + spawn_tx/rx</text>
<text x="622" y="165" text-anchor="middle" font-size="9" fill="#9aa0a6">per worker</text>
<line x1="720" y1="143" x2="775" y2="143" stroke="#5f6368" stroke-width="1.8" marker-end="url(#arrow-gray)"/>
<!-- Step 4: Init Shared State -->
<rect x="780" y="118" width="195" height="50" rx="12" fill="#e8f0fe" stroke="#4285f4" stroke-width="1.8" filter="url(#shadow)"/>
<text x="877" y="138" text-anchor="middle" font-size="12" font-weight="600" fill="#1a73e8">Init Shared State</text>
<text x="877" y="155" text-anchor="middle" font-size="10" fill="#5f6368">AddressMap, InboxRegistry,</text>
<text x="877" y="165" text-anchor="middle" font-size="9" fill="#5f6368">Placement</text>
<!-- Arrow from construction down to tick cycle -->
<line x1="600" y1="195" x2="600" y2="245" stroke="#5f6368" stroke-width="1.8" marker-end="url(#arrow-gray)"/>
<text x="612" y="222" font-size="10" fill="#5f6368">ready</text>
<!-- ============================================ -->
<!-- SECTION 2: TICK CYCLE -->
<!-- ============================================ -->
<rect x="30" y="250" width="1140" height="280" rx="6" fill="#e6f4ea" fill-opacity="0.15" stroke="#34a853" stroke-width="1" stroke-opacity="0.4"/>
<text x="50" y="272" font-size="13" font-weight="700" fill="#34a853" letter-spacing="1">TICK CYCLE</text>
<text x="1140" y="272" text-anchor="end" font-size="10" fill="#9aa0a6">worker.rs:40-96</text>
<!-- Phase boxes - row of 6 -->
<!-- Phase 1 -->
<rect x="55" y="295" width="155" height="80" rx="12" fill="#e8f0fe" stroke="#4285f4" stroke-width="1.8" filter="url(#shadow)"/>
<circle cx="75" cy="311" r="12" fill="#4285f4"/>
<text x="75" y="316" text-anchor="middle" font-size="11" fill="#fff" font-weight="bold">1</text>
<text x="132" y="316" text-anchor="middle" font-size="12" font-weight="600" fill="#1a73e8">Drain Spawns</text>
<text x="132" y="335" text-anchor="middle" font-size="10" fill="#5f6368">spawn_rx →</text>
<text x="132" y="349" text-anchor="middle" font-size="10" fill="#5f6368">pool.insert()</text>
<text x="132" y="366" text-anchor="middle" font-size="9" fill="#9aa0a6">new actors join</text>
<line x1="210" y1="335" x2="240" y2="335" stroke="#5f6368" stroke-width="1.5" marker-end="url(#arrow-gray)"/>
<!-- Phase 2 -->
<rect x="245" y="295" width="155" height="80" rx="12" fill="#fef7e0" stroke="#f9ab00" stroke-width="1.8" filter="url(#shadow)"/>
<circle cx="265" cy="311" r="12" fill="#f9ab00"/>
<text x="265" y="316" text-anchor="middle" font-size="11" fill="#fff" font-weight="bold">2</text>
<text x="322" y="316" text-anchor="middle" font-size="12" font-weight="600" fill="#e37400">Drain Transfers</text>
<text x="322" y="335" text-anchor="middle" font-size="10" fill="#5f6368">transfer_rx →</text>
<text x="322" y="349" text-anchor="middle" font-size="10" fill="#5f6368">pool.deliver()</text>
<text x="322" y="366" text-anchor="middle" font-size="9" fill="#9aa0a6">cross-worker msgs</text>
<line x1="400" y1="335" x2="430" y2="335" stroke="#5f6368" stroke-width="1.5" marker-end="url(#arrow-gray)"/>
<!-- Phase 3 -->
<rect x="435" y="295" width="155" height="80" rx="12" fill="#e6f4ea" stroke="#34a853" stroke-width="1.8" filter="url(#shadow)"/>
<circle cx="455" cy="311" r="12" fill="#34a853"/>
<text x="455" y="316" text-anchor="middle" font-size="11" fill="#fff" font-weight="bold">3</text>
<text x="512" y="316" text-anchor="middle" font-size="12" font-weight="600" fill="#137333">tick_all()</text>
<text x="512" y="335" text-anchor="middle" font-size="10" fill="#5f6368">pop_front → handle</text>
<text x="512" y="349" text-anchor="middle" font-size="10" fill="#5f6368">+ catch_unwind</text>
<text x="512" y="366" text-anchor="middle" font-size="9" fill="#9aa0a6">process messages</text>
<line x1="590" y1="335" x2="620" y2="335" stroke="#5f6368" stroke-width="1.5" marker-end="url(#arrow-gray)"/>
<!-- Phase 4 -->
<rect x="625" y="295" width="155" height="80" rx="12" fill="#e8f0fe" stroke="#4285f4" stroke-width="1.8" filter="url(#shadow)"/>
<circle cx="645" cy="311" r="12" fill="#4285f4"/>
<text x="645" y="316" text-anchor="middle" font-size="11" fill="#fff" font-weight="bold">4</text>
<text x="712" y="316" text-anchor="middle" font-size="12" font-weight="600" fill="#1a73e8">Drain Spawns</text>
<text x="712" y="335" text-anchor="middle" font-size="10" fill="#5f6368">again: actors</text>
<text x="712" y="349" text-anchor="middle" font-size="10" fill="#5f6368">spawned in Phase 3</text>
<text x="712" y="366" text-anchor="middle" font-size="9" fill="#9aa0a6">handler-created</text>
<line x1="780" y1="335" x2="810" y2="335" stroke="#5f6368" stroke-width="1.5" marker-end="url(#arrow-gray)"/>
<!-- Phase 5 -->
<rect x="815" y="295" width="155" height="80" rx="12" fill="#fef7e0" stroke="#f9ab00" stroke-width="1.8" filter="url(#shadow)"/>
<circle cx="835" cy="311" r="12" fill="#f9ab00"/>
<text x="835" y="316" text-anchor="middle" font-size="11" fill="#fff" font-weight="bold">5</text>
<text x="892" y="316" text-anchor="middle" font-size="12" font-weight="600" fill="#e37400">pending_local</text>
<text x="892" y="335" text-anchor="middle" font-size="10" fill="#5f6368">drain buffer →</text>
<text x="892" y="349" text-anchor="middle" font-size="10" fill="#5f6368">pool.deliver()</text>
<text x="892" y="366" text-anchor="middle" font-size="9" fill="#9aa0a6">same-worker msgs</text>
<line x1="970" y1="335" x2="1000" y2="335" stroke="#5f6368" stroke-width="1.5" marker-end="url(#arrow-gray)"/>
<!-- Phase 6 -->
<rect x="1005" y="295" width="145" height="80" rx="12" fill="#f1f3f4" stroke="#9aa0a6" stroke-width="1.8" filter="url(#shadow)"/>
<circle cx="1025" cy="311" r="12" fill="#9aa0a6"/>
<text x="1025" y="316" text-anchor="middle" font-size="11" fill="#fff" font-weight="bold">6</text>
<text x="1077" y="316" text-anchor="middle" font-size="12" font-weight="600" fill="#5f6368">Publish Stats</text>
<text x="1077" y="335" text-anchor="middle" font-size="10" fill="#5f6368">update atomics</text>
<text x="1077" y="349" text-anchor="middle" font-size="10" fill="#5f6368">WorkerStats</text>
<!-- Large curved loop-back arrow from Phase 6 back to Phase 1 -->
<path d="M 1077 375 Q 1077 430 900 445 Q 600 465 300 445 Q 132 430 132 395"
fill="none" stroke="#34a853" stroke-width="2.5" marker-end="url(#arrow-green)" stroke-dasharray="8,4"/>
<text x="600" y="470" text-anchor="middle" font-size="12" fill="#34a853" font-weight="600">next tick iteration</text>
<!-- tick_once label -->
<text x="600" y="510" text-anchor="middle" font-size="11" fill="#5f6368" font-style="italic">one call to tick_once() executes phases 1–6</text>
<!-- ============================================ -->
<!-- SECTION 3: EXECUTION MODES -->
<!-- ============================================ -->
<rect x="30" y="540" width="1140" height="330" rx="6" fill="#fef7e0" fill-opacity="0.12" stroke="#f9ab00" stroke-width="1" stroke-opacity="0.4"/>
<text x="50" y="562" font-size="13" font-weight="700" fill="#e37400" letter-spacing="1">EXECUTION MODES</text>
<text x="1140" y="562" text-anchor="end" font-size="10" fill="#9aa0a6">runtime.rs:185-217</text>
<!-- Mode A: tick() -->
<rect x="55" y="580" width="520" height="200" rx="10" fill="#fff" stroke="#4285f4" stroke-width="1.5" filter="url(#shadow)"/>
<rect x="55" y="580" width="520" height="32" rx="10" fill="#4285f4"/>
<rect x="55" y="600" width="520" height="12" fill="#4285f4"/>
<text x="315" y="601" text-anchor="middle" font-size="14" font-weight="bold" fill="#fff">tick() mode — single-threaded</text>
<!-- tick() flow -->
<rect x="80" y="630" width="170" height="42" rx="8" fill="#e8f0fe" stroke="#4285f4" stroke-width="1.2"/>
<text x="165" y="648" text-anchor="middle" font-size="11" font-weight="600" fill="#1a73e8">user calls</text>
<text x="165" y="663" text-anchor="middle" font-size="11" font-weight="600" fill="#1a73e8">runtime.tick()</text>
<line x1="250" y1="651" x2="290" y2="651" stroke="#5f6368" stroke-width="1.5" marker-end="url(#arrow-gray)"/>
<rect x="295" y="630" width="170" height="42" rx="8" fill="#e6f4ea" stroke="#34a853" stroke-width="1.2"/>
<text x="380" y="648" text-anchor="middle" font-size="11" font-weight="600" fill="#137333">iterate</text>
<text x="380" y="663" text-anchor="middle" font-size="11" font-weight="600" fill="#137333">tick_workers</text>
<line x1="380" y1="672" x2="380" y2="700" stroke="#5f6368" stroke-width="1.5" marker-end="url(#arrow-gray)"/>
<rect x="295" y="705" width="170" height="42" rx="8" fill="#fef7e0" stroke="#f9ab00" stroke-width="1.2"/>
<text x="380" y="723" text-anchor="middle" font-size="11" font-weight="600" fill="#e37400">w.tick_once()</text>
<text x="380" y="738" text-anchor="middle" font-size="10" fill="#5f6368">for each worker</text>
<!-- Loop arrow for tick_workers -->
<path d="M 465 726 Q 495 726 495 665 Q 495 651 465 651"
fill="none" stroke="#34a853" stroke-width="1.2" marker-end="url(#arrow-green)"/>
<text x="512" y="692" font-size="9" fill="#34a853">next</text>
<text x="165" y="756" font-size="10" fill="#5f6368">RefCell&lt;Vec&lt;Worker&gt;&gt; — stays on caller's thread</text>
<!-- Mode B: run() -->
<rect x="620" y="580" width="530" height="200" rx="10" fill="#fff" stroke="#34a853" stroke-width="1.5" filter="url(#shadow)"/>
<rect x="620" y="580" width="530" height="32" rx="10" fill="#34a853"/>
<rect x="620" y="600" width="530" height="12" fill="#34a853"/>
<text x="885" y="601" text-anchor="middle" font-size="14" font-weight="bold" fill="#fff">run() mode — multi-threaded</text>
<!-- run() flow -->
<rect x="645" y="630" width="155" height="42" rx="8" fill="#e6f4ea" stroke="#34a853" stroke-width="1.2"/>
<text x="722" y="648" text-anchor="middle" font-size="11" font-weight="600" fill="#137333">workers → threads</text>
<text x="722" y="663" text-anchor="middle" font-size="10" fill="#5f6368">std::thread::spawn</text>
<line x1="800" y1="651" x2="835" y2="651" stroke="#5f6368" stroke-width="1.5" marker-end="url(#arrow-gray)"/>
<rect x="840" y="630" width="155" height="42" rx="8" fill="#e8f0fe" stroke="#4285f4" stroke-width="1.2"/>
<text x="917" y="648" text-anchor="middle" font-size="11" font-weight="600" fill="#1a73e8">while is_running</text>
<text x="917" y="663" text-anchor="middle" font-size="10" fill="#5f6368">{ tick_once(); backoff() }</text>
<line x1="1000" y1="651" x2="1035" y2="651" stroke="#5f6368" stroke-width="1.5" marker-end="url(#arrow-gray)"/>
<rect x="1040" y="636" width="90" height="30" rx="8" fill="#e6f4ea" stroke="#34a853" stroke-width="1.2"/>
<text x="1085" y="656" text-anchor="middle" font-size="11" font-weight="600" fill="#137333">Handle</text>
<!-- Loop arrow -->
<path d="M 995 651 Q 1010 690 917 690 Q 850 690 840 672"
fill="none" stroke="#34a853" stroke-width="1.2" marker-end="url(#arrow-green)"/>
<text x="930" y="705" text-anchor="middle" font-size="9" fill="#34a853">loop</text>
<!-- Returns RuntimeHandle -->
<text x="722" y="700" font-size="10" fill="#5f6368">returns RuntimeHandle to caller</text>
<!-- Shutdown flow -->
<rect x="645" y="720" width="480" height="42" rx="8" fill="#fce8e6" stroke="#ea4335" stroke-width="1.2"/>
<text x="885" y="738" text-anchor="middle" font-size="12" font-weight="600" fill="#c5221f">Shutdown</text>
<text x="885" y="752" text-anchor="middle" font-size="10" fill="#ea4335">is_running.store(false) → worker loops exit → thread.join()</text>
<!-- Arrow from run loop to shutdown -->
<path d="M 917 672 Q 917 706 885 718"
fill="none" stroke="#ea4335" stroke-width="1.2" marker-end="url(#arrow-red)"/>
<!-- ============================================ -->
<!-- Bottom source refs and legend -->
<!-- ============================================ -->
<text x="600" y="885" text-anchor="middle" font-size="10" fill="#9aa0a6">runtime.rs:80-122 (new) · worker.rs:40-96 (tick_once) · worker.rs:98-120 (run loop) · runtime.rs:185-217 (run) · config.rs (BackoffPolicy)</text>
</svg>

After

Width:  |  Height:  |  Size: 15 KiB

View file

@ -0,0 +1,258 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Generated by graphviz version 14.1.1 (20251213.1925)
-->
<!-- Title: tick_cycle Pages: 1 -->
<svg width="1100pt" height="1053pt"
viewBox="0.00 0.00 1100.00 1053.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 1049.2)">
<title>tick_cycle</title>
<polygon fill="white" stroke="none" points="-4,4 -4,-1049.2 1096.05,-1049.2 1096.05,4 -4,4"/>
<g id="clust1" class="cluster">
<title>cluster_backoff</title>
<path fill="#f5f5f5" stroke="#888888" d="M20,-874.4C20,-874.4 385,-874.4 385,-874.4 391,-874.4 397,-880.4 397,-886.4 397,-886.4 397,-1025.2 397,-1025.2 397,-1031.2 391,-1037.2 385,-1037.2 385,-1037.2 20,-1037.2 20,-1037.2 14,-1037.2 8,-1031.2 8,-1025.2 8,-1025.2 8,-886.4 8,-886.4 8,-880.4 14,-874.4 20,-874.4"/>
<text xml:space="preserve" text-anchor="middle" x="62.96" y="-1024.2" font-family="Helvetica,sans-Serif" font-size="10.00">Worker::run() backoff</text>
</g>
<!-- start -->
<g id="node1" class="node">
<title>start</title>
<ellipse fill="#333333" stroke="black" cx="519" cy="-900.4" rx="10.8" ry="10.8"/>
</g>
<!-- phase1 -->
<g id="node3" class="node">
<title>phase1</title>
<path fill="#d6eaf8" stroke="#2980b9" d="M633.78,-837.4C633.78,-837.4 492.22,-837.4 492.22,-837.4 486.22,-837.4 480.22,-831.4 480.22,-825.4 480.22,-825.4 480.22,-802.6 480.22,-802.6 480.22,-796.6 486.22,-790.6 492.22,-790.6 492.22,-790.6 633.78,-790.6 633.78,-790.6 639.78,-790.6 645.78,-796.6 645.78,-802.6 645.78,-802.6 645.78,-825.4 645.78,-825.4 645.78,-831.4 639.78,-837.4 633.78,-837.4"/>
<text xml:space="preserve" text-anchor="start" x="493.81" y="-821.4" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00">Phase 1: Drain Spawn Queue</text>
<text xml:space="preserve" text-anchor="start" x="492.22" y="-801.3" font-family="Helvetica,sans-Serif" font-size="9.00">spawn_rx → pool.insert(addr, actor)</text>
</g>
<!-- start&#45;&gt;phase1 -->
<g id="edge1" class="edge">
<title>start&#45;&gt;phase1</title>
<path fill="none" stroke="black" d="M519,-889.1C519,-889.1 519,-849.2 519,-849.2"/>
<polygon fill="black" stroke="black" points="522.5,-849.2 519,-839.2 515.5,-849.2 522.5,-849.2"/>
</g>
<!-- end -->
<g id="node2" class="node">
<title>end</title>
<ellipse fill="#333333" stroke="black" cx="447" cy="-14.8" rx="10.8" ry="10.8"/>
<ellipse fill="none" stroke="black" cx="447" cy="-14.8" rx="14.8" ry="14.8"/>
</g>
<!-- idle_check -->
<g id="node13" class="node">
<title>idle_check</title>
<polygon fill="#fef9e7" stroke="black" points="302,-1009.2 249.63,-991.2 302,-973.2 354.37,-991.2 302,-1009.2"/>
<text xml:space="preserve" text-anchor="middle" x="302" y="-988.2" font-family="Helvetica,sans-Serif" font-size="10.00">did_work?</text>
</g>
<!-- end&#45;&gt;idle_check -->
<g id="edge17" class="edge">
<title>end&#45;&gt;idle_check</title>
<path fill="none" stroke="#888888" stroke-dasharray="5,2" d="M442.07,-29.27C442.07,-149.96 442.07,-985 442.07,-985 442.07,-985 349.22,-985 349.22,-985"/>
<polygon fill="#888888" stroke="#888888" points="349.22,-981.5 339.22,-985 349.22,-988.5 349.22,-981.5"/>
<text xml:space="preserve" text-anchor="middle" x="436.26" y="-527.7" font-family="Helvetica,sans-Serif" font-size="9.00">run() loop</text>
</g>
<!-- phase2 -->
<g id="node4" class="node">
<title>phase2</title>
<path fill="#d5f5e3" stroke="#27ae60" d="M652.54,-735.8C652.54,-735.8 491.46,-735.8 491.46,-735.8 485.46,-735.8 479.46,-729.8 479.46,-723.8 479.46,-723.8 479.46,-701 479.46,-701 479.46,-695 485.46,-689 491.46,-689 491.46,-689 652.54,-689 652.54,-689 658.54,-689 664.54,-695 664.54,-701 664.54,-701 664.54,-723.8 664.54,-723.8 664.54,-729.8 658.54,-735.8 652.54,-735.8"/>
<text xml:space="preserve" text-anchor="start" x="498.92" y="-719.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00">Phase 2: Drain Transfer Queue</text>
<text xml:space="preserve" text-anchor="start" x="491.46" y="-699.7" font-family="Helvetica,sans-Serif" font-size="9.00">transfer_rx → pool.deliver(dest, payload)</text>
</g>
<!-- phase1&#45;&gt;phase2 -->
<g id="edge2" class="edge">
<title>phase1&#45;&gt;phase2</title>
<path fill="none" stroke="black" d="M563,-790.18C563,-790.18 563,-747.58 563,-747.58"/>
<polygon fill="black" stroke="black" points="566.5,-747.58 563,-737.58 559.5,-747.59 566.5,-747.58"/>
<text xml:space="preserve" text-anchor="middle" x="639.28" y="-760.5" font-family="Helvetica,sans-Serif" font-size="9.00">(ActorAddress, Box&lt;dyn AnyActor&gt;)</text>
</g>
<!-- phase3 -->
<g id="node5" class="node">
<title>phase3</title>
<path fill="#fdebd0" stroke="#e67e22" d="M691.55,-634.2C691.55,-634.2 484.45,-634.2 484.45,-634.2 478.45,-634.2 472.45,-628.2 472.45,-622.2 472.45,-622.2 472.45,-580.6 472.45,-580.6 472.45,-574.6 478.45,-568.6 484.45,-568.6 484.45,-568.6 691.55,-568.6 691.55,-568.6 697.55,-568.6 703.55,-574.6 703.55,-580.6 703.55,-580.6 703.55,-622.2 703.55,-622.2 703.55,-628.2 697.55,-634.2 691.55,-634.2"/>
<text xml:space="preserve" text-anchor="start" x="531.03" y="-618.2" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00">Phase 3: Tick All Actors</text>
<text xml:space="preserve" text-anchor="start" x="484.45" y="-598.1" font-family="Helvetica,sans-Serif" font-size="9.00">for each actor: pop mailbox → handle_any(ctx, msg)</text>
<text xml:space="preserve" text-anchor="start" x="513.45" y="-579.3" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#c0392b">catch_unwind wraps each handle call</text>
</g>
<!-- phase2&#45;&gt;phase3 -->
<g id="edge3" class="edge">
<title>phase2&#45;&gt;phase3</title>
<path fill="none" stroke="black" d="M572,-688.59C572,-688.59 572,-645.95 572,-645.95"/>
<polygon fill="black" stroke="black" points="575.5,-645.95 572,-635.95 568.5,-645.95 575.5,-645.95"/>
<text xml:space="preserve" text-anchor="middle" x="645.92" y="-658.9" font-family="Helvetica,sans-Serif" font-size="9.00">Envelope → Box&lt;dyn Any + Send&gt;</text>
</g>
<!-- phase4 -->
<g id="node6" class="node">
<title>phase4</title>
<path fill="#d6eaf8" stroke="#2980b9" d="M665.97,-478C665.97,-478 492.03,-478 492.03,-478 486.03,-478 480.03,-472 480.03,-466 480.03,-466 480.03,-443.2 480.03,-443.2 480.03,-437.2 486.03,-431.2 492.03,-431.2 492.03,-431.2 665.97,-431.2 665.97,-431.2 671.97,-431.2 677.97,-437.2 677.97,-443.2 677.97,-443.2 677.97,-466 677.97,-466 677.97,-472 671.97,-478 665.97,-478"/>
<text xml:space="preserve" text-anchor="start" x="492.03" y="-462" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00">Phase 4: Drain Spawn Queue (again)</text>
<text xml:space="preserve" text-anchor="start" x="497.45" y="-441.9" font-family="Helvetica,sans-Serif" font-size="9.00">actors spawned during Phase 3 handlers</text>
</g>
<!-- phase3&#45;&gt;phase4 -->
<g id="edge4" class="edge">
<title>phase3&#45;&gt;phase4</title>
<path fill="none" stroke="black" d="M579,-568.46C579,-568.46 579,-489.81 579,-489.81"/>
<polygon fill="black" stroke="black" points="582.5,-489.81 579,-479.81 575.5,-489.81 582.5,-489.81"/>
</g>
<!-- route_decision -->
<g id="node9" class="node">
<title>route_decision</title>
<polygon fill="#fef9e7" stroke="#d4ac0d" points="850,-492.2 739.28,-454.6 850,-417 960.72,-454.6 850,-492.2"/>
<text xml:space="preserve" text-anchor="start" x="808.33" y="-459.4" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00">send_any routing</text>
<text xml:space="preserve" text-anchor="start" x="804.64" y="-444.2" font-family="Helvetica,sans-Serif" font-size="8.00">(inside Phase 3 handlers)</text>
</g>
<!-- phase3&#45;&gt;route_decision -->
<g id="edge8" class="edge">
<title>phase3&#45;&gt;route_decision</title>
<path fill="none" stroke="#888888" stroke-dasharray="5,2" d="M690.76,-568.36C690.76,-533.94 690.76,-485 690.76,-485 690.76,-485 816.01,-485 816.01,-485"/>
<polygon fill="#888888" stroke="#888888" points="816.01,-488.5 826.01,-485 816.01,-481.5 816.01,-488.5"/>
<text xml:space="preserve" text-anchor="middle" x="681.76" y="-527.7" font-family="Helvetica,sans-Serif" font-size="9.00">ctx.send()</text>
</g>
<!-- phase5 -->
<g id="node7" class="node">
<title>phase5</title>
<path fill="#e8daef" stroke="#8e44ad" d="M646.04,-260.6C646.04,-260.6 485.96,-260.6 485.96,-260.6 479.96,-260.6 473.96,-254.6 473.96,-248.6 473.96,-248.6 473.96,-207 473.96,-207 473.96,-201 479.96,-195 485.96,-195 485.96,-195 646.04,-195 646.04,-195 652.04,-195 658.04,-201 658.04,-207 658.04,-207 658.04,-248.6 658.04,-248.6 658.04,-254.6 652.04,-260.6 646.04,-260.6"/>
<text xml:space="preserve" text-anchor="start" x="495.7" y="-244.6" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00">Phase 5: Drain Pending Local</text>
<text xml:space="preserve" text-anchor="start" x="486.46" y="-224.5" font-family="Helvetica,sans-Serif" font-size="9.00">same&#45;worker sends buffered in Phase 3</text>
<text xml:space="preserve" text-anchor="start" x="485.96" y="-205.7" font-family="Helvetica,sans-Serif" font-size="9.00">pending_local → pool.deliver(addr, msg)</text>
</g>
<!-- phase4&#45;&gt;phase5 -->
<g id="edge5" class="edge">
<title>phase4&#45;&gt;phase5</title>
<path fill="none" stroke="black" d="M569.04,-430.76C569.04,-430.76 569.04,-272.3 569.04,-272.3"/>
<polygon fill="black" stroke="black" points="572.54,-272.3 569.04,-262.3 565.54,-272.3 572.54,-272.3"/>
<text xml:space="preserve" text-anchor="middle" x="605.03" y="-341.5" font-family="Helvetica,sans-Serif" font-size="9.00">new actors ready for local delivery</text>
</g>
<!-- phase6 -->
<g id="node8" class="node">
<title>phase6</title>
<path fill="#d1c4e9" stroke="#283593" d="M648.29,-150C648.29,-150 483.71,-150 483.71,-150 477.71,-150 471.71,-144 471.71,-138 471.71,-138 471.71,-96.4 471.71,-96.4 471.71,-90.4 477.71,-84.4 483.71,-84.4 483.71,-84.4 648.29,-84.4 648.29,-84.4 654.29,-84.4 660.29,-90.4 660.29,-96.4 660.29,-96.4 660.29,-138 660.29,-138 660.29,-144 654.29,-150 648.29,-150"/>
<text xml:space="preserve" text-anchor="start" x="512.37" y="-134" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00">Phase 6: Publish Stats</text>
<text xml:space="preserve" text-anchor="start" x="483.71" y="-113.9" font-family="Helvetica,sans-Serif" font-size="9.00">AtomicStore: num_actors, mailbox_depth</text>
<text xml:space="preserve" text-anchor="start" x="486.71" y="-95.1" font-family="Helvetica,sans-Serif" font-size="9.00">AtomicFetchAdd: messages_processed</text>
</g>
<!-- phase5&#45;&gt;phase6 -->
<g id="edge6" class="edge">
<title>phase5&#45;&gt;phase6</title>
<path fill="none" stroke="black" d="M566,-194.62C566,-194.62 566,-161.87 566,-161.87"/>
<polygon fill="black" stroke="black" points="569.5,-161.87 566,-151.87 562.5,-161.87 569.5,-161.87"/>
</g>
<!-- phase6&#45;&gt;end -->
<g id="edge7" class="edge">
<title>phase6&#45;&gt;end</title>
<path fill="none" stroke="black" d="M471.28,-117C459.75,-117 451.93,-117 451.93,-117 451.93,-117 451.93,-40.52 451.93,-40.52"/>
<polygon fill="black" stroke="black" points="455.43,-40.52 451.93,-30.52 448.43,-40.52 455.43,-40.52"/>
<text xml:space="preserve" text-anchor="middle" x="565.27" y="-54.3" font-family="Helvetica,sans-Serif" font-size="9.00">returns did_work: bool</text>
</g>
<!-- local_buf -->
<g id="node10" class="node">
<title>local_buf</title>
<polygon fill="#e8daef" stroke="black" points="809.03,-362.2 708.97,-362.2 708.97,-326.2 815.03,-326.2 815.03,-356.2 809.03,-362.2"/>
<polyline fill="none" stroke="black" points="809.03,-362.2 809.03,-356.2"/>
<polyline fill="none" stroke="black" points="815.03,-356.2 809.03,-356.2"/>
<text xml:space="preserve" text-anchor="middle" x="762" y="-347.2" font-family="Helvetica,sans-Serif" font-size="10.00">pending_local buffer</text>
<text xml:space="preserve" text-anchor="middle" x="762" y="-335.2" font-family="Helvetica,sans-Serif" font-size="10.00">(same worker)</text>
</g>
<!-- route_decision&#45;&gt;local_buf -->
<g id="edge9" class="edge">
<title>route_decision&#45;&gt;local_buf</title>
<path fill="none" stroke="#8e44ad" d="M777.16,-441.5C777.16,-441.5 777.16,-373.92 777.16,-373.92"/>
<polygon fill="#8e44ad" stroke="#8e44ad" points="780.66,-373.92 777.16,-363.92 773.66,-373.92 780.66,-373.92"/>
<text xml:space="preserve" text-anchor="middle" x="796.76" y="-386.9" font-family="Helvetica,sans-Serif" font-size="9.00">same worker</text>
</g>
<!-- cross_q -->
<g id="node11" class="node">
<title>cross_q</title>
<polygon fill="#fadbd8" stroke="black" points="928.9,-362.2 851.1,-362.2 851.1,-326.2 934.9,-326.2 934.9,-356.2 928.9,-362.2"/>
<polyline fill="none" stroke="black" points="928.9,-362.2 928.9,-356.2"/>
<polyline fill="none" stroke="black" points="934.9,-356.2 928.9,-356.2"/>
<text xml:space="preserve" text-anchor="middle" x="893" y="-347.2" font-family="Helvetica,sans-Serif" font-size="10.00">transfer_tx[wid]</text>
<text xml:space="preserve" text-anchor="middle" x="893" y="-335.2" font-family="Helvetica,sans-Serif" font-size="10.00">(cross worker)</text>
</g>
<!-- route_decision&#45;&gt;cross_q -->
<g id="edge10" class="edge">
<title>route_decision&#45;&gt;cross_q</title>
<path fill="none" stroke="#c0392b" d="M893,-431.19C893,-431.19 893,-373.99 893,-373.99"/>
<polygon fill="#c0392b" stroke="#c0392b" points="896.5,-373.99 893,-363.99 889.5,-373.99 896.5,-373.99"/>
<text xml:space="preserve" text-anchor="middle" x="905.01" y="-386.9" font-family="Helvetica,sans-Serif" font-size="9.00">different worker</text>
</g>
<!-- inbox_reg -->
<g id="node12" class="node">
<title>inbox_reg</title>
<polygon fill="#d1f2eb" stroke="black" points="1049.46,-362.2 970.54,-362.2 970.54,-326.2 1055.46,-326.2 1055.46,-356.2 1049.46,-362.2"/>
<polyline fill="none" stroke="black" points="1049.46,-362.2 1049.46,-356.2"/>
<polyline fill="none" stroke="black" points="1055.46,-356.2 1049.46,-356.2"/>
<text xml:space="preserve" text-anchor="middle" x="1013" y="-347.2" font-family="Helvetica,sans-Serif" font-size="10.00">inbox_registry</text>
<text xml:space="preserve" text-anchor="middle" x="1013" y="-335.2" font-family="Helvetica,sans-Serif" font-size="10.00">(external inbox)</text>
</g>
<!-- route_decision&#45;&gt;inbox_reg -->
<g id="edge11" class="edge">
<title>route_decision&#45;&gt;inbox_reg</title>
<path fill="none" stroke="#148f77" d="M947.81,-449.88C947.81,-428.07 947.81,-338 947.81,-338 947.81,-338 958.73,-338 958.73,-338"/>
<polygon fill="#148f77" stroke="#148f77" points="958.73,-341.5 968.73,-338 958.73,-334.5 958.73,-341.5"/>
<text xml:space="preserve" text-anchor="middle" x="1052.52" y="-386.9" font-family="Helvetica,sans-Serif" font-size="9.00">not in address_map</text>
</g>
<!-- local_buf&#45;&gt;phase5 -->
<g id="edge12" class="edge">
<title>local_buf&#45;&gt;phase5</title>
<path fill="none" stroke="#8e44ad" stroke-dasharray="5,2" d="M762,-325.79C762,-293.02 762,-228 762,-228 762,-228 669.77,-228 669.77,-228"/>
<polygon fill="#8e44ad" stroke="#8e44ad" points="669.77,-224.5 659.77,-228 669.77,-231.5 669.77,-224.5"/>
<text xml:space="preserve" text-anchor="middle" x="701.02" y="-296.1" font-family="Helvetica,sans-Serif" font-size="9.00">delivered</text>
<text xml:space="preserve" text-anchor="middle" x="701.02" y="-285.3" font-family="Helvetica,sans-Serif" font-size="9.00">next phase</text>
</g>
<!-- cross_q&#45;&gt;phase2 -->
<g id="edge13" class="edge">
<title>cross_q&#45;&gt;phase2</title>
<path fill="none" stroke="#c0392b" stroke-dasharray="5,2" d="M935.25,-350C951.23,-350 965.63,-350 965.63,-350 965.63,-350 965.63,-712 965.63,-712 965.63,-712 676.3,-712 676.3,-712"/>
<polygon fill="#c0392b" stroke="#c0392b" points="676.3,-708.5 666.3,-712 676.3,-715.5 676.3,-708.5"/>
<text xml:space="preserve" text-anchor="middle" x="781.01" y="-538.5" font-family="Helvetica,sans-Serif" font-size="9.00">delivered</text>
<text xml:space="preserve" text-anchor="middle" x="781.01" y="-527.7" font-family="Helvetica,sans-Serif" font-size="9.00">next tick</text>
<text xml:space="preserve" text-anchor="middle" x="781.01" y="-516.9" font-family="Helvetica,sans-Serif" font-size="9.00">(other worker)</text>
</g>
<!-- idle_check&#45;&gt;start -->
<g id="edge18" class="edge">
<title>idle_check&#45;&gt;start</title>
<path fill="none" stroke="#888888" stroke-dasharray="5,2" d="M338.81,-997C400.72,-997 519,-997 519,-997 519,-997 519,-922.99 519,-922.99"/>
<polygon fill="#888888" stroke="#888888" points="522.5,-922.99 519,-912.99 515.5,-922.99 522.5,-922.99"/>
<text xml:space="preserve" text-anchor="middle" x="534.4" y="-943.1" font-family="Helvetica,sans-Serif" font-size="9.00">yes / idle_count=0</text>
</g>
<!-- spinning -->
<g id="node14" class="node">
<title>spinning</title>
<path fill="#d5f5e3" stroke="black" d="M85.52,-918.4C85.52,-918.4 28.48,-918.4 28.48,-918.4 22.48,-918.4 16.48,-912.4 16.48,-906.4 16.48,-906.4 16.48,-894.4 16.48,-894.4 16.48,-888.4 22.48,-882.4 28.48,-882.4 28.48,-882.4 85.52,-882.4 85.52,-882.4 91.52,-882.4 97.52,-888.4 97.52,-894.4 97.52,-894.4 97.52,-906.4 97.52,-906.4 97.52,-912.4 91.52,-918.4 85.52,-918.4"/>
<text xml:space="preserve" text-anchor="middle" x="57" y="-897.4" font-family="Helvetica,sans-Serif" font-size="10.00">Spin (hot loop)</text>
</g>
<!-- idle_check&#45;&gt;spinning -->
<g id="edge14" class="edge">
<title>idle_check&#45;&gt;spinning</title>
<path fill="none" stroke="black" d="M248.84,-991C176.78,-991 57,-991 57,-991 57,-991 57,-930.25 57,-930.25"/>
<polygon fill="black" stroke="black" points="60.5,-930.25 57,-920.25 53.5,-930.25 60.5,-930.25"/>
<text xml:space="preserve" text-anchor="middle" x="93.4" y="-943.1" font-family="Helvetica,sans-Serif" font-size="9.00">idle &lt; spin_threshold</text>
</g>
<!-- yielding -->
<g id="node15" class="node">
<title>yielding</title>
<path fill="#fdebd0" stroke="black" d="M222.52,-918.4C222.52,-918.4 145.48,-918.4 145.48,-918.4 139.48,-918.4 133.48,-912.4 133.48,-906.4 133.48,-906.4 133.48,-894.4 133.48,-894.4 133.48,-888.4 139.48,-882.4 145.48,-882.4 145.48,-882.4 222.52,-882.4 222.52,-882.4 228.52,-882.4 234.52,-888.4 234.52,-894.4 234.52,-894.4 234.52,-906.4 234.52,-906.4 234.52,-912.4 228.52,-918.4 222.52,-918.4"/>
<text xml:space="preserve" text-anchor="middle" x="184" y="-897.4" font-family="Helvetica,sans-Serif" font-size="10.00">thread::yield_now()</text>
</g>
<!-- idle_check&#45;&gt;yielding -->
<g id="edge15" class="edge">
<title>idle_check&#45;&gt;yielding</title>
<path fill="none" stroke="black" d="M260.06,-987.28C260.06,-969.82 260.06,-900 260.06,-900 260.06,-900 246.24,-900 246.24,-900"/>
<polygon fill="black" stroke="black" points="246.24,-896.5 236.24,-900 246.24,-903.5 246.24,-896.5"/>
<text xml:space="preserve" text-anchor="middle" x="218.4" y="-943.1" font-family="Helvetica,sans-Serif" font-size="9.00">idle &lt; yield_threshold</text>
</g>
<!-- sleeping -->
<g id="node16" class="node">
<title>sleeping</title>
<path fill="#fadbd8" stroke="black" d="M349.52,-918.4C349.52,-918.4 282.48,-918.4 282.48,-918.4 276.48,-918.4 270.48,-912.4 270.48,-906.4 270.48,-906.4 270.48,-894.4 270.48,-894.4 270.48,-888.4 276.48,-882.4 282.48,-882.4 282.48,-882.4 349.52,-882.4 349.52,-882.4 355.52,-882.4 361.52,-888.4 361.52,-894.4 361.52,-894.4 361.52,-906.4 361.52,-906.4 361.52,-912.4 355.52,-918.4 349.52,-918.4"/>
<text xml:space="preserve" text-anchor="middle" x="316" y="-897.4" font-family="Helvetica,sans-Serif" font-size="10.00">thread::sleep(μs)</text>
</g>
<!-- idle_check&#45;&gt;sleeping -->
<g id="edge16" class="edge">
<title>idle_check&#45;&gt;sleeping</title>
<path fill="none" stroke="black" d="M312.43,-976.61C312.43,-976.61 312.43,-930.23 312.43,-930.23"/>
<polygon fill="black" stroke="black" points="315.93,-930.23 312.43,-920.23 308.93,-930.23 315.93,-930.23"/>
<text xml:space="preserve" text-anchor="middle" x="345.53" y="-943.1" font-family="Helvetica,sans-Serif" font-size="9.00">idle ≥ yield_threshold</text>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 20 KiB

View file

@ -0,0 +1,222 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Generated by graphviz version 14.1.1 (20251213.1925)
-->
<!-- Title: type_erasure Pages: 1 -->
<svg width="516pt" height="1234pt"
viewBox="0.00 0.00 516.00 1234.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 1230)">
<title>type_erasure</title>
<polygon fill="white" stroke="none" points="-4,4 -4,-1230 512,-1230 512,4 -4,4"/>
<g id="clust1" class="cluster">
<title>cluster_typed</title>
<path fill="#eafaf1" stroke="#27ae60" d="M123,-988.8C123,-988.8 331,-988.8 331,-988.8 337,-988.8 343,-994.8 343,-1000.8 343,-1000.8 343,-1206 343,-1206 343,-1212 337,-1218 331,-1218 331,-1218 123,-1218 123,-1218 117,-1218 111,-1212 111,-1206 111,-1206 111,-1000.8 111,-1000.8 111,-994.8 117,-988.8 123,-988.8"/>
<text xml:space="preserve" text-anchor="middle" x="154.14" y="-1204.1" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#27ae60">TYPED ZONE</text>
</g>
<g id="clust2" class="cluster">
<title>cluster_erased</title>
<path fill="#fdedec" stroke="#c0392b" d="M88,-341.2C88,-341.2 488,-341.2 488,-341.2 494,-341.2 500,-347.2 500,-353.2 500,-353.2 500,-941 500,-941 500,-947 494,-953 488,-953 488,-953 88,-953 88,-953 82,-953 76,-947 76,-941 76,-941 76,-353.2 76,-353.2 76,-347.2 82,-341.2 88,-341.2"/>
<text xml:space="preserve" text-anchor="middle" x="183.5" y="-939.1" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#c0392b">ERASED ZONE (Box&lt;dyn Any + Send&gt;)</text>
</g>
<g id="clust3" class="cluster">
<title>cluster_recovery</title>
<path fill="#eafaf1" stroke="#27ae60" d="M20,-8C20,-8 359,-8 359,-8 365,-8 371,-14 371,-20 371,-20 371,-293.4 371,-293.4 371,-299.4 365,-305.4 359,-305.4 359,-305.4 20,-305.4 20,-305.4 14,-305.4 8,-299.4 8,-293.4 8,-293.4 8,-20 8,-20 8,-14 14,-8 20,-8"/>
<text xml:space="preserve" text-anchor="middle" x="63.67" y="-291.5" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#27ae60">RECOVERY ZONE</text>
</g>
<!-- user_send -->
<g id="node1" class="node">
<title>user_send</title>
<path fill="#d5f5e3" stroke="#27ae60" d="M292.27,-1188.8C292.27,-1188.8 161.73,-1188.8 161.73,-1188.8 155.73,-1188.8 149.73,-1182.8 149.73,-1176.8 149.73,-1176.8 149.73,-1135.2 149.73,-1135.2 149.73,-1129.2 155.73,-1123.2 161.73,-1123.2 161.73,-1123.2 292.27,-1123.2 292.27,-1123.2 298.27,-1123.2 304.27,-1129.2 304.27,-1135.2 304.27,-1135.2 304.27,-1176.8 304.27,-1176.8 304.27,-1182.8 298.27,-1188.8 292.27,-1188.8"/>
<text xml:space="preserve" text-anchor="start" x="166.16" y="-1172.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00">ctx.send::&lt;M&gt;(addr, msg)</text>
<text xml:space="preserve" text-anchor="start" x="183.74" y="-1152.7" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#666666">actor.rs — Ctx::send()</text>
<text xml:space="preserve" text-anchor="start" x="161.73" y="-1133.9" font-family="Helvetica,sans-Serif" font-size="9.00">M: &#39;static + Clone + Send + Sync</text>
</g>
<!-- box_erase -->
<g id="node2" class="node">
<title>box_erase</title>
<path fill="#d5f5e3" stroke="#27ae60" d="M322.99,-1062.4C322.99,-1062.4 131.01,-1062.4 131.01,-1062.4 125.01,-1062.4 119.01,-1056.4 119.01,-1050.4 119.01,-1050.4 119.01,-1008.8 119.01,-1008.8 119.01,-1002.8 125.01,-996.8 131.01,-996.8 131.01,-996.8 322.99,-996.8 322.99,-996.8 328.99,-996.8 334.99,-1002.8 334.99,-1008.8 334.99,-1008.8 334.99,-1050.4 334.99,-1050.4 334.99,-1056.4 328.99,-1062.4 322.99,-1062.4"/>
<text xml:space="preserve" text-anchor="start" x="131.01" y="-1046.4" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00">Box::new(msg) → Box&lt;dyn Any + Send&gt;</text>
<text xml:space="preserve" text-anchor="start" x="169.23" y="-1026.3" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#666666">actor.rs — Ctx::send() line 94</text>
<text xml:space="preserve" text-anchor="start" x="168.22" y="-1007.5" font-family="Helvetica,sans-Serif" font-size="9.00">Type information erased here</text>
</g>
<!-- user_send&#45;&gt;box_erase -->
<g id="edge1" class="edge">
<title>user_send&#45;&gt;box_erase</title>
<path fill="none" stroke="black" d="M227,-1122.96C227,-1108.02 227,-1090.01 227,-1073.87"/>
<polygon fill="black" stroke="black" points="230.5,-1074.04 227,-1064.04 223.5,-1074.04 230.5,-1074.04"/>
<text xml:space="preserve" text-anchor="middle" x="230.75" y="-1090.1" font-family="Helvetica,sans-Serif" font-size="9.00">M</text>
</g>
<!-- send_any -->
<g id="node3" class="node">
<title>send_any</title>
<path fill="#fadbd8" stroke="#c0392b" d="M311.46,-923.8C311.46,-923.8 142.54,-923.8 142.54,-923.8 136.54,-923.8 130.54,-917.8 130.54,-911.8 130.54,-911.8 130.54,-889 130.54,-889 130.54,-883 136.54,-877 142.54,-877 142.54,-877 311.46,-877 311.46,-877 317.46,-877 323.46,-883 323.46,-889 323.46,-889 323.46,-911.8 323.46,-911.8 323.46,-917.8 317.46,-923.8 311.46,-923.8"/>
<text xml:space="preserve" text-anchor="start" x="142.54" y="-907.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00">ContextInner::send_any(addr, msg)</text>
<text xml:space="preserve" text-anchor="start" x="147.23" y="-887.7" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#666666">worker.rs — WorkerContext::send_any()</text>
</g>
<!-- box_erase&#45;&gt;send_any -->
<g id="edge12" class="edge">
<title>box_erase&#45;&gt;send_any</title>
<path fill="none" stroke="black" d="M227,-996.5C227,-986.72 227,-975.6 227,-964.5"/>
<polygon fill="black" stroke="black" points="230.5,-964.51 227,-954.51 223.5,-964.51 230.5,-964.51"/>
<text xml:space="preserve" text-anchor="middle" x="271.91" y="-963.7" font-family="Helvetica,sans-Serif" font-size="9.00">Box&lt;dyn Any + Send&gt;</text>
</g>
<!-- route -->
<g id="node4" class="node">
<title>route</title>
<polygon fill="#fef9e7" stroke="#d4ac0d" points="227,-826 111.39,-788.4 227,-750.8 342.61,-788.4 227,-826"/>
<text xml:space="preserve" text-anchor="start" x="195.06" y="-793.2" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00">Route lookup</text>
<text xml:space="preserve" text-anchor="start" x="179.2" y="-778" font-family="Helvetica,sans-Serif" font-size="8.00">address_map.lookup(addr)</text>
</g>
<!-- send_any&#45;&gt;route -->
<g id="edge2" class="edge">
<title>send_any&#45;&gt;route</title>
<path fill="none" stroke="black" d="M227,-876.61C227,-865.3 227,-851.19 227,-837.63"/>
<polygon fill="black" stroke="black" points="230.5,-837.97 227,-827.97 223.5,-837.97 230.5,-837.97"/>
</g>
<!-- pending -->
<g id="node5" class="node">
<title>pending</title>
<polygon fill="#e8daef" stroke="black" points="221.65,-569.8 84.35,-569.8 84.35,-533.8 227.65,-533.8 227.65,-563.8 221.65,-569.8"/>
<polyline fill="none" stroke="black" points="221.65,-569.8 221.65,-563.8"/>
<polyline fill="none" stroke="black" points="227.65,-563.8 221.65,-563.8"/>
<text xml:space="preserve" text-anchor="middle" x="156" y="-554.8" font-family="Helvetica,sans-Serif" font-size="10.00">pending_local</text>
<text xml:space="preserve" text-anchor="middle" x="156" y="-542.8" font-family="Helvetica,sans-Serif" font-size="10.00">Vec&lt;(Addr, Box&lt;dyn Any&gt;)&gt;</text>
</g>
<!-- route&#45;&gt;pending -->
<g id="edge3" class="edge">
<title>route&#45;&gt;pending</title>
<path fill="none" stroke="black" d="M196.71,-760.56C177.98,-743.96 157.49,-725.8 157.49,-725.8 157.49,-725.8 107,-679.2 107,-679.2 107,-679.2 107,-594.8 107,-594.8 107,-594.8 116.52,-586.64 127.17,-577.51"/>
<polygon fill="black" stroke="black" points="129.32,-580.28 134.64,-571.11 124.77,-574.96 129.32,-580.28"/>
<text xml:space="preserve" text-anchor="middle" x="183.24" y="-717.7" font-family="Helvetica,sans-Serif" font-size="9.00">same worker</text>
<text xml:space="preserve" text-anchor="middle" x="183.24" y="-706.9" font-family="Helvetica,sans-Serif" font-size="9.00">(wid == self)</text>
</g>
<!-- envelope -->
<g id="node6" class="node">
<title>envelope</title>
<path fill="#fadbd8" stroke="#c0392b" d="M283.12,-679.2C283.12,-679.2 140.88,-679.2 140.88,-679.2 134.88,-679.2 128.88,-673.2 128.88,-667.2 128.88,-667.2 128.88,-606.8 128.88,-606.8 128.88,-600.8 134.88,-594.8 140.88,-594.8 140.88,-594.8 283.12,-594.8 283.12,-594.8 289.12,-594.8 295.12,-600.8 295.12,-606.8 295.12,-606.8 295.12,-667.2 295.12,-667.2 295.12,-673.2 289.12,-679.2 283.12,-679.2"/>
<text xml:space="preserve" text-anchor="start" x="140.88" y="-663.2" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00">Envelope::new(addr, payload)</text>
<text xml:space="preserve" text-anchor="start" x="191.5" y="-643.1" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#666666">delivery.rs</text>
<text xml:space="preserve" text-anchor="start" x="173.98" y="-624.3" font-family="Helvetica,sans-Serif" font-size="9.00">dest: ActorAddress</text>
<text xml:space="preserve" text-anchor="start" x="148.83" y="-605.5" font-family="Helvetica,sans-Serif" font-size="9.00">payload: Box&lt;dyn Any + Send&gt;</text>
</g>
<!-- route&#45;&gt;envelope -->
<g id="edge4" class="edge">
<title>route&#45;&gt;envelope</title>
<path fill="none" stroke="black" d="M223.41,-751.61C221.58,-733.42 219.32,-710.95 217.3,-690.8"/>
<polygon fill="black" stroke="black" points="220.81,-690.69 216.33,-681.09 213.84,-691.39 220.81,-690.69"/>
<text xml:space="preserve" text-anchor="middle" x="245.9" y="-717.7" font-family="Helvetica,sans-Serif" font-size="9.00">cross worker</text>
<text xml:space="preserve" text-anchor="middle" x="245.9" y="-706.9" font-family="Helvetica,sans-Serif" font-size="9.00">(wid != self)</text>
</g>
<!-- inbox_send -->
<g id="node7" class="node">
<title>inbox_send</title>
<path fill="#d1f2eb" stroke="#148f77" d="M480.29,-669.8C480.29,-669.8 349.71,-669.8 349.71,-669.8 343.71,-669.8 337.71,-663.8 337.71,-657.8 337.71,-657.8 337.71,-616.2 337.71,-616.2 337.71,-610.2 343.71,-604.2 349.71,-604.2 349.71,-604.2 480.29,-604.2 480.29,-604.2 486.29,-604.2 492.29,-610.2 492.29,-616.2 492.29,-616.2 492.29,-657.8 492.29,-657.8 492.29,-663.8 486.29,-669.8 480.29,-669.8"/>
<text xml:space="preserve" text-anchor="start" x="349.71" y="-653.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00">InboxRegistry::try_deliver()</text>
<text xml:space="preserve" text-anchor="start" x="356.72" y="-633.7" font-family="Helvetica,sans-Serif" font-size="9.00">SenderT::try_send_any(msg)</text>
<text xml:space="preserve" text-anchor="start" x="354.97" y="-614.9" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#c0392b">downcast inside SenderT impl</text>
</g>
<!-- route&#45;&gt;inbox_send -->
<g id="edge5" class="edge">
<title>route&#45;&gt;inbox_send</title>
<path fill="none" stroke="black" d="M259.75,-761.38C289.01,-738.12 332.3,-703.72 365.73,-677.15"/>
<polygon fill="black" stroke="black" points="367.61,-680.13 373.26,-671.17 363.25,-674.65 367.61,-680.13"/>
<text xml:space="preserve" text-anchor="middle" x="357.65" y="-717.7" font-family="Helvetica,sans-Serif" font-size="9.00">not found</text>
<text xml:space="preserve" text-anchor="middle" x="357.65" y="-706.9" font-family="Helvetica,sans-Serif" font-size="9.00">(external inbox)</text>
</g>
<!-- mailbox -->
<g id="node9" class="node">
<title>mailbox</title>
<path fill="#fadbd8" stroke="black" d="M258.71,-389.2C258.71,-391.41 227.47,-393.2 189,-393.2 150.53,-393.2 119.29,-391.41 119.29,-389.2 119.29,-389.2 119.29,-353.2 119.29,-353.2 119.29,-350.99 150.53,-349.2 189,-349.2 227.47,-349.2 258.71,-350.99 258.71,-353.2 258.71,-353.2 258.71,-389.2 258.71,-389.2"/>
<path fill="none" stroke="black" d="M258.71,-389.2C258.71,-386.99 227.47,-385.2 189,-385.2 150.53,-385.2 119.29,-386.99 119.29,-389.2"/>
<text xml:space="preserve" text-anchor="middle" x="189" y="-374.2" font-family="Helvetica,sans-Serif" font-size="10.00">ActorSlot::mailbox</text>
<text xml:space="preserve" text-anchor="middle" x="189" y="-362.2" font-family="Helvetica,sans-Serif" font-size="10.00">VecDeque&lt;Box&lt;dyn Any&gt;&gt;</text>
</g>
<!-- pending&#45;&gt;mailbox -->
<g id="edge8" class="edge">
<title>pending&#45;&gt;mailbox</title>
<path fill="none" stroke="black" d="M157.41,-533.4C159.45,-508.27 162.98,-464.8 162.98,-464.8 162.98,-464.8 172.39,-431.29 179.9,-404.59"/>
<polygon fill="black" stroke="black" points="183.22,-405.71 182.56,-395.13 176.48,-403.81 183.22,-405.71"/>
<text xml:space="preserve" text-anchor="middle" x="188.99" y="-489.5" font-family="Helvetica,sans-Serif" font-size="9.00">Phase 5</text>
<text xml:space="preserve" text-anchor="middle" x="188.99" y="-478.7" font-family="Helvetica,sans-Serif" font-size="9.00">local delivery</text>
</g>
<!-- channel -->
<g id="node8" class="node">
<title>channel</title>
<path fill="#fadbd8" stroke="black" d="M371.9,-504.8C371.9,-507.01 341.46,-508.8 304,-508.8 266.54,-508.8 236.1,-507.01 236.1,-504.8 236.1,-504.8 236.1,-468.8 236.1,-468.8 236.1,-466.59 266.54,-464.8 304,-464.8 341.46,-464.8 371.9,-466.59 371.9,-468.8 371.9,-468.8 371.9,-504.8 371.9,-504.8"/>
<path fill="none" stroke="black" d="M371.9,-504.8C371.9,-502.59 341.46,-500.8 304,-500.8 266.54,-500.8 236.1,-502.59 236.1,-504.8"/>
<text xml:space="preserve" text-anchor="middle" x="304" y="-489.8" font-family="Helvetica,sans-Serif" font-size="10.00">HybridChannel</text>
<text xml:space="preserve" text-anchor="middle" x="304" y="-477.8" font-family="Helvetica,sans-Serif" font-size="10.00">(ArrayQueue + SegQueue)</text>
</g>
<!-- envelope&#45;&gt;channel -->
<g id="edge6" class="edge">
<title>envelope&#45;&gt;channel</title>
<path fill="none" stroke="black" d="M237.87,-594.32C252.74,-570.38 271.1,-540.8 284.7,-518.9"/>
<polygon fill="black" stroke="black" points="287.67,-520.75 289.97,-510.41 281.72,-517.06 287.67,-520.75"/>
<text xml:space="preserve" text-anchor="middle" x="291.5" y="-549.1" font-family="Helvetica,sans-Serif" font-size="9.00">try_send</text>
</g>
<!-- channel&#45;&gt;mailbox -->
<g id="edge7" class="edge">
<title>channel&#45;&gt;mailbox</title>
<path fill="none" stroke="black" d="M282.37,-464.43C264.38,-446.66 238.59,-421.19 218.67,-401.51"/>
<polygon fill="black" stroke="black" points="221.18,-399.07 211.61,-394.54 216.26,-404.05 221.18,-399.07"/>
<text xml:space="preserve" text-anchor="middle" x="290.06" y="-431.7" font-family="Helvetica,sans-Serif" font-size="9.00">transfer_rx.pop()</text>
<text xml:space="preserve" text-anchor="middle" x="290.06" y="-420.9" font-family="Helvetica,sans-Serif" font-size="9.00">→ pool.deliver()</text>
</g>
<!-- handle_any -->
<g id="node10" class="node">
<title>handle_any</title>
<path fill="#d5f5e3" stroke="#27ae60" d="M265.96,-276.2C265.96,-276.2 112.04,-276.2 112.04,-276.2 106.04,-276.2 100.04,-270.2 100.04,-264.2 100.04,-264.2 100.04,-241.4 100.04,-241.4 100.04,-235.4 106.04,-229.4 112.04,-229.4 112.04,-229.4 265.96,-229.4 265.96,-229.4 271.96,-229.4 277.96,-235.4 277.96,-241.4 277.96,-241.4 277.96,-264.2 277.96,-264.2 277.96,-270.2 271.96,-276.2 265.96,-276.2"/>
<text xml:space="preserve" text-anchor="start" x="112.04" y="-260.2" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00">Actor&lt;A&gt;::handle_any(ctx, msg)</text>
<text xml:space="preserve" text-anchor="start" x="152.98" y="-240.1" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#666666">actor.rs line 56&#45;61</text>
</g>
<!-- mailbox&#45;&gt;handle_any -->
<g id="edge13" class="edge">
<title>mailbox&#45;&gt;handle_any</title>
<path fill="none" stroke="black" d="M189,-348.85C189,-339.58 189,-328.19 189,-316.56"/>
<polygon fill="black" stroke="black" points="192.5,-316.91 189,-306.91 185.5,-316.91 192.5,-316.91"/>
<text xml:space="preserve" text-anchor="middle" x="211.01" y="-316.1" font-family="Helvetica,sans-Serif" font-size="9.00">pop_front()</text>
</g>
<!-- downcast -->
<g id="node11" class="node">
<title>downcast</title>
<polygon fill="#fef9e7" stroke="#d4ac0d" points="189,-178.4 25.08,-160.4 189,-142.4 352.92,-160.4 189,-178.4"/>
<text xml:space="preserve" text-anchor="start" x="112.61" y="-158.4" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00">msg.downcast::&lt;A::Incoming&gt;()</text>
</g>
<!-- handle_any&#45;&gt;downcast -->
<g id="edge9" class="edge">
<title>handle_any&#45;&gt;downcast</title>
<path fill="none" stroke="black" d="M189,-228.99C189,-217.18 189,-202.64 189,-190.07"/>
<polygon fill="black" stroke="black" points="192.5,-190.3 189,-180.3 185.5,-190.3 192.5,-190.3"/>
</g>
<!-- ok_path -->
<g id="node12" class="node">
<title>ok_path</title>
<path fill="#d5f5e3" stroke="#27ae60" d="M147.53,-72.2C147.53,-72.2 28.47,-72.2 28.47,-72.2 22.47,-72.2 16.47,-66.2 16.47,-60.2 16.47,-60.2 16.47,-37.4 16.47,-37.4 16.47,-31.4 22.47,-25.4 28.47,-25.4 28.47,-25.4 147.53,-25.4 147.53,-25.4 153.53,-25.4 159.53,-31.4 159.53,-37.4 159.53,-37.4 159.53,-60.2 159.53,-60.2 159.53,-66.2 153.53,-72.2 147.53,-72.2"/>
<text xml:space="preserve" text-anchor="start" x="36.33" y="-56.2" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00">A::handle(ctx, *typed)</text>
<text xml:space="preserve" text-anchor="start" x="28.47" y="-36.1" font-family="Helvetica,sans-Serif" font-size="9.00">Fully typed handler invocation</text>
</g>
<!-- downcast&#45;&gt;ok_path -->
<g id="edge10" class="edge">
<title>downcast&#45;&gt;ok_path</title>
<path fill="none" stroke="#27ae60" d="M174.53,-143.7C159.45,-127.33 135.42,-101.26 116.51,-80.73"/>
<polygon fill="#27ae60" stroke="#27ae60" points="119.13,-78.42 109.78,-73.44 113.98,-83.16 119.13,-78.42"/>
<text xml:space="preserve" text-anchor="middle" x="167.3" y="-109.3" font-family="Helvetica,sans-Serif" font-size="9.00">Ok(typed)</text>
</g>
<!-- err_path -->
<g id="node13" class="node">
<title>err_path</title>
<path fill="#fadbd8" stroke="#c0392b" d="M351.28,-81.6C351.28,-81.6 214.72,-81.6 214.72,-81.6 208.72,-81.6 202.72,-75.6 202.72,-69.6 202.72,-69.6 202.72,-28 202.72,-28 202.72,-22 208.72,-16 214.72,-16 214.72,-16 351.28,-16 351.28,-16 357.28,-16 363.28,-22 363.28,-28 363.28,-28 363.28,-69.6 363.28,-69.6 363.28,-75.6 357.28,-81.6 351.28,-81.6"/>
<text xml:space="preserve" text-anchor="start" x="256.89" y="-65.6" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00">Silent drop</text>
<text xml:space="preserve" text-anchor="start" x="214.72" y="-45.5" font-family="Helvetica,sans-Serif" font-size="9.00">Wrong type — message discarded</text>
<text xml:space="preserve" text-anchor="start" x="226.47" y="-26.7" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#c0392b">type_mismatches counter++</text>
</g>
<!-- downcast&#45;&gt;err_path -->
<g id="edge11" class="edge">
<title>downcast&#45;&gt;err_path</title>
<path fill="none" stroke="#c0392b" d="M202.47,-143.7C214.39,-129.8 232.31,-108.9 248.23,-90.34"/>
<polygon fill="#c0392b" stroke="#c0392b" points="250.77,-92.76 254.62,-82.89 245.45,-88.2 250.77,-92.76"/>
<text xml:space="preserve" text-anchor="middle" x="244.68" y="-109.3" font-family="Helvetica,sans-Serif" font-size="9.00">Err(_)</text>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 19 KiB

View file

@ -0,0 +1,155 @@
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::thread;
use std::time::Duration;
use swactor::actor::{ActorAddress, ActorInterface, Ctx};
use swactor::config::RuntimeConfig;
use swactor::runtime::Runtime;
use runtime_dashboard::{start_dashboard, DashboardConfig};
// ── Demo actors ─────────────────────────────────────────────────────────
#[derive(Clone)]
struct Ping(ActorAddress);
struct PingActor {
count: u32,
}
impl PingActor {
fn new() -> Self {
Self { count: 0 }
}
}
impl ActorInterface for PingActor {
type Incoming = Ping;
type Response = ();
fn handle(&mut self, ctx: &Ctx, msg: Ping) {
self.count += 1;
// Forward to the target — creates cross-worker traffic
if self.count < 200 {
let _ = ctx.send(msg.0, Ping(ctx.self_addr()));
}
}
}
#[derive(Clone)]
struct Tick;
struct CounterActor {
ticks: u64,
}
impl CounterActor {
fn new() -> Self {
Self { ticks: 0 }
}
}
impl ActorInterface for CounterActor {
type Incoming = Tick;
type Response = ();
fn handle(&mut self, _ctx: &Ctx, _msg: Tick) {
self.ticks += 1;
}
}
// ── Main ────────────────────────────────────────────────────────────────
fn main() {
let stop = Arc::new(AtomicBool::new(false));
// Handle Ctrl+C gracefully
{
let stop = Arc::clone(&stop);
let _ = std::panic::catch_unwind(|| {
// Try to register a signal handler; fall back to running until killed
unsafe {
libc_signal(2, move || stop.store(true, Ordering::Relaxed));
}
});
}
let dash = start_dashboard(DashboardConfig {
port: 9090,
..Default::default()
});
dash.install_tracing();
let rt = Runtime::new(RuntimeConfig {
num_threads: 4,
max_actors: 1024,
actor_max_messages: 2000,
..Default::default()
});
// Spawn ping actors for cross-worker traffic
let mut ping_addrs = Vec::new();
for _ in 0..16 {
let addr = rt.spawn(PingActor::new()).unwrap();
ping_addrs.push(addr);
}
// Spawn counter actors for sustained traffic
let mut counter_addrs = Vec::new();
for _ in 0..20 {
let addr = rt.spawn(CounterActor::new()).unwrap();
counter_addrs.push(addr);
}
let handle = rt.run().expect("failed to start runtime");
dash.set_runtime(handle.runtime.clone());
eprintln!("Dashboard at http://localhost:9090 — press Ctrl+C to stop");
// Kick off ping-pong chains
for i in 0..ping_addrs.len() {
let target = ping_addrs[(i + 1) % ping_addrs.len()];
let _ = handle.runtime.send_to(ping_addrs[i], Ping(target));
}
let mut round: u64 = 0;
while !stop.load(Ordering::Relaxed) {
// Send ticks to all counter actors
for addr in &counter_addrs {
let _ = handle.runtime.send_to(*addr, Tick);
}
// Periodically spawn more actors
if round % 150 == 75 && counter_addrs.len() < 200 {
for _ in 0..8 {
match handle.runtime.spawn(CounterActor::new()) {
Ok(addr) => counter_addrs.push(addr),
Err(_) => break,
}
}
}
// Periodically re-kick ping chains
if round % 80 == 0 && round > 0 {
for i in 0..ping_addrs.len() {
let target = ping_addrs[(i + 1) % ping_addrs.len()];
let _ = handle.runtime.send_to(ping_addrs[i], Ping(target));
}
}
round += 1;
thread::sleep(Duration::from_millis(200));
}
eprintln!("\nShutting down...");
handle.shutdown();
dash.shutdown();
handle.join();
}
// Minimal signal handling without external deps
unsafe fn libc_signal(_sig: i32, _handler: impl FnOnce()) {
// This is a no-op fallback; the loop checks the AtomicBool
// In practice, Ctrl+C will terminate the process
}

View file

@ -0,0 +1,125 @@
use std::thread;
use std::time::Duration;
use swactor::actor::{ActorAddress, ActorInterface, Ctx};
use swactor::config::RuntimeConfig;
use swactor::runtime::Runtime;
use runtime_dashboard::{start_dashboard, DashboardConfig};
// ── Demo actors ─────────────────────────────────────────────────────────
#[derive(Clone)]
struct Ping(ActorAddress);
struct PingActor {
count: u32,
}
impl PingActor {
fn new() -> Self {
Self { count: 0 }
}
}
impl ActorInterface for PingActor {
type Incoming = Ping;
type Response = ();
fn handle(&mut self, ctx: &Ctx, msg: Ping) {
self.count += 1;
if self.count < 100 {
let _ = ctx.send(msg.0, Ping(ctx.self_addr()));
}
}
}
#[derive(Clone)]
struct Tick;
struct CounterActor;
impl ActorInterface for CounterActor {
type Incoming = Tick;
type Response = ();
fn handle(&mut self, _ctx: &Ctx, _msg: Tick) {}
}
// ── Main ────────────────────────────────────────────────────────────────
fn main() {
let dash = start_dashboard(DashboardConfig {
port: 9090,
record: true, // Enable trace recording
..Default::default()
});
dash.install_tracing();
let rt = Runtime::new(RuntimeConfig {
num_threads: 4,
max_actors: 512,
actor_max_messages: 1000,
..Default::default()
});
let mut ping_addrs = Vec::new();
for _ in 0..12 {
let addr = rt.spawn(PingActor::new()).unwrap();
ping_addrs.push(addr);
}
let mut counter_addrs = Vec::new();
for _ in 0..16 {
let addr = rt.spawn(CounterActor).unwrap();
counter_addrs.push(addr);
}
let handle = rt.run().expect("failed to start runtime");
dash.set_runtime(handle.runtime.clone());
eprintln!("Recording trace for 10 seconds...");
eprintln!("Dashboard at http://localhost:9090");
// Kick off ping-pong chains
for i in 0..ping_addrs.len() {
let target = ping_addrs[(i + 1) % ping_addrs.len()];
let _ = handle.runtime.send_to(ping_addrs[i], Ping(target));
}
for round in 0..50 {
for addr in &counter_addrs {
let _ = handle.runtime.send_to(*addr, Tick);
}
// Spawn more actors mid-recording
if round == 20 {
for _ in 0..8 {
let addr = handle.runtime.spawn(CounterActor).unwrap();
counter_addrs.push(addr);
}
eprintln!(" Spawned 8 more actors");
}
// Re-kick pings
if round == 25 {
for i in 0..ping_addrs.len() {
let target = ping_addrs[(i + 1) % ping_addrs.len()];
let _ = handle.runtime.send_to(ping_addrs[i], Ping(target));
}
}
thread::sleep(Duration::from_millis(200));
}
handle.shutdown();
dash.shutdown();
handle.join();
// Save trace
let path = "runtime_trace.json";
match dash.save_trace(path) {
Ok(()) => eprintln!("Trace saved to {path}"),
Err(e) => eprintln!("Failed to save trace: {e}"),
}
}

View file

@ -0,0 +1,23 @@
use runtime_dashboard::{serve_replay, ReplayConfig};
fn main() {
let args: Vec<String> = std::env::args().collect();
if args.len() < 2 {
eprintln!("Usage: replay_demo <trace.json> [speed]");
eprintln!(" speed: playback multiplier (default 1.0, e.g. 2.0 = 2x speed)");
std::process::exit(1);
}
let path = &args[1];
let speed = args
.get(2)
.and_then(|s| s.parse::<f64>().ok())
.unwrap_or(1.0);
eprintln!("Replaying {path} at {speed}x speed");
if let Err(e) = serve_replay(path, ReplayConfig { port: 9090, speed }) {
eprintln!("Error: {e}");
std::process::exit(1);
}
}

View file

@ -0,0 +1,369 @@
pub const DASHBOARD_HTML: &str = r##"<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Swactor Runtime Dashboard</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: 'Menlo', 'Consolas', 'Monaco', monospace; background: #0f1117; color: #e0e0e0; font-size: 13px; }
.header {
display: flex; align-items: center; justify-content: space-between;
padding: 12px 20px; background: #161822; border-bottom: 1px solid #2a2d3e;
}
.header h1 { font-size: 16px; font-weight: 600; color: #fff; }
.status-dot {
width: 10px; height: 10px; border-radius: 50%; background: #4caf50;
display: inline-block; margin-left: 8px; vertical-align: middle;
}
.status-dot.disconnected { background: #f44336; }
.status-dot.done { background: #ff9800; }
.status-dot.replaying { background: #2196f3; animation: pulse 1.5s infinite; }
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.4; }
}
.replay-badge {
display: none; background: #2196f3; color: #fff; font-size: 10px; font-weight: 700;
padding: 2px 8px; border-radius: 3px; margin-left: 10px; letter-spacing: 1px;
vertical-align: middle;
}
.replay-badge.visible { display: inline-block; }
.header-right { display: flex; align-items: center; gap: 12px; }
.progress-bar-wrap {
display: none; width: 100%; height: 3px; background: #2a2d3e;
}
.progress-bar-wrap.visible { display: block; }
.progress-fill {
height: 100%; width: 0%; background: #2196f3; transition: width 0.3s;
}
.grid {
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-rows: auto auto;
gap: 12px; padding: 12px;
}
.panel {
background: #161822; border: 1px solid #2a2d3e; border-radius: 6px;
padding: 14px; overflow: hidden;
}
.panel h2 { font-size: 12px; color: #888; text-transform: uppercase; letter-spacing: 1px; margin-bottom: 10px; }
.stats-cards {
display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px;
}
.stat-card {
background: #1c1f2e; border-radius: 4px; padding: 10px; text-align: center;
}
.stat-card .value { font-size: 22px; font-weight: 700; color: #fff; }
.stat-card .label { font-size: 10px; color: #888; text-transform: uppercase; margin-top: 2px; }
.chart-panel { grid-row: span 2; }
canvas#workerChart { width: 100%; height: 200px; }
.actor-table-wrap { max-height: 200px; overflow-y: auto; }
.actor-table-wrap table { width: 100%; border-collapse: collapse; }
.actor-table-wrap th, .actor-table-wrap td {
padding: 4px 8px; text-align: left; border-bottom: 1px solid #2a2d3e; font-size: 12px;
}
.actor-table-wrap th { color: #888; font-weight: 500; position: sticky; top: 0; background: #161822; }
.log-panel {
grid-column: 1 / -1;
}
.log-wrap { max-height: 300px; overflow-y: auto; }
.log-wrap table { width: 100%; border-collapse: collapse; }
.log-wrap th, .log-wrap td {
padding: 3px 8px; text-align: left; border-bottom: 1px solid #1c1f2e; font-size: 11px;
white-space: nowrap;
}
.log-wrap th { color: #888; font-weight: 500; position: sticky; top: 0; background: #161822; }
.log-wrap td.msg { white-space: normal; word-break: break-all; max-width: 400px; }
.level-ERROR { color: #f44336; font-weight: 700; }
.level-WARN { color: #ff9800; }
.level-INFO { color: #4caf50; }
.level-DEBUG { color: #2196f3; }
.level-TRACE { color: #666; }
::-webkit-scrollbar { width: 6px; }
::-webkit-scrollbar-track { background: #0f1117; }
::-webkit-scrollbar-thumb { background: #2a2d3e; border-radius: 3px; }
</style>
</head>
<body>
<div class="header">
<h1>
Swactor Runtime Dashboard
<span id="statusDot" class="status-dot"></span>
<span id="replayBadge" class="replay-badge">REPLAY</span>
</h1>
<div class="header-right">
<span id="replaySpeed" style="color:#2196f3;font-size:12px;display:none;"></span>
<span id="replayPct" style="color:#888;font-size:12px;display:none;"></span>
<span id="uptimeLabel" style="color:#888;font-size:12px;"></span>
</div>
</div>
<div id="progressBarWrap" class="progress-bar-wrap">
<div id="progressFill" class="progress-fill"></div>
</div>
<div class="grid">
<div class="panel chart-panel">
<h2>Worker Distribution</h2>
<canvas id="workerChart"></canvas>
<div id="workerLegend" style="margin-top:8px;font-size:11px;color:#888;"></div>
</div>
<div class="panel">
<h2>Stats</h2>
<div class="stats-cards">
<div class="stat-card"><div class="value" id="statActors">0</div><div class="label">Actors</div></div>
<div class="stat-card"><div class="value" id="statMessages">0</div><div class="label">Messages</div></div>
<div class="stat-card"><div class="value" id="statWorkers">0</div><div class="label">Workers</div></div>
<div class="stat-card"><div class="value" id="statMailbox">0</div><div class="label">Mailbox</div></div>
</div>
</div>
<div class="panel">
<h2>Actors</h2>
<div class="actor-table-wrap">
<table>
<thead><tr><th>Address</th><th>Worker</th></tr></thead>
<tbody id="actorTableBody"></tbody>
</table>
</div>
</div>
<div class="panel log-panel">
<h2>Activity Log <span id="logCount" style="color:#555;font-weight:400;"></span></h2>
<div class="log-wrap" id="logWrap">
<table>
<thead><tr><th>Seq</th><th>Time</th><th>Level</th><th>Worker</th><th>Message</th><th>Fields</th></tr></thead>
<tbody id="logTableBody"></tbody>
</table>
</div>
</div>
</div>
<script>
(function() {
var DASHBOARD_MODE = '__DASHBOARD_MODE__';
var startTime = Date.now();
var logRowCount = 0;
var MAX_LOG_ROWS = 2000;
var isReplay = (DASHBOARD_MODE === 'replay');
var dot = document.getElementById('statusDot');
var uptimeLabel = document.getElementById('uptimeLabel');
var replayBadge = document.getElementById('replayBadge');
var replaySpeed = document.getElementById('replaySpeed');
var replayPct = document.getElementById('replayPct');
var progressBarWrap = document.getElementById('progressBarWrap');
var progressFill = document.getElementById('progressFill');
if (isReplay) {
replayBadge.className = 'replay-badge visible';
progressBarWrap.className = 'progress-bar-wrap visible';
dot.className = 'status-dot replaying';
uptimeLabel.style.display = 'none';
}
function updateUptime() {
if (isReplay) return;
var s = Math.floor((Date.now() - startTime) / 1000);
var m = Math.floor(s / 60);
var h = Math.floor(m / 60);
uptimeLabel.textContent = (h > 0 ? h + 'h ' : '') + (m % 60) + 'm ' + (s % 60) + 's';
}
setInterval(updateUptime, 1000);
var canvas = document.getElementById('workerChart');
var ctx = canvas.getContext('2d');
var colors = ['#4caf50','#2196f3','#ff9800','#f44336','#9c27b0','#00bcd4','#ffeb3b','#e91e63'];
function drawWorkerChart(workers) {
var dpr = window.devicePixelRatio || 1;
var rect = canvas.getBoundingClientRect();
canvas.width = rect.width * dpr;
canvas.height = rect.height * dpr;
ctx.scale(dpr, dpr);
var W = rect.width, H = rect.height;
ctx.clearRect(0, 0, W, H);
if (!workers || workers.length === 0) return;
var maxActors = Math.max(1, Math.max.apply(null, workers.map(function(w) { return w.num_actors; })));
var barW = Math.max(8, Math.floor((W - 40) / workers.length) - 6);
var chartH = H - 30;
workers.forEach(function(w, i) {
var x = 20 + i * (barW + 6);
var h = (w.num_actors / maxActors) * (chartH * 0.45);
ctx.fillStyle = colors[i % colors.length];
ctx.globalAlpha = 0.8;
ctx.fillRect(x, chartH * 0.5 - h, barW, h);
var mh = Math.min(w.mailbox_depth * 2, chartH * 0.4);
ctx.globalAlpha = 0.4;
ctx.fillRect(x, chartH * 0.55, barW, mh);
ctx.globalAlpha = 1;
ctx.fillStyle = '#888';
ctx.font = '10px monospace';
ctx.textAlign = 'center';
ctx.fillText('W' + w.id, x + barW / 2, H - 2);
});
var legend = document.getElementById('workerLegend');
legend.innerHTML = workers.map(function(w, i) {
return '<span style="color:' + colors[i % colors.length] + '">W' + w.id +
': ' + w.num_actors + ' actors, ' + w.messages_processed + ' msgs, mbox ' + w.mailbox_depth + '</span>';
}).join(' &nbsp;|&nbsp; ');
}
function updateStats(data) {
var totalActors = data.actors ? data.actors.length : 0;
var totalMsgs = data.workers ? data.workers.reduce(function(s, w) { return s + w.messages_processed; }, 0) : 0;
var totalMailbox = data.workers ? data.workers.reduce(function(s, w) { return s + w.mailbox_depth; }, 0) : 0;
document.getElementById('statActors').textContent = totalActors;
document.getElementById('statMessages').textContent = totalMsgs.toLocaleString();
document.getElementById('statWorkers').textContent = data.num_workers || 0;
document.getElementById('statMailbox').textContent = totalMailbox;
drawWorkerChart(data.workers);
var tbody = document.getElementById('actorTableBody');
tbody.innerHTML = '';
if (data.actors) {
var shown = data.actors.slice(0, 200);
shown.forEach(function(entry) {
var addr = entry[0];
var wid = entry[1];
var hex = '';
if (addr && addr.length > 0) {
var bytes = Array.isArray(addr) ? addr : Object.values(addr);
for (var j = 0; j < Math.min(8, bytes.length); j++) {
hex += ('0' + bytes[j].toString(16)).slice(-2);
}
hex += '\u2026';
}
var tr = document.createElement('tr');
tr.innerHTML = '<td style="color:#aaa;font-size:11px;">' + hex + '</td><td>W' + wid + '</td>';
tbody.appendChild(tr);
});
if (data.actors.length > 200) {
var tr2 = document.createElement('tr');
tr2.innerHTML = '<td colspan="2" style="color:#555;">... and ' + (data.actors.length - 200) + ' more</td>';
tbody.appendChild(tr2);
}
}
}
function addLogEvents(events) {
var tbody = document.getElementById('logTableBody');
var wrap = document.getElementById('logWrap');
var wasAtBottom = wrap.scrollTop + wrap.clientHeight >= wrap.scrollHeight - 20;
events.forEach(function(ev) {
if (logRowCount >= MAX_LOG_ROWS) {
tbody.removeChild(tbody.firstChild);
logRowCount--;
}
var tr = document.createElement('tr');
var ts = new Date(ev.timestamp_ms);
var timeStr = ts.toLocaleTimeString() + '.' + String(ts.getMilliseconds()).padStart(3, '0');
var wid = ev.worker_id !== null && ev.worker_id !== undefined ? 'W' + ev.worker_id : '-';
var fieldsStr = Object.keys(ev.fields).length > 0 ? JSON.stringify(ev.fields) : '';
tr.innerHTML =
'<td>' + ev.seq + '</td>' +
'<td>' + timeStr + '</td>' +
'<td class="level-' + ev.level + '">' + ev.level + '</td>' +
'<td>' + wid + '</td>' +
'<td class="msg">' + escapeHtml(ev.message) + '</td>' +
'<td style="color:#555;max-width:300px;overflow:hidden;text-overflow:ellipsis;">' + escapeHtml(fieldsStr) + '</td>';
tbody.appendChild(tr);
logRowCount++;
});
document.getElementById('logCount').textContent = '(' + logRowCount + ')';
if (wasAtBottom) {
wrap.scrollTop = wrap.scrollHeight;
}
}
function escapeHtml(s) {
if (!s) return '';
return s.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
}
function setStatus(s) {
if (s === 'done') {
dot.className = 'status-dot done';
if (isReplay) {
replayPct.textContent = '100%';
progressFill.style.width = '100%';
}
} else if (s === 'disconnected') {
dot.className = 'status-dot disconnected';
} else {
dot.className = isReplay ? 'status-dot replaying' : 'status-dot';
}
}
var es = new EventSource('/events');
es.addEventListener('stats', function(e) {
try { updateStats(JSON.parse(e.data)); } catch(err) { console.error('stats parse error', err); }
});
es.addEventListener('activity', function(e) {
try { addLogEvents(JSON.parse(e.data)); } catch(err) { console.error('activity parse error', err); }
});
es.addEventListener('replay_meta', function(e) {
try {
var meta = JSON.parse(e.data);
replaySpeed.textContent = meta.speed + 'x';
replaySpeed.style.display = 'inline';
replayPct.style.display = 'inline';
replayPct.textContent = '0%';
} catch(err) { console.error('replay_meta parse error', err); }
});
es.addEventListener('replay_progress', function(e) {
try {
var data = JSON.parse(e.data);
var pct = Math.round(data.progress * 100);
replayPct.textContent = pct + '%';
progressFill.style.width = pct + '%';
} catch(err) { console.error('replay_progress parse error', err); }
});
es.addEventListener('done', function() {
setStatus('done');
es.close();
});
es.onerror = function() {
setStatus('disconnected');
};
es.onopen = function() {
setStatus('connected');
};
})();
</script>
</body>
</html>
"##;

View file

@ -0,0 +1,227 @@
use std::collections::VecDeque;
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Mutex;
use std::time::{SystemTime, UNIX_EPOCH};
use serde::{Deserialize, Serialize};
use tracing::field::{Field, Visit};
use tracing::span;
use tracing::Subscriber;
use tracing_subscriber::layer::Context;
use tracing_subscriber::registry::LookupSpan;
use tracing_subscriber::Layer;
/// A single captured tracing event.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DashboardEvent {
pub seq: u64,
pub timestamp_ms: u64,
pub level: String,
pub message: String,
pub worker_id: Option<usize>,
pub fields: serde_json::Map<String, serde_json::Value>,
}
/// Thread-safe ring buffer for dashboard events, with optional full-log recording.
pub struct EventStore {
events: Mutex<VecDeque<DashboardEvent>>,
capacity: usize,
next_seq: AtomicU64,
/// When recording is enabled, all events are also appended here (unbounded).
full_log: Option<Mutex<Vec<DashboardEvent>>>,
}
impl EventStore {
pub fn new(capacity: usize, record: bool) -> Self {
Self {
events: Mutex::new(VecDeque::with_capacity(capacity)),
capacity,
next_seq: AtomicU64::new(0),
full_log: if record {
Some(Mutex::new(Vec::new()))
} else {
None
},
}
}
pub fn push(&self, mut event: DashboardEvent) {
event.seq = self.next_seq.fetch_add(1, Ordering::Relaxed);
if let Some(ref log) = self.full_log {
log.lock().unwrap().push(event.clone());
}
let mut events = self.events.lock().unwrap();
if events.len() >= self.capacity {
events.pop_front();
}
events.push_back(event);
}
/// Read events starting from `cursor`. Returns the new events and the updated cursor.
pub fn read_from(&self, cursor: u64) -> (Vec<DashboardEvent>, u64) {
let events = self.events.lock().unwrap();
if events.is_empty() {
return (Vec::new(), cursor);
}
let first_seq = events.front().unwrap().seq;
let last_seq = events.back().unwrap().seq;
if cursor > last_seq {
return (Vec::new(), cursor);
}
let start = if cursor <= first_seq {
0
} else {
(cursor - first_seq) as usize
};
let batch: Vec<DashboardEvent> = events.iter().skip(start).cloned().collect();
let new_cursor = last_seq + 1;
(batch, new_cursor)
}
/// Returns a clone of the full event log. Only available when recording is enabled.
pub fn all_events(&self) -> Option<Vec<DashboardEvent>> {
self.full_log
.as_ref()
.map(|log| log.lock().unwrap().clone())
}
}
pub(crate) fn now_ms() -> u64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_millis() as u64
}
/// Visitor that extracts the message string and collects other fields.
struct FieldVisitor {
message: String,
fields: serde_json::Map<String, serde_json::Value>,
}
impl FieldVisitor {
fn new() -> Self {
Self {
message: String::new(),
fields: serde_json::Map::new(),
}
}
}
impl Visit for FieldVisitor {
fn record_debug(&mut self, field: &Field, value: &dyn std::fmt::Debug) {
if field.name() == "message" {
self.message = format!("{:?}", value);
} else {
self.fields.insert(
field.name().to_string(),
serde_json::Value::String(format!("{:?}", value)),
);
}
}
fn record_u64(&mut self, field: &Field, value: u64) {
if field.name() == "message" {
self.message = value.to_string();
} else {
self.fields.insert(
field.name().to_string(),
serde_json::Value::Number(value.into()),
);
}
}
fn record_i64(&mut self, field: &Field, value: i64) {
if field.name() == "message" {
self.message = value.to_string();
} else {
self.fields.insert(
field.name().to_string(),
serde_json::Value::Number(value.into()),
);
}
}
fn record_str(&mut self, field: &Field, value: &str) {
if field.name() == "message" {
self.message = value.to_string();
} else {
self.fields.insert(
field.name().to_string(),
serde_json::Value::String(value.to_string()),
);
}
}
}
/// A tracing Layer that captures events into an EventStore.
pub struct DashboardLayer {
store: std::sync::Arc<EventStore>,
}
impl DashboardLayer {
pub fn new(store: std::sync::Arc<EventStore>) -> Self {
Self { store }
}
}
impl<S> Layer<S> for DashboardLayer
where
S: Subscriber + for<'a> LookupSpan<'a>,
{
fn on_event(&self, event: &tracing::Event<'_>, ctx: Context<'_, S>) {
let mut visitor = FieldVisitor::new();
event.record(&mut visitor);
// Walk span context to find worker_id
let mut worker_id = None;
if let Some(scope) = ctx.event_scope(event) {
for span in scope {
let exts = span.extensions();
if let Some(wid) = exts.get::<WorkerIdField>() {
worker_id = Some(wid.0);
break;
}
}
}
// Also check if worker_id was a field on the event itself
if worker_id.is_none() {
if let Some(serde_json::Value::Number(n)) = visitor.fields.get("worker_id") {
worker_id = n.as_u64().map(|v| v as usize);
}
}
let dashboard_event = DashboardEvent {
seq: 0, // filled by push()
timestamp_ms: now_ms(),
level: event.metadata().level().to_string(),
message: visitor.message,
worker_id,
fields: visitor.fields,
};
self.store.push(dashboard_event);
}
fn on_new_span(&self, attrs: &span::Attributes<'_>, id: &span::Id, ctx: Context<'_, S>) {
// Extract worker_id from span fields and store in extensions
let mut visitor = FieldVisitor::new();
attrs.record(&mut visitor);
if let Some(serde_json::Value::Number(n)) = visitor.fields.get("worker_id") {
if let Some(wid) = n.as_u64() {
if let Some(span) = ctx.span(id) {
span.extensions_mut().insert(WorkerIdField(wid as usize));
}
}
}
}
}
/// Stored in span extensions to propagate worker_id to child events.
struct WorkerIdField(usize);

View file

@ -0,0 +1,198 @@
pub mod layer;
pub mod trace;
mod dashboard_html;
mod server;
use std::io;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;
use swactor::config::RuntimeConfig;
use swactor::runtime::{Runtime, RuntimeHandle};
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::util::SubscriberInitExt;
use crate::layer::{now_ms, DashboardLayer, EventStore};
use crate::trace::{RuntimeTrace, TimestampedStats};
/// Configuration for the runtime dashboard.
#[derive(Debug, Clone)]
pub struct DashboardConfig {
pub port: u16,
pub event_capacity: usize,
/// Enable trace recording for `save_trace()`. When true, all events
/// are kept in an unbounded log and stats are periodically sampled.
pub record: bool,
}
impl Default for DashboardConfig {
fn default() -> Self {
Self {
port: 9090,
event_capacity: 10_000,
record: false,
}
}
}
/// Configuration for replaying a recorded trace.
#[derive(Debug, Clone)]
pub struct ReplayConfig {
pub port: u16,
/// Playback speed multiplier (1.0 = real-time, 2.0 = double speed).
pub speed: f64,
}
impl Default for ReplayConfig {
fn default() -> Self {
Self {
port: 9090,
speed: 1.0,
}
}
}
/// Handle to a running dashboard. Allows attaching a runtime after creation.
pub struct DashboardHandle {
store: Arc<EventStore>,
runtime: Arc<Mutex<Option<Arc<Runtime>>>>,
shutdown: Arc<AtomicBool>,
stats_timeline: Arc<Mutex<Vec<TimestampedStats>>>,
recording: bool,
}
impl DashboardHandle {
/// Install a global tracing subscriber with the dashboard layer.
pub fn install_tracing(&self) {
tracing_subscriber::registry()
.with(DashboardLayer::new(Arc::clone(&self.store)))
.init();
}
/// Return the raw `DashboardLayer` for users who want to compose their own subscriber.
pub fn layer(&self) -> DashboardLayer {
DashboardLayer::new(Arc::clone(&self.store))
}
/// Attach a runtime to the dashboard, enabling stats polling.
pub fn set_runtime(&self, runtime: Arc<Runtime>) {
*self.runtime.lock().unwrap() = Some(runtime);
}
/// Whether trace recording is enabled.
pub fn is_recording(&self) -> bool {
self.recording
}
/// Signal the dashboard to shut down (SSE clients receive "done").
pub fn shutdown(&self) {
self.shutdown.store(true, Ordering::Release);
}
/// Save the recorded trace to a JSON file.
///
/// Only works when `DashboardConfig::record` was set to `true`.
pub fn save_trace(&self, path: &str) -> io::Result<()> {
let events = self.store.all_events().ok_or_else(|| {
io::Error::new(
io::ErrorKind::Other,
"recording not enabled (set DashboardConfig::record = true)",
)
})?;
let stats_timeline = self.stats_timeline.lock().unwrap().clone();
let trace = RuntimeTrace {
events,
stats_timeline,
};
let json = serde_json::to_string(&trace)
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
std::fs::write(path, json)
}
}
/// Start a dashboard server and return a handle.
///
/// The dashboard starts serving immediately. Call `install_tracing()` to set up
/// the global subscriber, and `set_runtime()` to enable stats polling.
pub fn start_dashboard(config: DashboardConfig) -> DashboardHandle {
let store = Arc::new(EventStore::new(config.event_capacity, config.record));
let runtime: Arc<Mutex<Option<Arc<Runtime>>>> = Arc::new(Mutex::new(None));
let shutdown = Arc::new(AtomicBool::new(false));
let stats_timeline = Arc::new(Mutex::new(Vec::new()));
server::spawn_http_server(
Arc::clone(&store),
Arc::clone(&runtime),
Arc::clone(&shutdown),
config.port,
);
// Start stats recorder thread when recording is enabled
if config.record {
let rt_ref = Arc::clone(&runtime);
let timeline = Arc::clone(&stats_timeline);
let stop = Arc::clone(&shutdown);
thread::spawn(move || {
loop {
if stop.load(Ordering::Relaxed) {
return;
}
let maybe_rt = rt_ref.lock().unwrap().clone();
if let Some(rt) = maybe_rt {
let stats = rt.stats();
let ts = TimestampedStats {
timestamp_ms: now_ms(),
stats,
};
timeline.lock().unwrap().push(ts);
}
thread::sleep(Duration::from_millis(200));
}
});
}
eprintln!("Runtime dashboard at http://localhost:{}", config.port);
DashboardHandle {
store,
runtime,
shutdown,
stats_timeline,
recording: config.record,
}
}
/// Convenience: create a runtime, start a dashboard, install tracing, and run.
///
/// Returns the runtime handle and dashboard handle.
pub fn run_with_dashboard(
rt_config: RuntimeConfig,
dash_config: DashboardConfig,
) -> (RuntimeHandle, DashboardHandle) {
let dash = start_dashboard(dash_config);
dash.install_tracing();
let rt = Runtime::new(rt_config);
let handle = rt.run().expect("failed to start runtime");
dash.set_runtime(Arc::clone(&handle.runtime));
(handle, dash)
}
/// Load a trace file and serve a replay dashboard. Blocks indefinitely.
pub fn serve_replay(path: &str, config: ReplayConfig) -> io::Result<()> {
let data = std::fs::read_to_string(path)?;
let trace: RuntimeTrace = serde_json::from_str(&data)
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
server::spawn_replay_server(Arc::new(trace), config.port, config.speed);
eprintln!("Replay dashboard at http://localhost:{}", config.port);
eprintln!("Press Ctrl+C to stop");
loop {
thread::sleep(Duration::from_secs(3600));
}
}

View file

@ -0,0 +1,337 @@
use std::io::{self, Read as IoRead};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{mpsc, Arc, Mutex};
use std::thread;
use std::time::{Duration, Instant};
use swactor::runtime::Runtime;
use crate::dashboard_html::DASHBOARD_HTML;
use crate::layer::EventStore;
use crate::trace::RuntimeTrace;
/// Format a server-sent event.
fn format_sse(event: &str, data: &str) -> Vec<u8> {
format!("event: {event}\ndata: {data}\n\n").into_bytes()
}
/// Adapts an `mpsc::Receiver<Vec<u8>>` to `std::io::Read` for tiny_http streaming.
struct ChannelReader {
rx: mpsc::Receiver<Vec<u8>>,
buf: Vec<u8>,
pos: usize,
}
impl ChannelReader {
fn new(rx: mpsc::Receiver<Vec<u8>>) -> Self {
Self {
rx,
buf: Vec::new(),
pos: 0,
}
}
}
impl IoRead for ChannelReader {
fn read(&mut self, out: &mut [u8]) -> io::Result<usize> {
// Drain current buffer first.
if self.pos < self.buf.len() {
let n = std::cmp::min(out.len(), self.buf.len() - self.pos);
out[..n].copy_from_slice(&self.buf[self.pos..self.pos + n]);
self.pos += n;
return Ok(n);
}
// Wait for next chunk.
match self.rx.recv() {
Ok(data) => {
if data.is_empty() {
return Ok(0); // EOF signal
}
let n = std::cmp::min(out.len(), data.len());
out[..n].copy_from_slice(&data[..n]);
if n < data.len() {
self.buf = data;
self.pos = n;
} else {
self.buf.clear();
self.pos = 0;
}
Ok(n)
}
Err(_) => Ok(0), // channel closed
}
}
}
fn make_sse_response(
rx: mpsc::Receiver<Vec<u8>>,
) -> tiny_http::Response<Box<dyn IoRead + Send>> {
let reader = ChannelReader::new(rx);
tiny_http::Response::new(
tiny_http::StatusCode(200),
vec![
"Content-Type: text/event-stream"
.parse::<tiny_http::Header>()
.unwrap(),
"Cache-Control: no-cache"
.parse::<tiny_http::Header>()
.unwrap(),
"Connection: keep-alive"
.parse::<tiny_http::Header>()
.unwrap(),
],
Box::new(reader) as Box<dyn IoRead + Send>,
None,
None,
)
}
fn respond_html(request: tiny_http::Request, mode: &str) {
let html = DASHBOARD_HTML.replace("__DASHBOARD_MODE__", mode);
let response = tiny_http::Response::from_string(html).with_header(
"Content-Type: text/html; charset=utf-8"
.parse::<tiny_http::Header>()
.unwrap(),
);
let _ = request.respond(response);
}
fn respond_404(request: tiny_http::Request) {
let response = tiny_http::Response::from_string("Not Found").with_status_code(404);
let _ = request.respond(response);
}
// ── Live server ─────────────────────────────────────────────────────────
/// Start the live HTTP server with a pool of handler threads.
pub(crate) fn spawn_http_server(
store: Arc<EventStore>,
runtime: Arc<Mutex<Option<Arc<Runtime>>>>,
shutdown: Arc<AtomicBool>,
port: u16,
) {
let addr = format!("0.0.0.0:{port}");
let server = tiny_http::Server::http(&addr).expect("failed to bind HTTP server");
let server = Arc::new(server);
for _ in 0..4 {
let server = Arc::clone(&server);
let store = Arc::clone(&store);
let runtime = Arc::clone(&runtime);
let shutdown = Arc::clone(&shutdown);
thread::spawn(move || {
loop {
let request = match server.recv() {
Ok(r) => r,
Err(_) => break,
};
let url = request.url().to_string();
match url.as_str() {
"/" => respond_html(request, "live"),
"/events" => {
handle_live_sse(
request,
Arc::clone(&store),
Arc::clone(&runtime),
Arc::clone(&shutdown),
);
}
"/api/stats" => {
handle_stats_api(request, Arc::clone(&runtime));
}
_ => respond_404(request),
}
}
});
}
}
fn handle_live_sse(
request: tiny_http::Request,
store: Arc<EventStore>,
runtime: Arc<Mutex<Option<Arc<Runtime>>>>,
shutdown: Arc<AtomicBool>,
) {
let (tx, rx) = mpsc::channel::<Vec<u8>>();
let response = make_sse_response(rx);
// Spawn producer thread
thread::spawn(move || {
let mut cursor: u64 = 0;
loop {
// Send stats if runtime is available
{
let maybe_rt = runtime.lock().unwrap().clone();
if let Some(rt) = maybe_rt {
let stats = rt.stats();
let json = serde_json::to_string(&stats).unwrap();
if tx.send(format_sse("stats", &json)).is_err() {
return;
}
}
}
// Send new activity events
let (batch, new_cursor) = store.read_from(cursor);
if !batch.is_empty() {
let json = serde_json::to_string(&batch).unwrap();
if tx.send(format_sse("activity", &json)).is_err() {
return;
}
cursor = new_cursor;
}
if shutdown.load(Ordering::Relaxed) {
let _ = tx.send(format_sse("done", "{}"));
let _ = tx.send(Vec::new()); // EOF
return;
}
thread::sleep(Duration::from_millis(200));
}
});
// Blocks until connection closes
let _ = request.respond(response);
}
fn handle_stats_api(request: tiny_http::Request, runtime: Arc<Mutex<Option<Arc<Runtime>>>>) {
let maybe_rt = runtime.lock().unwrap().clone();
let json = match maybe_rt {
Some(rt) => serde_json::to_string(&rt.stats()).unwrap(),
None => "{}".to_string(),
};
let response = tiny_http::Response::from_string(json).with_header(
"Content-Type: application/json"
.parse::<tiny_http::Header>()
.unwrap(),
);
let _ = request.respond(response);
}
// ── Replay server ───────────────────────────────────────────────────────
/// Start a replay HTTP server that serves a pre-recorded trace.
pub(crate) fn spawn_replay_server(trace: Arc<RuntimeTrace>, port: u16, speed: f64) {
let addr = format!("0.0.0.0:{port}");
let server = tiny_http::Server::http(&addr).expect("failed to bind HTTP server");
let server = Arc::new(server);
for _ in 0..4 {
let server = Arc::clone(&server);
let trace = Arc::clone(&trace);
thread::spawn(move || {
loop {
let request = match server.recv() {
Ok(r) => r,
Err(_) => break,
};
let url = request.url().to_string();
match url.as_str() {
"/" => respond_html(request, "replay"),
"/events" => {
handle_replay_sse(request, Arc::clone(&trace), speed);
}
_ => respond_404(request),
}
}
});
}
}
fn handle_replay_sse(request: tiny_http::Request, trace: Arc<RuntimeTrace>, speed: f64) {
let (tx, rx) = mpsc::channel::<Vec<u8>>();
let response = make_sse_response(rx);
thread::spawn(move || {
// Send replay metadata
let meta = serde_json::json!({
"total_events": trace.events.len(),
"total_stats": trace.stats_timeline.len(),
"speed": speed,
});
if tx.send(format_sse("replay_meta", &meta.to_string())).is_err() {
return;
}
// Find the earliest timestamp across events and stats
let base_time = trace
.events
.first()
.map(|e| e.timestamp_ms)
.into_iter()
.chain(trace.stats_timeline.first().map(|s| s.timestamp_ms))
.min()
.unwrap_or(0);
let playback_start = Instant::now();
let mut event_idx = 0;
let mut stats_idx = 0;
loop {
let elapsed_ms = (playback_start.elapsed().as_millis() as f64 * speed) as u64;
let virtual_time = base_time + elapsed_ms;
// Batch events up to virtual_time
let mut batch = Vec::new();
while event_idx < trace.events.len()
&& trace.events[event_idx].timestamp_ms <= virtual_time
{
batch.push(trace.events[event_idx].clone());
event_idx += 1;
}
if !batch.is_empty() {
let json = serde_json::to_string(&batch).unwrap();
if tx.send(format_sse("activity", &json)).is_err() {
return;
}
}
// Send stats snapshots up to virtual_time
while stats_idx < trace.stats_timeline.len()
&& trace.stats_timeline[stats_idx].timestamp_ms <= virtual_time
{
let json =
serde_json::to_string(&trace.stats_timeline[stats_idx].stats).unwrap();
if tx.send(format_sse("stats", &json)).is_err() {
return;
}
stats_idx += 1;
}
// Send progress
let total = trace.events.len() + trace.stats_timeline.len();
let done_count = event_idx + stats_idx;
let progress = if total > 0 {
done_count as f64 / total as f64
} else {
1.0
};
let progress_json = serde_json::json!({ "progress": progress });
if tx
.send(format_sse("replay_progress", &progress_json.to_string()))
.is_err()
{
return;
}
// Check if replay is complete
if event_idx >= trace.events.len()
&& stats_idx >= trace.stats_timeline.len()
{
let _ = tx.send(format_sse("done", "{}"));
let _ = tx.send(Vec::new()); // EOF
return;
}
thread::sleep(Duration::from_millis(50));
}
});
// Blocks until connection closes
let _ = request.respond(response);
}

View file

@ -0,0 +1,18 @@
use serde::{Deserialize, Serialize};
use swactor::stats::RuntimeStats;
use crate::layer::DashboardEvent;
/// Complete trace of a runtime execution, suitable for saving/loading.
#[derive(Debug, Serialize, Deserialize)]
pub struct RuntimeTrace {
pub events: Vec<DashboardEvent>,
pub stats_timeline: Vec<TimestampedStats>,
}
/// A stats snapshot with a wall-clock timestamp.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TimestampedStats {
pub timestamp_ms: u64,
pub stats: RuntimeStats,
}

View file

@ -10,7 +10,7 @@ use crate::actor::{ActorAddress, AnyActor, Ctx};
use crate::channel::Receiver; use crate::channel::Receiver;
use crate::config::RuntimeConfig; use crate::config::RuntimeConfig;
use crate::delivery::{AddressMap, Envelope, InboxRegistry, Placement, TickContext, WorkerId}; use crate::delivery::{AddressMap, Envelope, InboxRegistry, Placement, TickContext, WorkerId};
use crate::stats::WorkerStats; use crate::stats::{MailboxSnapshot, WorkerStats};
use crate::worker::Worker; use crate::worker::Worker;
@ -68,7 +68,8 @@ impl Env {
let tc_spawn = spawn_rx.new_sender(); let tc_spawn = spawn_rx.new_sender();
let stats = Arc::new(WorkerStats::new()); let stats = Arc::new(WorkerStats::new());
let worker = Worker::new(WorkerId(0), transfer_rx, spawn_rx, stats.clone()); let mbox_snap = Arc::new(std::sync::Mutex::new(MailboxSnapshot::new()));
let worker = Worker::new(WorkerId(0), transfer_rx, spawn_rx, stats.clone(), mbox_snap);
Self { Self {
worker, worker,
@ -385,7 +386,8 @@ fn run_loop_stops_on_shutdown() {
let spawn_tx = spawn_rx.new_sender(); let spawn_tx = spawn_rx.new_sender();
let stats = Arc::new(WorkerStats::new()); let stats = Arc::new(WorkerStats::new());
let mut worker = Worker::new(WorkerId(0), transfer_rx, spawn_rx, stats); let mbox_snap = Arc::new(std::sync::Mutex::new(MailboxSnapshot::new()));
let mut worker = Worker::new(WorkerId(0), transfer_rx, spawn_rx, stats, mbox_snap);
let is_running = AtomicBool::new(false); let is_running = AtomicBool::new(false);
let address_map = AddressMap::new(); let address_map = AddressMap::new();

View file

@ -9,7 +9,7 @@ use crate::channel::{Receiver, Sender};
// Re-export config types so existing code using `runtime::RuntimeConfig` still works // Re-export config types so existing code using `runtime::RuntimeConfig` still works
pub use crate::config::{BackoffPolicy, RuntimeConfig}; pub use crate::config::{BackoffPolicy, RuntimeConfig};
use crate::delivery::{AddressMap, Envelope, InboxRegistry, Placement, TickContext, WorkerId}; use crate::delivery::{AddressMap, Envelope, InboxRegistry, Placement, TickContext, WorkerId};
use crate::stats::WorkerStats; use crate::stats::{ActorInfo, MailboxSnapshot, WorkerStats};
// Re-export stats types so existing code using `runtime::*` still works // Re-export stats types so existing code using `runtime::*` still works
pub use crate::stats::{RuntimeStats, WorkerInfo}; pub use crate::stats::{RuntimeStats, WorkerInfo};
use crate::worker::Worker; use crate::worker::Worker;
@ -66,6 +66,8 @@ pub struct Runtime {
placement: Placement, placement: Placement,
is_running: AtomicBool, is_running: AtomicBool,
worker_stats: Vec<Arc<WorkerStats>>, worker_stats: Vec<Arc<WorkerStats>>,
/// Per-worker mailbox snapshots, updated each tick by workers.
mailbox_snapshots: Vec<Arc<std::sync::Mutex<MailboxSnapshot>>>,
/// Workers available for tick(). run() drains this and moves workers to threads. /// Workers available for tick(). run() drains this and moves workers to threads.
tick_workers: RefCell<Vec<Worker>>, tick_workers: RefCell<Vec<Worker>>,
} }
@ -91,6 +93,7 @@ impl Runtime {
let mut transfer_txs = Vec::with_capacity(num_workers); let mut transfer_txs = Vec::with_capacity(num_workers);
let mut spawn_txs = Vec::with_capacity(num_workers); let mut spawn_txs = Vec::with_capacity(num_workers);
let mut worker_stats = Vec::with_capacity(num_workers); let mut worker_stats = Vec::with_capacity(num_workers);
let mut mailbox_snapshots = Vec::with_capacity(num_workers);
let mut workers = Vec::with_capacity(num_workers); let mut workers = Vec::with_capacity(num_workers);
for i in 0..num_workers { for i in 0..num_workers {
@ -104,11 +107,13 @@ impl Runtime {
spawn_txs.push(spawn_tx); spawn_txs.push(spawn_tx);
let stats = Arc::new(WorkerStats::new()); let stats = Arc::new(WorkerStats::new());
let mbox_snap = Arc::new(std::sync::Mutex::new(MailboxSnapshot::new()));
worker_stats.push(stats.clone()); worker_stats.push(stats.clone());
workers.push(Worker::new(WorkerId(i), transfer_rx, spawn_rx, stats)); mailbox_snapshots.push(mbox_snap.clone());
workers.push(Worker::new(WorkerId(i), transfer_rx, spawn_rx, stats, mbox_snap));
} }
Self { let rt = Self {
config, config,
address_map, address_map,
inbox_registry, inbox_registry,
@ -117,8 +122,18 @@ impl Runtime {
placement, placement,
is_running: AtomicBool::new(false), is_running: AtomicBool::new(false),
worker_stats, worker_stats,
mailbox_snapshots,
tick_workers: RefCell::new(workers), tick_workers: RefCell::new(workers),
} };
#[cfg(feature = "tracing")]
tracing::info!(
num_workers,
max_actors = rt.config.max_actors,
"runtime.created"
);
rt
} }
/// Spawn an actor, returns its address /// Spawn an actor, returns its address
@ -130,12 +145,24 @@ impl Runtime {
self.spawn_txs[worker_id.as_usize()] self.spawn_txs[worker_id.as_usize()]
.try_send((addr, boxed)) .try_send((addr, boxed))
.map_err(|_| Error::from("Runtime error: spawn queue full"))?; .map_err(|_| Error::from("Runtime error: spawn queue full"))?;
#[cfg(feature = "tracing")]
tracing::info!(
actor_addr = %addr,
worker_id = worker_id.as_usize(),
"actor.spawned"
);
Ok(addr) Ok(addr)
} }
/// Send a message to an actor address /// Send a message to an actor address
pub fn send_to<M: Message>(&self, addr: ActorAddress, msg: M) -> Result<(), Error> { pub fn send_to<M: Message>(&self, addr: ActorAddress, msg: M) -> Result<(), Error> {
let msg_box: Box<dyn Any + Send> = Box::new(msg); let msg_box: Box<dyn Any + Send> = Box::new(msg);
#[cfg(feature = "tracing")]
tracing::trace!(dest = %addr, "message.sent");
match self.address_map.lookup(&addr) { match self.address_map.lookup(&addr) {
Some(wid) => self.transfer_txs[wid.as_usize()] Some(wid) => self.transfer_txs[wid.as_usize()]
.try_send(Envelope::new(addr, msg_box)) .try_send(Envelope::new(addr, msg_box))
@ -185,6 +212,9 @@ impl Runtime {
pub fn run(self) -> Result<RuntimeHandle, Error> { pub fn run(self) -> Result<RuntimeHandle, Error> {
self.is_running.store(true, Ordering::Release); self.is_running.store(true, Ordering::Release);
#[cfg(feature = "tracing")]
tracing::info!(num_workers = self.config.num_threads.max(1), "runtime.started");
let workers: Vec<Worker> = self.tick_workers.replace(Vec::new()); let workers: Vec<Worker> = self.tick_workers.replace(Vec::new());
let rt = Arc::new(self); let rt = Arc::new(self);
@ -232,6 +262,11 @@ impl Runtime {
num_actors: ws.num_actors.load(Ordering::Relaxed), num_actors: ws.num_actors.load(Ordering::Relaxed),
mailbox_depth: ws.total_mailbox_depth.load(Ordering::Relaxed), mailbox_depth: ws.total_mailbox_depth.load(Ordering::Relaxed),
messages_processed: ws.messages_processed.load(Ordering::Relaxed), messages_processed: ws.messages_processed.load(Ordering::Relaxed),
local_sends: ws.local_sends.load(Ordering::Relaxed),
cross_sends: ws.cross_sends.load(Ordering::Relaxed),
inbox_sends: ws.inbox_sends.load(Ordering::Relaxed),
type_mismatches: ws.type_mismatches.load(Ordering::Relaxed),
panics: ws.panics.load(Ordering::Relaxed),
}) })
.collect(); .collect();
let actors = self let actors = self
@ -240,15 +275,41 @@ impl Runtime {
.into_iter() .into_iter()
.map(|(addr, wid)| (addr, wid.as_usize())) .map(|(addr, wid)| (addr, wid.as_usize()))
.collect(); .collect();
// Collect per-actor mailbox depth details
let mut actor_details = Vec::new();
for (wid, snap_lock) in self.mailbox_snapshots.iter().enumerate() {
let snap = snap_lock.lock().unwrap();
for &(addr, depth) in &snap.depths {
actor_details.push(ActorInfo {
address: addr,
worker_id: wid,
mailbox_depth: depth,
});
}
}
// Drain tick timings from each worker
let tick_timings = self
.worker_stats
.iter()
.map(|ws| ws.drain_tick_timings())
.collect();
RuntimeStats { RuntimeStats {
num_workers, num_workers,
actors, actors,
workers, workers,
actor_details,
tick_timings,
} }
} }
/// Signal all workers to stop /// Signal all workers to stop
pub fn shutdown(&self) { pub fn shutdown(&self) {
#[cfg(feature = "tracing")]
tracing::info!("runtime.shutdown");
self.is_running.store(false, Ordering::Release); self.is_running.store(false, Ordering::Release);
} }
} }

View file

@ -2,11 +2,32 @@ use std::sync::atomic::{AtomicU64, AtomicUsize};
use crate::actor::ActorAddress; use crate::actor::ActorAddress;
/// Timing data for one tick_once invocation.
#[derive(Debug, Clone, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct TickTiming {
/// Microseconds spent in each of the 6 phases.
pub phase_us: [u64; 6],
/// Total messages processed this tick.
pub messages_processed: usize,
/// Whether any work was done this tick.
pub did_work: bool,
}
/// Per-worker stats published via atomics. Readable from any thread. /// Per-worker stats published via atomics. Readable from any thread.
pub struct WorkerStats { pub struct WorkerStats {
pub num_actors: AtomicUsize, pub num_actors: AtomicUsize,
pub total_mailbox_depth: AtomicUsize, pub total_mailbox_depth: AtomicUsize,
pub messages_processed: AtomicU64, pub messages_processed: AtomicU64,
// Message routing counters
pub local_sends: AtomicU64,
pub cross_sends: AtomicU64,
pub inbox_sends: AtomicU64,
// Error counters
pub type_mismatches: AtomicU64,
pub panics: AtomicU64,
// Tick timing ring buffer (last N ticks)
tick_timings: std::sync::Mutex<RingBuffer<TickTiming>>,
} }
impl WorkerStats { impl WorkerStats {
@ -15,22 +36,97 @@ impl WorkerStats {
num_actors: AtomicUsize::new(0), num_actors: AtomicUsize::new(0),
total_mailbox_depth: AtomicUsize::new(0), total_mailbox_depth: AtomicUsize::new(0),
messages_processed: AtomicU64::new(0), messages_processed: AtomicU64::new(0),
local_sends: AtomicU64::new(0),
cross_sends: AtomicU64::new(0),
inbox_sends: AtomicU64::new(0),
type_mismatches: AtomicU64::new(0),
panics: AtomicU64::new(0),
tick_timings: std::sync::Mutex::new(RingBuffer::new(1024)),
} }
} }
pub fn push_tick_timing(&self, timing: TickTiming) {
self.tick_timings.lock().unwrap().push(timing);
}
/// Returns a snapshot of recent tick timings (drains the buffer).
pub fn drain_tick_timings(&self) -> Vec<TickTiming> {
self.tick_timings.lock().unwrap().drain()
}
} }
/// Snapshot of per-worker state. /// Snapshot of per-worker state.
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct WorkerInfo { pub struct WorkerInfo {
pub id: usize, pub id: usize,
pub num_actors: usize, pub num_actors: usize,
pub mailbox_depth: usize, pub mailbox_depth: usize,
pub messages_processed: u64, pub messages_processed: u64,
pub local_sends: u64,
pub cross_sends: u64,
pub inbox_sends: u64,
pub type_mismatches: u64,
pub panics: u64,
}
/// Per-actor info for the dashboard.
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct ActorInfo {
pub address: ActorAddress,
pub worker_id: usize,
pub mailbox_depth: usize,
} }
/// Snapshot of overall runtime state. /// Snapshot of overall runtime state.
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct RuntimeStats { pub struct RuntimeStats {
pub num_workers: usize, pub num_workers: usize,
/// Each entry is (address, worker_id). /// Each entry is (address, worker_id).
pub actors: Vec<(ActorAddress, usize)>, pub actors: Vec<(ActorAddress, usize)>,
pub workers: Vec<WorkerInfo>, pub workers: Vec<WorkerInfo>,
/// Per-actor detail including mailbox depth.
pub actor_details: Vec<ActorInfo>,
/// Recent tick timings per worker (index = worker id).
pub tick_timings: Vec<Vec<TickTiming>>,
}
/// Simple ring buffer for storing recent values.
pub(crate) struct RingBuffer<T> {
buf: Vec<T>,
capacity: usize,
}
impl<T> RingBuffer<T> {
pub fn new(capacity: usize) -> Self {
Self {
buf: Vec::with_capacity(capacity),
capacity,
}
}
pub fn push(&mut self, value: T) {
if self.buf.len() >= self.capacity {
self.buf.remove(0);
}
self.buf.push(value);
}
/// Drain all items, returning them and leaving the buffer empty.
pub fn drain(&mut self) -> Vec<T> {
std::mem::take(&mut self.buf)
}
}
/// Per-actor mailbox depth snapshot, collected by workers.
pub(crate) struct MailboxSnapshot {
pub depths: Vec<(ActorAddress, usize)>,
}
impl MailboxSnapshot {
pub fn new() -> Self {
Self { depths: Vec::new() }
}
} }

View file

@ -4,11 +4,12 @@ use std::collections::{HashMap, VecDeque};
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc; use std::sync::Arc;
use std::thread; use std::thread;
use std::time::Instant;
use crate::actor::{ActorAddress, AnyActor, ContextInner, Ctx}; use crate::actor::{ActorAddress, AnyActor, ContextInner, Ctx};
use crate::channel::Receiver; use crate::channel::Receiver;
use crate::delivery::{Envelope, TickContext, WorkerId}; use crate::delivery::{Envelope, TickContext, WorkerId};
use crate::stats::WorkerStats; use crate::stats::{MailboxSnapshot, TickTiming, WorkerStats};
use crate::Error; use crate::Error;
/// A worker owns a set of actors and runs them in a loop. /// A worker owns a set of actors and runs them in a loop.
@ -18,6 +19,8 @@ pub(crate) struct Worker {
transfer_rx: Receiver<Envelope>, transfer_rx: Receiver<Envelope>,
spawn_rx: Receiver<(ActorAddress, Box<dyn AnyActor>)>, spawn_rx: Receiver<(ActorAddress, Box<dyn AnyActor>)>,
stats: Arc<WorkerStats>, stats: Arc<WorkerStats>,
/// Shared snapshot of per-actor mailbox depths, readable by Runtime::stats().
mailbox_snapshot: Arc<std::sync::Mutex<MailboxSnapshot>>,
} }
impl Worker { impl Worker {
@ -26,6 +29,7 @@ impl Worker {
transfer_rx: Receiver<Envelope>, transfer_rx: Receiver<Envelope>,
spawn_rx: Receiver<(ActorAddress, Box<dyn AnyActor>)>, spawn_rx: Receiver<(ActorAddress, Box<dyn AnyActor>)>,
stats: Arc<WorkerStats>, stats: Arc<WorkerStats>,
mailbox_snapshot: Arc<std::sync::Mutex<MailboxSnapshot>>,
) -> Self { ) -> Self {
Self { Self {
id, id,
@ -33,18 +37,32 @@ impl Worker {
transfer_rx, transfer_rx,
spawn_rx, spawn_rx,
stats, stats,
mailbox_snapshot,
} }
} }
/// Run one iteration of the worker loop. Returns `true` if any work was done. /// Run one iteration of the worker loop. Returns `true` if any work was done.
pub(crate) fn tick_once(&mut self, tc: &TickContext) -> bool { pub(crate) fn tick_once(&mut self, tc: &TickContext) -> bool {
#[cfg(feature = "tracing")]
let _span = tracing::trace_span!("worker.tick", worker_id = self.id.0).entered();
let mut did_work = false; let mut did_work = false;
let t0 = Instant::now();
// 1. Drain spawn queue → add actors to pool // 1. Drain spawn queue → add actors to pool
#[cfg(feature = "tracing")]
let mut spawn_count: usize = 0;
while let Some((addr, actor)) = self.spawn_rx.try_recv() { while let Some((addr, actor)) = self.spawn_rx.try_recv() {
self.pool.insert(addr, actor); self.pool.insert(addr, actor);
#[cfg(feature = "tracing")]
{ spawn_count += 1; }
did_work = true; did_work = true;
} }
#[cfg(feature = "tracing")]
if spawn_count > 0 {
tracing::debug!(worker_id = self.id.0, count = spawn_count, "worker.spawns_drained");
}
let t1 = Instant::now();
// 2. Drain transfer queue → deliver envelopes to actors // 2. Drain transfer queue → deliver envelopes to actors
while let Some(envelope) = self.transfer_rx.try_recv() { while let Some(envelope) = self.transfer_rx.try_recv() {
@ -53,6 +71,7 @@ impl Worker {
self.pool.deliver(&dest, payload); self.pool.deliver(&dest, payload);
did_work = true; did_work = true;
} }
let t2 = Instant::now();
// 3. Tick all actors with WorkerContext // 3. Tick all actors with WorkerContext
let pending_local: RefCell<Vec<(ActorAddress, Box<dyn Any + Send>)>> = let pending_local: RefCell<Vec<(ActorAddress, Box<dyn Any + Send>)>> =
@ -64,12 +83,23 @@ impl Worker {
worker_id: self.id, worker_id: self.id,
tc, tc,
pending_local: &pending_local, pending_local: &pending_local,
stats: &self.stats,
}; };
processed = self.pool.tick_all(&worker_ctx); processed = self.pool.tick_all(&worker_ctx, &self.stats);
if processed > 0 { if processed > 0 {
did_work = true; did_work = true;
} }
} }
let t3 = Instant::now();
#[cfg(feature = "tracing")]
if processed > 0 {
tracing::debug!(
worker_id = self.id.0,
messages_processed = processed,
"worker.tick_all"
);
}
// 4. Drain spawn queue again — actors spawned during step 3 // 4. Drain spawn queue again — actors spawned during step 3
// must be in the pool before pending_local delivery. // must be in the pool before pending_local delivery.
@ -77,6 +107,7 @@ impl Worker {
self.pool.insert(addr, actor); self.pool.insert(addr, actor);
did_work = true; did_work = true;
} }
let t4 = Instant::now();
// 5. Drain pending_local buffer → deliver to local actors // 5. Drain pending_local buffer → deliver to local actors
let pending = pending_local.into_inner(); let pending = pending_local.into_inner();
@ -86,16 +117,55 @@ impl Worker {
for (addr, msg) in pending { for (addr, msg) in pending {
self.pool.deliver(&addr, msg); self.pool.deliver(&addr, msg);
} }
let t5 = Instant::now();
// 6. Publish stats // 6. Publish stats
self.stats.num_actors.store(self.pool.len(), Ordering::Relaxed); self.stats.num_actors.store(self.pool.len(), Ordering::Relaxed);
self.stats.total_mailbox_depth.store(self.pool.total_mailbox_depth(), Ordering::Relaxed); self.stats.total_mailbox_depth.store(self.pool.total_mailbox_depth(), Ordering::Relaxed);
self.stats.messages_processed.fetch_add(processed as u64, Ordering::Relaxed); self.stats.messages_processed.fetch_add(processed as u64, Ordering::Relaxed);
// Publish per-actor mailbox depths
{
let depths: Vec<(ActorAddress, usize)> = self.pool.mailbox_depths();
let mut snap = self.mailbox_snapshot.lock().unwrap();
snap.depths = depths;
}
let t6 = Instant::now();
// Record tick timing
let timing = TickTiming {
phase_us: [
t1.duration_since(t0).as_micros() as u64,
t2.duration_since(t1).as_micros() as u64,
t3.duration_since(t2).as_micros() as u64,
t4.duration_since(t3).as_micros() as u64,
t5.duration_since(t4).as_micros() as u64,
t6.duration_since(t5).as_micros() as u64,
],
messages_processed: processed,
did_work,
};
self.stats.push_tick_timing(timing);
#[cfg(feature = "tracing")]
if did_work {
tracing::debug!(
worker_id = self.id.0,
num_actors = self.pool.len(),
mailbox_depth = self.pool.total_mailbox_depth(),
messages_processed = processed,
"worker.stats"
);
}
did_work did_work
} }
pub(crate) fn run(&mut self, tc: &TickContext, is_running: &AtomicBool) { pub(crate) fn run(&mut self, tc: &TickContext, is_running: &AtomicBool) {
#[cfg(feature = "tracing")]
let _span = tracing::info_span!("worker.run", worker_id = self.id.0).entered();
let backoff = &tc.config.backoff_policy; let backoff = &tc.config.backoff_policy;
let mut idle_count: u32 = 0; let mut idle_count: u32 = 0;
while is_running.load(Ordering::Acquire) { while is_running.load(Ordering::Acquire) {
@ -128,6 +198,7 @@ struct WorkerContext<'a> {
worker_id: WorkerId, worker_id: WorkerId,
tc: &'a TickContext<'a>, tc: &'a TickContext<'a>,
pending_local: &'a RefCell<Vec<(ActorAddress, Box<dyn Any + Send>)>>, pending_local: &'a RefCell<Vec<(ActorAddress, Box<dyn Any + Send>)>>,
stats: &'a WorkerStats,
} }
impl ContextInner for WorkerContext<'_> { impl ContextInner for WorkerContext<'_> {
@ -135,17 +206,20 @@ impl ContextInner for WorkerContext<'_> {
match self.tc.address_map.lookup(&addr) { match self.tc.address_map.lookup(&addr) {
Some(wid) if wid == self.worker_id => { Some(wid) if wid == self.worker_id => {
// Same worker: buffer for local delivery (after current tick round) // Same worker: buffer for local delivery (after current tick round)
self.stats.local_sends.fetch_add(1, Ordering::Relaxed);
self.pending_local.borrow_mut().push((addr, msg)); self.pending_local.borrow_mut().push((addr, msg));
Ok(()) Ok(())
} }
Some(wid) => { Some(wid) => {
// Cross worker: envelope through transfer queue // Cross worker: envelope through transfer queue
self.stats.cross_sends.fetch_add(1, Ordering::Relaxed);
let envelope = Envelope::new(addr, msg); let envelope = Envelope::new(addr, msg);
let _ = self.tc.transfer_txs[wid.as_usize()].try_send(envelope); let _ = self.tc.transfer_txs[wid.as_usize()].try_send(envelope);
Ok(()) Ok(())
} }
None => { None => {
// Try inbox registry (external inboxes) // Try inbox registry (external inboxes)
self.stats.inbox_sends.fetch_add(1, Ordering::Relaxed);
self.tc.inbox_registry.try_deliver(addr, msg) self.tc.inbox_registry.try_deliver(addr, msg)
} }
} }
@ -201,7 +275,7 @@ impl ActorPool {
} }
/// Tick all actors in the pool. Returns the number of messages processed. /// Tick all actors in the pool. Returns the number of messages processed.
pub fn tick_all(&mut self, inner: &dyn ContextInner) -> usize { pub fn tick_all(&mut self, inner: &dyn ContextInner, stats: &WorkerStats) -> usize {
let mut count = 0; let mut count = 0;
for (&addr, slot) in self.actors.iter_mut() { for (&addr, slot) in self.actors.iter_mut() {
let ctx = Ctx::new(inner, addr); let ctx = Ctx::new(inner, addr);
@ -210,7 +284,10 @@ impl ActorPool {
slot.actor.handle_any(&ctx, msg); slot.actor.handle_any(&ctx, msg);
})); }));
if result.is_err() { if result.is_err() {
stats.panics.fetch_add(1, Ordering::Relaxed);
eprintln!("swactor: actor {addr} panicked in handler"); eprintln!("swactor: actor {addr} panicked in handler");
#[cfg(feature = "tracing")]
tracing::error!(actor_addr = %addr, "actor.panicked");
} }
count += 1; count += 1;
} }
@ -225,7 +302,9 @@ impl ActorPool {
pub fn total_mailbox_depth(&self) -> usize { pub fn total_mailbox_depth(&self) -> usize {
self.actors.values().map(|slot| slot.mailbox.len()).sum() self.actors.values().map(|slot| slot.mailbox.len()).sum()
} }
/// Returns per-actor mailbox depths for dashboard reporting.
pub fn mailbox_depths(&self) -> Vec<(ActorAddress, usize)> {
self.actors.iter().map(|(&addr, slot)| (addr, slot.mailbox.len())).collect()
}
} }

View file

@ -1,5 +1,5 @@
{ {
"name": "workspace", "name": "tools",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {