agy-superpowers 5.1.4 → 5.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/template/agent/rules/debug-confirmation-policy.md +34 -0
- package/template/agent/rules/language-matching.md +32 -0
- package/template/agent/skills/rust-developer/SKILL.md +281 -0
- package/template/agent/skills/rust-developer/references/rust-rules/_sections.md +231 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-clone-excessive.md +124 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-collect-intermediate.md +131 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-empty-catch.md +132 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-expect-lazy.md +95 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-format-hot-path.md +141 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-index-over-iter.md +125 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-lock-across-await.md +127 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-over-abstraction.md +120 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-panic-expected.md +131 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-premature-optimize.md +156 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-string-for-str.md +122 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-stringly-typed.md +167 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-type-erasure.md +134 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-unwrap-abuse.md +143 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-vec-for-slice.md +121 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-builder-must-use.md +143 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-builder-pattern.md +187 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-common-traits.md +165 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-default-impl.md +177 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-extension-trait.md +163 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-from-not-into.md +146 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-impl-asref.md +142 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-impl-into.md +160 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-must-use.md +125 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-newtype-safety.md +162 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-non-exhaustive.md +177 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-parse-dont-validate.md +184 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-sealed-trait.md +168 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-serde-optional.md +182 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-typestate.md +199 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-bounded-channel.md +175 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-broadcast-pubsub.md +185 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-cancellation-token.md +203 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-clone-before-await.md +171 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-join-parallel.md +158 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-joinset-structured.md +195 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-mpsc-queue.md +171 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-no-lock-await.md +156 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-oneshot-response.md +191 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-select-racing.md +198 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-spawn-blocking.md +154 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-tokio-fs.md +167 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-tokio-runtime.md +169 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-try-join.md +172 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-watch-latest.md +189 -0
- package/template/agent/skills/rust-developer/references/rust-rules/doc-all-public.md +113 -0
- package/template/agent/skills/rust-developer/references/rust-rules/doc-cargo-metadata.md +147 -0
- package/template/agent/skills/rust-developer/references/rust-rules/doc-errors-section.md +122 -0
- package/template/agent/skills/rust-developer/references/rust-rules/doc-examples-section.md +161 -0
- package/template/agent/skills/rust-developer/references/rust-rules/doc-hidden-setup.md +149 -0
- package/template/agent/skills/rust-developer/references/rust-rules/doc-intra-links.md +138 -0
- package/template/agent/skills/rust-developer/references/rust-rules/doc-link-types.md +169 -0
- package/template/agent/skills/rust-developer/references/rust-rules/doc-module-inner.md +116 -0
- package/template/agent/skills/rust-developer/references/rust-rules/doc-panics-section.md +128 -0
- package/template/agent/skills/rust-developer/references/rust-rules/doc-question-mark.md +136 -0
- package/template/agent/skills/rust-developer/references/rust-rules/doc-safety-section.md +131 -0
- package/template/agent/skills/rust-developer/references/rust-rules/err-anyhow-app.md +179 -0
- package/template/agent/skills/rust-developer/references/rust-rules/err-context-chain.md +144 -0
- package/template/agent/skills/rust-developer/references/rust-rules/err-custom-type.md +152 -0
- package/template/agent/skills/rust-developer/references/rust-rules/err-doc-errors.md +145 -0
- package/template/agent/skills/rust-developer/references/rust-rules/err-expect-bugs-only.md +133 -0
- package/template/agent/skills/rust-developer/references/rust-rules/err-from-impl.md +152 -0
- package/template/agent/skills/rust-developer/references/rust-rules/err-lowercase-msg.md +124 -0
- package/template/agent/skills/rust-developer/references/rust-rules/err-no-unwrap-prod.md +115 -0
- package/template/agent/skills/rust-developer/references/rust-rules/err-question-mark.md +151 -0
- package/template/agent/skills/rust-developer/references/rust-rules/err-result-over-panic.md +130 -0
- package/template/agent/skills/rust-developer/references/rust-rules/err-source-chain.md +155 -0
- package/template/agent/skills/rust-developer/references/rust-rules/err-thiserror-lib.md +171 -0
- package/template/agent/skills/rust-developer/references/rust-rules/lint-cargo-metadata.md +138 -0
- package/template/agent/skills/rust-developer/references/rust-rules/lint-deny-correctness.md +107 -0
- package/template/agent/skills/rust-developer/references/rust-rules/lint-missing-docs.md +154 -0
- package/template/agent/skills/rust-developer/references/rust-rules/lint-pedantic-selective.md +118 -0
- package/template/agent/skills/rust-developer/references/rust-rules/lint-rustfmt-check.md +157 -0
- package/template/agent/skills/rust-developer/references/rust-rules/lint-unsafe-doc.md +133 -0
- package/template/agent/skills/rust-developer/references/rust-rules/lint-warn-complexity.md +131 -0
- package/template/agent/skills/rust-developer/references/rust-rules/lint-warn-perf.md +136 -0
- package/template/agent/skills/rust-developer/references/rust-rules/lint-warn-style.md +135 -0
- package/template/agent/skills/rust-developer/references/rust-rules/lint-warn-suspicious.md +122 -0
- package/template/agent/skills/rust-developer/references/rust-rules/lint-workspace-lints.md +172 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-arena-allocator.md +168 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-arrayvec.md +142 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-assert-type-size.md +168 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-avoid-format.md +147 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-box-large-variant.md +158 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-boxed-slice.md +139 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-clone-from.md +147 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-compact-string.md +149 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-reuse-collections.md +174 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-smaller-integers.md +159 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-smallvec.md +138 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-thinvec.md +142 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-with-capacity.md +156 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-write-over-format.md +172 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-zero-copy.md +164 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-acronym-word.md +99 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-as-free.md +104 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-consts-screaming.md +94 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-crate-no-rs.md +78 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-funcs-snake.md +76 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-into-ownership.md +123 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-is-has-bool.md +127 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-iter-convention.md +129 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-iter-method.md +131 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-iter-type-match.md +142 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-lifetime-short.md +86 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-no-get-prefix.md +154 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-to-expensive.md +118 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-type-param-single.md +92 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-types-camel.md +65 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-variants-camel.md +101 -0
- package/template/agent/skills/rust-developer/references/rust-rules/opt-bounds-check.md +161 -0
- package/template/agent/skills/rust-developer/references/rust-rules/opt-cache-friendly.md +187 -0
- package/template/agent/skills/rust-developer/references/rust-rules/opt-codegen-units.md +142 -0
- package/template/agent/skills/rust-developer/references/rust-rules/opt-cold-unlikely.md +152 -0
- package/template/agent/skills/rust-developer/references/rust-rules/opt-inline-always-rare.md +141 -0
- package/template/agent/skills/rust-developer/references/rust-rules/opt-inline-never-cold.md +181 -0
- package/template/agent/skills/rust-developer/references/rust-rules/opt-inline-small.md +160 -0
- package/template/agent/skills/rust-developer/references/rust-rules/opt-likely-hint.md +171 -0
- package/template/agent/skills/rust-developer/references/rust-rules/opt-lto-release.md +130 -0
- package/template/agent/skills/rust-developer/references/rust-rules/opt-pgo-profile.md +167 -0
- package/template/agent/skills/rust-developer/references/rust-rules/opt-simd-portable.md +144 -0
- package/template/agent/skills/rust-developer/references/rust-rules/opt-target-cpu.md +154 -0
- package/template/agent/skills/rust-developer/references/rust-rules/own-arc-shared.md +141 -0
- package/template/agent/skills/rust-developer/references/rust-rules/own-borrow-over-clone.md +95 -0
- package/template/agent/skills/rust-developer/references/rust-rules/own-clone-explicit.md +135 -0
- package/template/agent/skills/rust-developer/references/rust-rules/own-copy-small.md +124 -0
- package/template/agent/skills/rust-developer/references/rust-rules/own-cow-conditional.md +135 -0
- package/template/agent/skills/rust-developer/references/rust-rules/own-lifetime-elision.md +134 -0
- package/template/agent/skills/rust-developer/references/rust-rules/own-move-large.md +134 -0
- package/template/agent/skills/rust-developer/references/rust-rules/own-mutex-interior.md +105 -0
- package/template/agent/skills/rust-developer/references/rust-rules/own-rc-single-thread.md +65 -0
- package/template/agent/skills/rust-developer/references/rust-rules/own-refcell-interior.md +97 -0
- package/template/agent/skills/rust-developer/references/rust-rules/own-rwlock-readers.md +122 -0
- package/template/agent/skills/rust-developer/references/rust-rules/own-slice-over-vec.md +119 -0
- package/template/agent/skills/rust-developer/references/rust-rules/perf-black-box-bench.md +153 -0
- package/template/agent/skills/rust-developer/references/rust-rules/perf-chain-avoid.md +136 -0
- package/template/agent/skills/rust-developer/references/rust-rules/perf-collect-into.md +133 -0
- package/template/agent/skills/rust-developer/references/rust-rules/perf-collect-once.md +120 -0
- package/template/agent/skills/rust-developer/references/rust-rules/perf-drain-reuse.md +137 -0
- package/template/agent/skills/rust-developer/references/rust-rules/perf-entry-api.md +134 -0
- package/template/agent/skills/rust-developer/references/rust-rules/perf-extend-batch.md +150 -0
- package/template/agent/skills/rust-developer/references/rust-rules/perf-iter-lazy.md +123 -0
- package/template/agent/skills/rust-developer/references/rust-rules/perf-iter-over-index.md +113 -0
- package/template/agent/skills/rust-developer/references/rust-rules/perf-profile-first.md +175 -0
- package/template/agent/skills/rust-developer/references/rust-rules/perf-release-profile.md +149 -0
- package/template/agent/skills/rust-developer/references/rust-rules/proj-bin-dir.md +142 -0
- package/template/agent/skills/rust-developer/references/rust-rules/proj-flat-small.md +133 -0
- package/template/agent/skills/rust-developer/references/rust-rules/proj-lib-main-split.md +148 -0
- package/template/agent/skills/rust-developer/references/rust-rules/proj-mod-by-feature.md +130 -0
- package/template/agent/skills/rust-developer/references/rust-rules/proj-mod-rs-dir.md +120 -0
- package/template/agent/skills/rust-developer/references/rust-rules/proj-prelude-module.md +155 -0
- package/template/agent/skills/rust-developer/references/rust-rules/proj-pub-crate-internal.md +139 -0
- package/template/agent/skills/rust-developer/references/rust-rules/proj-pub-super-parent.md +135 -0
- package/template/agent/skills/rust-developer/references/rust-rules/proj-pub-use-reexport.md +162 -0
- package/template/agent/skills/rust-developer/references/rust-rules/proj-workspace-deps.md +186 -0
- package/template/agent/skills/rust-developer/references/rust-rules/proj-workspace-large.md +162 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-arrange-act-assert.md +160 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-cfg-test-module.md +151 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-criterion-bench.md +171 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-descriptive-names.md +142 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-doctest-examples.md +168 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-fixture-raii.md +151 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-integration-dir.md +144 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-mock-traits.md +189 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-mockall-mocking.md +226 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-proptest-properties.md +161 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-should-panic.md +130 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-tokio-async.md +154 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-use-super.md +127 -0
- package/template/agent/skills/rust-developer/references/rust-rules/type-enum-states.md +154 -0
- package/template/agent/skills/rust-developer/references/rust-rules/type-generic-bounds.md +142 -0
- package/template/agent/skills/rust-developer/references/rust-rules/type-never-diverge.md +146 -0
- package/template/agent/skills/rust-developer/references/rust-rules/type-newtype-ids.md +160 -0
- package/template/agent/skills/rust-developer/references/rust-rules/type-newtype-validated.md +159 -0
- package/template/agent/skills/rust-developer/references/rust-rules/type-no-stringly.md +144 -0
- package/template/agent/skills/rust-developer/references/rust-rules/type-option-nullable.md +137 -0
- package/template/agent/skills/rust-developer/references/rust-rules/type-phantom-marker.md +188 -0
- package/template/agent/skills/rust-developer/references/rust-rules/type-repr-transparent.md +143 -0
- package/template/agent/skills/rust-developer/references/rust-rules/type-result-fallible.md +131 -0
- package/template/agent/skills/systematic-debugging/SKILL.md +17 -0
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# async-bounded-channel
|
|
2
|
+
|
|
3
|
+
> Use bounded channels to apply backpressure and prevent unbounded memory growth
|
|
4
|
+
|
|
5
|
+
## Why It Matters
|
|
6
|
+
|
|
7
|
+
Unbounded channels grow without limit when producers outpace consumers. In production, this leads to memory exhaustion. Bounded channels apply backpressure—producers wait when the channel is full, naturally throttling the system. This prevents OOM and makes resource usage predictable.
|
|
8
|
+
|
|
9
|
+
## Bad
|
|
10
|
+
|
|
11
|
+
```rust
|
|
12
|
+
use tokio::sync::mpsc;
|
|
13
|
+
|
|
14
|
+
// Unbounded channel - can grow forever
|
|
15
|
+
let (tx, mut rx) = mpsc::unbounded_channel::<Message>();
|
|
16
|
+
|
|
17
|
+
// Fast producer, slow consumer = unbounded memory growth
|
|
18
|
+
tokio::spawn(async move {
|
|
19
|
+
loop {
|
|
20
|
+
let msg = generate_message();
|
|
21
|
+
tx.send(msg).unwrap(); // Never blocks, never fails (until OOM)
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
tokio::spawn(async move {
|
|
26
|
+
while let Some(msg) = rx.recv().await {
|
|
27
|
+
slow_process(msg).await; // Can't keep up
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
// Memory grows unboundedly until crash
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Good
|
|
34
|
+
|
|
35
|
+
```rust
|
|
36
|
+
use tokio::sync::mpsc;
|
|
37
|
+
|
|
38
|
+
// Bounded channel - backpressure when full
|
|
39
|
+
let (tx, mut rx) = mpsc::channel::<Message>(100); // Max 100 items
|
|
40
|
+
|
|
41
|
+
// Producer waits when channel full
|
|
42
|
+
tokio::spawn(async move {
|
|
43
|
+
loop {
|
|
44
|
+
let msg = generate_message();
|
|
45
|
+
// Blocks if channel is full - natural backpressure
|
|
46
|
+
tx.send(msg).await.unwrap();
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
tokio::spawn(async move {
|
|
51
|
+
while let Some(msg) = rx.recv().await {
|
|
52
|
+
slow_process(msg).await;
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
// Memory usage capped at ~100 messages
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Choosing Buffer Size
|
|
59
|
+
|
|
60
|
+
```rust
|
|
61
|
+
// Too small: frequent blocking, reduced throughput
|
|
62
|
+
let (tx, rx) = mpsc::channel::<Item>(1);
|
|
63
|
+
|
|
64
|
+
// Too large: delayed backpressure, memory waste
|
|
65
|
+
let (tx, rx) = mpsc::channel::<Item>(1_000_000);
|
|
66
|
+
|
|
67
|
+
// Guidelines:
|
|
68
|
+
// - Start with expected burst size
|
|
69
|
+
// - Measure actual usage in production
|
|
70
|
+
// - Err on the smaller side initially
|
|
71
|
+
|
|
72
|
+
// Small items, high throughput
|
|
73
|
+
let (tx, rx) = mpsc::channel::<u64>(1000);
|
|
74
|
+
|
|
75
|
+
// Large items, moderate throughput
|
|
76
|
+
let (tx, rx) = mpsc::channel::<LargeStruct>(100);
|
|
77
|
+
|
|
78
|
+
// Low latency requirement
|
|
79
|
+
let (tx, rx) = mpsc::channel::<Command>(10);
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Handling Full Channel
|
|
83
|
+
|
|
84
|
+
```rust
|
|
85
|
+
use tokio::sync::mpsc;
|
|
86
|
+
use tokio::time::{timeout, Duration};
|
|
87
|
+
|
|
88
|
+
let (tx, mut rx) = mpsc::channel::<Message>(100);
|
|
89
|
+
|
|
90
|
+
// Option 1: Wait indefinitely (default)
|
|
91
|
+
tx.send(msg).await?;
|
|
92
|
+
|
|
93
|
+
// Option 2: Try send, fail if full
|
|
94
|
+
match tx.try_send(msg) {
|
|
95
|
+
Ok(()) => println!("Sent"),
|
|
96
|
+
Err(TrySendError::Full(msg)) => {
|
|
97
|
+
println!("Channel full, dropping message");
|
|
98
|
+
}
|
|
99
|
+
Err(TrySendError::Closed(msg)) => {
|
|
100
|
+
println!("Receiver dropped");
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Option 3: Timeout
|
|
105
|
+
match timeout(Duration::from_secs(1), tx.send(msg)).await {
|
|
106
|
+
Ok(Ok(())) => println!("Sent"),
|
|
107
|
+
Ok(Err(_)) => println!("Channel closed"),
|
|
108
|
+
Err(_) => println!("Timeout - channel full for too long"),
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Option 4: send with permit reservation
|
|
112
|
+
let permit = tx.reserve().await?;
|
|
113
|
+
permit.send(msg); // Guaranteed to succeed
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Channel Types
|
|
117
|
+
|
|
118
|
+
```rust
|
|
119
|
+
// mpsc: many producers, single consumer
|
|
120
|
+
let (tx, rx) = mpsc::channel::<Message>(100);
|
|
121
|
+
let tx2 = tx.clone(); // Can clone sender
|
|
122
|
+
|
|
123
|
+
// oneshot: single value, one producer, one consumer
|
|
124
|
+
let (tx, rx) = oneshot::channel::<Response>();
|
|
125
|
+
tx.send(response); // Can only send once
|
|
126
|
+
|
|
127
|
+
// broadcast: multiple consumers, each gets all messages
|
|
128
|
+
let (tx, _) = broadcast::channel::<Event>(100);
|
|
129
|
+
let mut rx1 = tx.subscribe();
|
|
130
|
+
let mut rx2 = tx.subscribe();
|
|
131
|
+
|
|
132
|
+
// watch: single latest value, multiple consumers
|
|
133
|
+
let (tx, rx) = watch::channel::<State>(initial);
|
|
134
|
+
// Receivers see latest value, not all values
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Worker Pool Pattern
|
|
138
|
+
|
|
139
|
+
```rust
|
|
140
|
+
async fn process_with_workers(items: Vec<Item>) -> Vec<Result> {
|
|
141
|
+
let (tx, rx) = mpsc::channel(100);
|
|
142
|
+
let rx = Arc::new(Mutex::new(rx));
|
|
143
|
+
|
|
144
|
+
// Spawn worker pool
|
|
145
|
+
let workers: Vec<_> = (0..4).map(|_| {
|
|
146
|
+
let rx = rx.clone();
|
|
147
|
+
tokio::spawn(async move {
|
|
148
|
+
loop {
|
|
149
|
+
let item = {
|
|
150
|
+
let mut rx = rx.lock().await;
|
|
151
|
+
rx.recv().await
|
|
152
|
+
};
|
|
153
|
+
match item {
|
|
154
|
+
Some(item) => process(item).await,
|
|
155
|
+
None => break,
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
})
|
|
159
|
+
}).collect();
|
|
160
|
+
|
|
161
|
+
// Send items
|
|
162
|
+
for item in items {
|
|
163
|
+
tx.send(item).await.unwrap();
|
|
164
|
+
}
|
|
165
|
+
drop(tx); // Signal workers to stop
|
|
166
|
+
|
|
167
|
+
futures::future::join_all(workers).await;
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## See Also
|
|
172
|
+
|
|
173
|
+
- [async-mpsc-queue](./async-mpsc-queue.md) - Multi-producer patterns
|
|
174
|
+
- [async-oneshot-response](./async-oneshot-response.md) - Request-response pattern
|
|
175
|
+
- [async-watch-latest](./async-watch-latest.md) - Latest-value broadcasting
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
# async-broadcast-pubsub
|
|
2
|
+
|
|
3
|
+
> Use `broadcast` channel for pub/sub where all subscribers receive all messages
|
|
4
|
+
|
|
5
|
+
## Why It Matters
|
|
6
|
+
|
|
7
|
+
Unlike `mpsc` where one consumer receives each message, `broadcast` delivers each message to all subscribers. This is ideal for event broadcasting, real-time notifications, or when multiple components need to react to the same events independently.
|
|
8
|
+
|
|
9
|
+
## Bad
|
|
10
|
+
|
|
11
|
+
```rust
|
|
12
|
+
use tokio::sync::mpsc;
|
|
13
|
+
|
|
14
|
+
// mpsc only delivers to ONE consumer
|
|
15
|
+
let (tx, mut rx) = mpsc::channel::<Event>(100);
|
|
16
|
+
|
|
17
|
+
// Only one of these receives each message!
|
|
18
|
+
let mut rx2 = ???; // Can't clone receiver
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Good
|
|
22
|
+
|
|
23
|
+
```rust
|
|
24
|
+
use tokio::sync::broadcast;
|
|
25
|
+
|
|
26
|
+
// broadcast delivers to ALL subscribers
|
|
27
|
+
let (tx, _) = broadcast::channel::<Event>(100);
|
|
28
|
+
|
|
29
|
+
// Each subscriber gets ALL messages
|
|
30
|
+
let mut rx1 = tx.subscribe();
|
|
31
|
+
let mut rx2 = tx.subscribe();
|
|
32
|
+
|
|
33
|
+
tokio::spawn(async move {
|
|
34
|
+
while let Ok(event) = rx1.recv().await {
|
|
35
|
+
handle_in_logger(event);
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
tokio::spawn(async move {
|
|
40
|
+
while let Ok(event) = rx2.recv().await {
|
|
41
|
+
handle_in_metrics(event);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// Both subscribers receive this
|
|
46
|
+
tx.send(Event::UserLogin { user_id: 42 })?;
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Broadcast Semantics
|
|
50
|
+
|
|
51
|
+
```rust
|
|
52
|
+
use tokio::sync::broadcast;
|
|
53
|
+
|
|
54
|
+
let (tx, mut rx1) = broadcast::channel::<i32>(16);
|
|
55
|
+
let mut rx2 = tx.subscribe();
|
|
56
|
+
|
|
57
|
+
tx.send(1)?;
|
|
58
|
+
tx.send(2)?;
|
|
59
|
+
|
|
60
|
+
// Both receive all messages
|
|
61
|
+
assert_eq!(rx1.recv().await?, 1);
|
|
62
|
+
assert_eq!(rx1.recv().await?, 2);
|
|
63
|
+
assert_eq!(rx2.recv().await?, 1);
|
|
64
|
+
assert_eq!(rx2.recv().await?, 2);
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Handling Lagging Receivers
|
|
68
|
+
|
|
69
|
+
```rust
|
|
70
|
+
use tokio::sync::broadcast::{self, error::RecvError};
|
|
71
|
+
|
|
72
|
+
let (tx, mut rx) = broadcast::channel::<Event>(16);
|
|
73
|
+
|
|
74
|
+
loop {
|
|
75
|
+
match rx.recv().await {
|
|
76
|
+
Ok(event) => {
|
|
77
|
+
process(event);
|
|
78
|
+
}
|
|
79
|
+
Err(RecvError::Lagged(count)) => {
|
|
80
|
+
// Receiver couldn't keep up, missed `count` messages
|
|
81
|
+
log::warn!("Missed {} events", count);
|
|
82
|
+
// Continue receiving new messages
|
|
83
|
+
}
|
|
84
|
+
Err(RecvError::Closed) => {
|
|
85
|
+
break; // All senders dropped
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Event Bus Pattern
|
|
92
|
+
|
|
93
|
+
```rust
|
|
94
|
+
use tokio::sync::broadcast;
|
|
95
|
+
|
|
96
|
+
#[derive(Clone, Debug)]
|
|
97
|
+
enum AppEvent {
|
|
98
|
+
UserLoggedIn { user_id: u64 },
|
|
99
|
+
OrderCreated { order_id: u64 },
|
|
100
|
+
SystemShutdown,
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
struct EventBus {
|
|
104
|
+
tx: broadcast::Sender<AppEvent>,
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
impl EventBus {
|
|
108
|
+
fn new() -> Self {
|
|
109
|
+
let (tx, _) = broadcast::channel(1000);
|
|
110
|
+
EventBus { tx }
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
fn publish(&self, event: AppEvent) {
|
|
114
|
+
// Ignore error if no subscribers
|
|
115
|
+
let _ = self.tx.send(event);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
fn subscribe(&self) -> broadcast::Receiver<AppEvent> {
|
|
119
|
+
self.tx.subscribe()
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Usage
|
|
124
|
+
let bus = EventBus::new();
|
|
125
|
+
|
|
126
|
+
// Logger subscribes
|
|
127
|
+
let mut log_rx = bus.subscribe();
|
|
128
|
+
tokio::spawn(async move {
|
|
129
|
+
while let Ok(event) = log_rx.recv().await {
|
|
130
|
+
log::info!("Event: {:?}", event);
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// Metrics subscribes
|
|
135
|
+
let mut metrics_rx = bus.subscribe();
|
|
136
|
+
tokio::spawn(async move {
|
|
137
|
+
while let Ok(event) = metrics_rx.recv().await {
|
|
138
|
+
record_metric(&event);
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
// Publish events
|
|
143
|
+
bus.publish(AppEvent::UserLoggedIn { user_id: 42 });
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Broadcast vs Watch
|
|
147
|
+
|
|
148
|
+
```rust
|
|
149
|
+
// broadcast: subscribers get ALL messages
|
|
150
|
+
// Good for: events, logs, notifications
|
|
151
|
+
let (tx, _) = broadcast::channel::<Event>(100);
|
|
152
|
+
|
|
153
|
+
// watch: subscribers get LATEST value only
|
|
154
|
+
// Good for: config changes, state updates
|
|
155
|
+
let (tx, _) = watch::channel(initial_state);
|
|
156
|
+
|
|
157
|
+
// If subscriber is slow:
|
|
158
|
+
// - broadcast: they receive old messages (or lag)
|
|
159
|
+
// - watch: they skip to latest (no history)
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## Clone Requirement
|
|
163
|
+
|
|
164
|
+
```rust
|
|
165
|
+
// broadcast requires Clone because message is cloned to each receiver
|
|
166
|
+
use tokio::sync::broadcast;
|
|
167
|
+
|
|
168
|
+
#[derive(Clone)] // Required for broadcast
|
|
169
|
+
struct Event {
|
|
170
|
+
data: String,
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
let (tx, _) = broadcast::channel::<Event>(100);
|
|
174
|
+
|
|
175
|
+
// For non-Clone types, wrap in Arc
|
|
176
|
+
use std::sync::Arc;
|
|
177
|
+
|
|
178
|
+
let (tx, _) = broadcast::channel::<Arc<LargeNonClone>>(100);
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
## See Also
|
|
182
|
+
|
|
183
|
+
- [async-mpsc-queue](./async-mpsc-queue.md) - Single-consumer channels
|
|
184
|
+
- [async-watch-latest](./async-watch-latest.md) - Latest-value only
|
|
185
|
+
- [async-bounded-channel](./async-bounded-channel.md) - Buffer sizing
|
package/template/agent/skills/rust-developer/references/rust-rules/async-cancellation-token.md
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
# async-cancellation-token
|
|
2
|
+
|
|
3
|
+
> Use `CancellationToken` for graceful shutdown and task cancellation
|
|
4
|
+
|
|
5
|
+
## Why It Matters
|
|
6
|
+
|
|
7
|
+
Dropping a `JoinHandle` doesn't cancel the task—it just detaches it. For graceful shutdown, you need explicit cancellation. `tokio_util::sync::CancellationToken` provides a cooperative cancellation mechanism that tasks can check and respond to, enabling clean resource cleanup.
|
|
8
|
+
|
|
9
|
+
## Bad
|
|
10
|
+
|
|
11
|
+
```rust
|
|
12
|
+
// Dropping handle doesn't stop the task
|
|
13
|
+
let handle = tokio::spawn(async {
|
|
14
|
+
loop {
|
|
15
|
+
do_work().await;
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
drop(handle); // Task continues running in background!
|
|
20
|
+
|
|
21
|
+
// Using bool flag - not async-aware
|
|
22
|
+
let running = Arc::new(AtomicBool::new(true));
|
|
23
|
+
|
|
24
|
+
tokio::spawn({
|
|
25
|
+
let running = running.clone();
|
|
26
|
+
async move {
|
|
27
|
+
while running.load(Ordering::Relaxed) {
|
|
28
|
+
do_work().await; // Can't wake up if blocked here
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
running.store(false, Ordering::Relaxed);
|
|
34
|
+
// Task won't stop until current do_work() completes
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Good
|
|
38
|
+
|
|
39
|
+
```rust
|
|
40
|
+
use tokio_util::sync::CancellationToken;
|
|
41
|
+
|
|
42
|
+
let token = CancellationToken::new();
|
|
43
|
+
|
|
44
|
+
let handle = tokio::spawn({
|
|
45
|
+
let token = token.clone();
|
|
46
|
+
async move {
|
|
47
|
+
loop {
|
|
48
|
+
tokio::select! {
|
|
49
|
+
_ = token.cancelled() => {
|
|
50
|
+
println!("Shutting down gracefully");
|
|
51
|
+
cleanup().await;
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
_ = do_work() => {
|
|
55
|
+
// Work completed
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// Later: trigger cancellation
|
|
63
|
+
token.cancel();
|
|
64
|
+
handle.await?; // Task completes cleanly
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## CancellationToken API
|
|
68
|
+
|
|
69
|
+
```rust
|
|
70
|
+
use tokio_util::sync::CancellationToken;
|
|
71
|
+
|
|
72
|
+
// Create token
|
|
73
|
+
let token = CancellationToken::new();
|
|
74
|
+
|
|
75
|
+
// Clone for sharing (cheap Arc-based clone)
|
|
76
|
+
let token2 = token.clone();
|
|
77
|
+
|
|
78
|
+
// Check if cancelled (non-blocking)
|
|
79
|
+
if token.is_cancelled() {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Wait for cancellation (async)
|
|
84
|
+
token.cancelled().await;
|
|
85
|
+
|
|
86
|
+
// Trigger cancellation
|
|
87
|
+
token.cancel();
|
|
88
|
+
|
|
89
|
+
// Child tokens - cancelled when parent is cancelled
|
|
90
|
+
let child = token.child_token();
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Hierarchical Cancellation
|
|
94
|
+
|
|
95
|
+
```rust
|
|
96
|
+
async fn run_server(shutdown: CancellationToken) {
|
|
97
|
+
let listener = TcpListener::bind("0.0.0.0:8080").await?;
|
|
98
|
+
|
|
99
|
+
loop {
|
|
100
|
+
tokio::select! {
|
|
101
|
+
_ = shutdown.cancelled() => {
|
|
102
|
+
println!("Server shutting down");
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
result = listener.accept() => {
|
|
106
|
+
let (socket, _) = result?;
|
|
107
|
+
// Each connection gets child token
|
|
108
|
+
let conn_token = shutdown.child_token();
|
|
109
|
+
tokio::spawn(handle_connection(socket, conn_token));
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Child tokens auto-cancelled when we exit
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async fn handle_connection(socket: TcpStream, token: CancellationToken) {
|
|
118
|
+
loop {
|
|
119
|
+
tokio::select! {
|
|
120
|
+
_ = token.cancelled() => {
|
|
121
|
+
// Connection cleanup
|
|
122
|
+
break;
|
|
123
|
+
}
|
|
124
|
+
data = socket.read() => {
|
|
125
|
+
// Handle data
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Graceful Shutdown Pattern
|
|
133
|
+
|
|
134
|
+
```rust
|
|
135
|
+
use tokio::signal;
|
|
136
|
+
|
|
137
|
+
async fn main() -> Result<()> {
|
|
138
|
+
let shutdown = CancellationToken::new();
|
|
139
|
+
|
|
140
|
+
// Spawn signal handler
|
|
141
|
+
let shutdown_trigger = shutdown.clone();
|
|
142
|
+
tokio::spawn(async move {
|
|
143
|
+
signal::ctrl_c().await.expect("failed to listen for Ctrl+C");
|
|
144
|
+
println!("Received Ctrl+C, initiating shutdown...");
|
|
145
|
+
shutdown_trigger.cancel();
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
// Run application with shutdown token
|
|
149
|
+
run_app(shutdown).await
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
async fn run_app(shutdown: CancellationToken) -> Result<()> {
|
|
153
|
+
let mut tasks = JoinSet::new();
|
|
154
|
+
|
|
155
|
+
tasks.spawn(worker_task(shutdown.child_token()));
|
|
156
|
+
tasks.spawn(server_task(shutdown.child_token()));
|
|
157
|
+
|
|
158
|
+
// Wait for shutdown or task completion
|
|
159
|
+
tokio::select! {
|
|
160
|
+
_ = shutdown.cancelled() => {
|
|
161
|
+
println!("Shutdown requested, waiting for tasks...");
|
|
162
|
+
}
|
|
163
|
+
Some(result) = tasks.join_next() => {
|
|
164
|
+
// A task completed/failed
|
|
165
|
+
result??;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Wait for remaining tasks with timeout
|
|
170
|
+
tokio::time::timeout(
|
|
171
|
+
Duration::from_secs(30),
|
|
172
|
+
async { while tasks.join_next().await.is_some() {} }
|
|
173
|
+
).await.ok();
|
|
174
|
+
|
|
175
|
+
Ok(())
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## DropGuard Pattern
|
|
180
|
+
|
|
181
|
+
```rust
|
|
182
|
+
use tokio_util::sync::CancellationToken;
|
|
183
|
+
|
|
184
|
+
// Auto-cancel on drop
|
|
185
|
+
let token = CancellationToken::new();
|
|
186
|
+
let guard = token.clone().drop_guard();
|
|
187
|
+
|
|
188
|
+
tokio::spawn({
|
|
189
|
+
let token = token.clone();
|
|
190
|
+
async move {
|
|
191
|
+
token.cancelled().await;
|
|
192
|
+
println!("Cancelled!");
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
drop(guard); // Automatically calls token.cancel()
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## See Also
|
|
200
|
+
|
|
201
|
+
- [async-joinset-structured](./async-joinset-structured.md) - Managing multiple tasks
|
|
202
|
+
- [async-select-racing](./async-select-racing.md) - select! for cancellation
|
|
203
|
+
- [async-tokio-runtime](./async-tokio-runtime.md) - Runtime shutdown
|