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,156 @@
|
|
|
1
|
+
# async-no-lock-await
|
|
2
|
+
|
|
3
|
+
> Never hold `Mutex`/`RwLock` across `.await`
|
|
4
|
+
|
|
5
|
+
## Why It Matters
|
|
6
|
+
|
|
7
|
+
Holding a lock across an `.await` point can cause deadlocks and severely hurt performance. The task may be suspended while holding the lock, blocking all other tasks waiting for it - potentially indefinitely.
|
|
8
|
+
|
|
9
|
+
## Bad
|
|
10
|
+
|
|
11
|
+
```rust
|
|
12
|
+
use tokio::sync::Mutex;
|
|
13
|
+
|
|
14
|
+
async fn bad_update(state: &Mutex<State>) {
|
|
15
|
+
let mut guard = state.lock().await;
|
|
16
|
+
|
|
17
|
+
// BAD: Lock held across await!
|
|
18
|
+
let data = fetch_from_network().await;
|
|
19
|
+
|
|
20
|
+
guard.value = data;
|
|
21
|
+
} // Lock finally released
|
|
22
|
+
|
|
23
|
+
// This can deadlock or starve other tasks
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Good
|
|
27
|
+
|
|
28
|
+
```rust
|
|
29
|
+
use tokio::sync::Mutex;
|
|
30
|
+
|
|
31
|
+
async fn good_update(state: &Mutex<State>) {
|
|
32
|
+
// Fetch data BEFORE taking the lock
|
|
33
|
+
let data = fetch_from_network().await;
|
|
34
|
+
|
|
35
|
+
// Lock only for the quick update
|
|
36
|
+
let mut guard = state.lock().await;
|
|
37
|
+
guard.value = data;
|
|
38
|
+
} // Lock released immediately
|
|
39
|
+
|
|
40
|
+
// Alternative: Clone data out, process, then update
|
|
41
|
+
async fn good_update_v2(state: &Mutex<State>) {
|
|
42
|
+
// Extract what we need
|
|
43
|
+
let id = {
|
|
44
|
+
let guard = state.lock().await;
|
|
45
|
+
guard.id.clone()
|
|
46
|
+
}; // Lock released!
|
|
47
|
+
|
|
48
|
+
// Do async work without lock
|
|
49
|
+
let data = fetch_by_id(id).await;
|
|
50
|
+
|
|
51
|
+
// Quick update
|
|
52
|
+
state.lock().await.value = data;
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## The Problem Visualized
|
|
57
|
+
|
|
58
|
+
```rust
|
|
59
|
+
// Task A:
|
|
60
|
+
let guard = mutex.lock().await; // Acquires lock
|
|
61
|
+
expensive_io().await; // Suspended, still holding lock!
|
|
62
|
+
// ... many milliseconds pass ...
|
|
63
|
+
drop(guard); // Finally releases
|
|
64
|
+
|
|
65
|
+
// Task B, C, D:
|
|
66
|
+
let guard = mutex.lock().await; // All blocked waiting for A!
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Patterns for Extraction
|
|
70
|
+
|
|
71
|
+
```rust
|
|
72
|
+
use tokio::sync::Mutex;
|
|
73
|
+
|
|
74
|
+
// Pattern 1: Clone out, process, update
|
|
75
|
+
async fn pattern_clone(state: &Mutex<State>) {
|
|
76
|
+
let config = state.lock().await.config.clone();
|
|
77
|
+
let result = process_with_io(&config).await;
|
|
78
|
+
state.lock().await.result = result;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Pattern 2: Compute closure, apply
|
|
82
|
+
async fn pattern_closure(state: &Mutex<State>) {
|
|
83
|
+
let update = compute_update().await;
|
|
84
|
+
|
|
85
|
+
state.lock().await.apply(update);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Pattern 3: Message passing
|
|
89
|
+
async fn pattern_message(
|
|
90
|
+
state: &Mutex<State>,
|
|
91
|
+
tx: mpsc::Sender<Update>,
|
|
92
|
+
) {
|
|
93
|
+
let update = compute_update().await;
|
|
94
|
+
tx.send(update).await.unwrap();
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Separate task handles updates
|
|
98
|
+
async fn state_manager(
|
|
99
|
+
state: Arc<Mutex<State>>,
|
|
100
|
+
mut rx: mpsc::Receiver<Update>,
|
|
101
|
+
) {
|
|
102
|
+
while let Some(update) = rx.recv().await {
|
|
103
|
+
state.lock().await.apply(update);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Using RwLock
|
|
109
|
+
|
|
110
|
+
```rust
|
|
111
|
+
use tokio::sync::RwLock;
|
|
112
|
+
|
|
113
|
+
async fn read_heavy(state: &RwLock<State>) {
|
|
114
|
+
// Multiple readers OK, but still don't hold across await
|
|
115
|
+
let value = {
|
|
116
|
+
let guard = state.read().await;
|
|
117
|
+
guard.value.clone()
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
// Process without lock
|
|
121
|
+
let result = process(value).await;
|
|
122
|
+
|
|
123
|
+
// Write lock for update
|
|
124
|
+
state.write().await.result = result;
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## std::sync::Mutex vs tokio::sync::Mutex
|
|
129
|
+
|
|
130
|
+
```rust
|
|
131
|
+
// std::sync::Mutex: Blocks the entire thread
|
|
132
|
+
// - Use for quick, CPU-only operations
|
|
133
|
+
// - NEVER use in async code with await inside
|
|
134
|
+
|
|
135
|
+
// tokio::sync::Mutex: Async-aware, yields to runtime
|
|
136
|
+
// - Use in async code
|
|
137
|
+
// - Still don't hold across await points!
|
|
138
|
+
|
|
139
|
+
// std::sync::Mutex in async (quick operation, OK):
|
|
140
|
+
async fn quick_update(state: &std::sync::Mutex<State>) {
|
|
141
|
+
state.lock().unwrap().counter += 1; // No await, OK
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// tokio::sync::Mutex (must use if lock scope has await):
|
|
145
|
+
async fn must_await_inside(state: &tokio::sync::Mutex<State>) {
|
|
146
|
+
let mut guard = state.lock().await;
|
|
147
|
+
// Only if you REALLY need the lock during async op
|
|
148
|
+
// (usually you don't - redesign instead)
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## See Also
|
|
153
|
+
|
|
154
|
+
- [async-spawn-blocking](async-spawn-blocking.md) - Use spawn_blocking for CPU work
|
|
155
|
+
- [async-clone-before-await](async-clone-before-await.md) - Clone data before await
|
|
156
|
+
- [anti-lock-across-await](anti-lock-across-await.md) - Anti-pattern reference
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
# async-oneshot-response
|
|
2
|
+
|
|
3
|
+
> Use `oneshot` channel for request-response patterns
|
|
4
|
+
|
|
5
|
+
## Why It Matters
|
|
6
|
+
|
|
7
|
+
When one task needs to send a request and wait for exactly one response, `oneshot` is the perfect fit. It's a single-use channel optimized for this pattern—no buffering, no clone overhead. Combined with `mpsc`, it enables clean actor-style message passing.
|
|
8
|
+
|
|
9
|
+
## Bad
|
|
10
|
+
|
|
11
|
+
```rust
|
|
12
|
+
// Using mpsc for single response - wasteful
|
|
13
|
+
let (tx, mut rx) = mpsc::channel::<Response>(1);
|
|
14
|
+
send_request().await;
|
|
15
|
+
let response = rx.recv().await.unwrap();
|
|
16
|
+
// Channel persists, could accidentally receive more
|
|
17
|
+
|
|
18
|
+
// Using shared state - complex
|
|
19
|
+
let result = Arc::new(Mutex::new(None));
|
|
20
|
+
send_request(result.clone()).await;
|
|
21
|
+
while result.lock().await.is_none() {
|
|
22
|
+
tokio::time::sleep(Duration::from_millis(10)).await; // Polling!
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Good
|
|
27
|
+
|
|
28
|
+
```rust
|
|
29
|
+
use tokio::sync::oneshot;
|
|
30
|
+
|
|
31
|
+
let (tx, rx) = oneshot::channel::<Response>();
|
|
32
|
+
|
|
33
|
+
// Send request with reply channel
|
|
34
|
+
send_request(Request { data, reply: tx }).await;
|
|
35
|
+
|
|
36
|
+
// Wait for response
|
|
37
|
+
let response = rx.await?;
|
|
38
|
+
|
|
39
|
+
// Channel is consumed - can't accidentally reuse
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Request-Response Pattern
|
|
43
|
+
|
|
44
|
+
```rust
|
|
45
|
+
use tokio::sync::{mpsc, oneshot};
|
|
46
|
+
|
|
47
|
+
enum Request {
|
|
48
|
+
Get {
|
|
49
|
+
key: String,
|
|
50
|
+
reply: oneshot::Sender<Option<Value>>,
|
|
51
|
+
},
|
|
52
|
+
Set {
|
|
53
|
+
key: String,
|
|
54
|
+
value: Value,
|
|
55
|
+
reply: oneshot::Sender<bool>,
|
|
56
|
+
},
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Service handler
|
|
60
|
+
async fn service(mut rx: mpsc::Receiver<Request>) {
|
|
61
|
+
let mut store = HashMap::new();
|
|
62
|
+
|
|
63
|
+
while let Some(req) = rx.recv().await {
|
|
64
|
+
match req {
|
|
65
|
+
Request::Get { key, reply } => {
|
|
66
|
+
let value = store.get(&key).cloned();
|
|
67
|
+
let _ = reply.send(value); // Ignore if receiver dropped
|
|
68
|
+
}
|
|
69
|
+
Request::Set { key, value, reply } => {
|
|
70
|
+
store.insert(key, value);
|
|
71
|
+
let _ = reply.send(true);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Client
|
|
78
|
+
async fn get_value(tx: &mpsc::Sender<Request>, key: &str) -> Option<Value> {
|
|
79
|
+
let (reply_tx, reply_rx) = oneshot::channel();
|
|
80
|
+
|
|
81
|
+
tx.send(Request::Get {
|
|
82
|
+
key: key.to_string(),
|
|
83
|
+
reply: reply_tx,
|
|
84
|
+
}).await.ok()?;
|
|
85
|
+
|
|
86
|
+
reply_rx.await.ok()?
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## With Timeout
|
|
91
|
+
|
|
92
|
+
```rust
|
|
93
|
+
use tokio::time::{timeout, Duration};
|
|
94
|
+
|
|
95
|
+
async fn request_with_timeout(
|
|
96
|
+
tx: &mpsc::Sender<Request>,
|
|
97
|
+
key: &str,
|
|
98
|
+
) -> Result<Value, Error> {
|
|
99
|
+
let (reply_tx, reply_rx) = oneshot::channel();
|
|
100
|
+
|
|
101
|
+
tx.send(Request::Get {
|
|
102
|
+
key: key.to_string(),
|
|
103
|
+
reply: reply_tx,
|
|
104
|
+
}).await.map_err(|_| Error::ServiceDown)?;
|
|
105
|
+
|
|
106
|
+
timeout(Duration::from_secs(5), reply_rx)
|
|
107
|
+
.await
|
|
108
|
+
.map_err(|_| Error::Timeout)?
|
|
109
|
+
.map_err(|_| Error::ServiceDown)?
|
|
110
|
+
.ok_or(Error::NotFound)
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Error Handling
|
|
115
|
+
|
|
116
|
+
```rust
|
|
117
|
+
use tokio::sync::oneshot;
|
|
118
|
+
|
|
119
|
+
let (tx, rx) = oneshot::channel::<String>();
|
|
120
|
+
|
|
121
|
+
// Sender dropped without sending
|
|
122
|
+
drop(tx);
|
|
123
|
+
match rx.await {
|
|
124
|
+
Ok(value) => println!("Got: {}", value),
|
|
125
|
+
Err(oneshot::error::RecvError { .. }) => {
|
|
126
|
+
println!("Sender dropped");
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Receiver dropped before send
|
|
131
|
+
let (tx, rx) = oneshot::channel::<String>();
|
|
132
|
+
drop(rx);
|
|
133
|
+
match tx.send("hello".to_string()) {
|
|
134
|
+
Ok(()) => println!("Sent"),
|
|
135
|
+
Err(value) => println!("Receiver dropped, value: {}", value),
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Closed Detection
|
|
140
|
+
|
|
141
|
+
```rust
|
|
142
|
+
// Check if receiver is still waiting
|
|
143
|
+
let (tx, rx) = oneshot::channel::<i32>();
|
|
144
|
+
|
|
145
|
+
// In producer
|
|
146
|
+
if tx.is_closed() {
|
|
147
|
+
println!("Receiver already gone, skip expensive computation");
|
|
148
|
+
} else {
|
|
149
|
+
let result = expensive_computation();
|
|
150
|
+
tx.send(result).ok();
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Async wait for close
|
|
154
|
+
let tx_clone = tx.clone(); // Note: can't actually clone, just showing concept
|
|
155
|
+
tokio::select! {
|
|
156
|
+
_ = tx.closed() => println!("Receiver dropped"),
|
|
157
|
+
result = compute() => { tx.send(result).ok(); }
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## Response Type Wrapper
|
|
162
|
+
|
|
163
|
+
```rust
|
|
164
|
+
// Standardize request-response pattern
|
|
165
|
+
struct RpcRequest<Req, Res> {
|
|
166
|
+
request: Req,
|
|
167
|
+
reply: oneshot::Sender<Res>,
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
impl<Req, Res> RpcRequest<Req, Res> {
|
|
171
|
+
fn new(request: Req) -> (Self, oneshot::Receiver<Res>) {
|
|
172
|
+
let (tx, rx) = oneshot::channel();
|
|
173
|
+
(RpcRequest { request, reply: tx }, rx)
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
fn respond(self, response: Res) {
|
|
177
|
+
let _ = self.reply.send(response);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Usage
|
|
182
|
+
let (req, rx) = RpcRequest::new(GetUser { id: 42 });
|
|
183
|
+
tx.send(req).await?;
|
|
184
|
+
let user = rx.await?;
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## See Also
|
|
188
|
+
|
|
189
|
+
- [async-mpsc-queue](./async-mpsc-queue.md) - Pair with oneshot for request-response
|
|
190
|
+
- [async-bounded-channel](./async-bounded-channel.md) - Channel sizing
|
|
191
|
+
- [async-select-racing](./async-select-racing.md) - Timeout patterns
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
# async-select-racing
|
|
2
|
+
|
|
3
|
+
> Use `select!` to race futures and handle the first to complete
|
|
4
|
+
|
|
5
|
+
## Why It Matters
|
|
6
|
+
|
|
7
|
+
Sometimes you need the first result from multiple futures—timeout vs operation, cancellation vs work, or competing alternatives. `tokio::select!` lets you race futures and handle whichever completes first, while properly cancelling the others.
|
|
8
|
+
|
|
9
|
+
## Bad
|
|
10
|
+
|
|
11
|
+
```rust
|
|
12
|
+
// Can't express "whichever finishes first"
|
|
13
|
+
async fn fetch_with_fallback() -> Data {
|
|
14
|
+
match fetch_primary().await {
|
|
15
|
+
Ok(data) => data,
|
|
16
|
+
Err(_) => fetch_fallback().await.unwrap(), // Sequential, not racing
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Manual timeout is error-prone
|
|
21
|
+
async fn fetch_with_timeout() -> Option<Data> {
|
|
22
|
+
let start = Instant::now();
|
|
23
|
+
loop {
|
|
24
|
+
if start.elapsed() > Duration::from_secs(5) {
|
|
25
|
+
return None;
|
|
26
|
+
}
|
|
27
|
+
// How do we check timeout while awaiting?
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Good
|
|
33
|
+
|
|
34
|
+
```rust
|
|
35
|
+
use tokio::select;
|
|
36
|
+
|
|
37
|
+
async fn fetch_with_timeout() -> Result<Data, Error> {
|
|
38
|
+
select! {
|
|
39
|
+
result = fetch_data() => result,
|
|
40
|
+
_ = tokio::time::sleep(Duration::from_secs(5)) => {
|
|
41
|
+
Err(Error::Timeout)
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async fn fetch_with_fallback() -> Data {
|
|
47
|
+
select! {
|
|
48
|
+
result = fetch_primary() => {
|
|
49
|
+
match result {
|
|
50
|
+
Ok(data) => data,
|
|
51
|
+
Err(_) => fetch_fallback().await.unwrap()
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
_ = tokio::time::sleep(Duration::from_secs(1)) => {
|
|
55
|
+
// Primary too slow, use fallback
|
|
56
|
+
fetch_fallback().await.unwrap()
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## select! Syntax
|
|
63
|
+
|
|
64
|
+
```rust
|
|
65
|
+
select! {
|
|
66
|
+
// Pattern = future => handler
|
|
67
|
+
result = async_operation() => {
|
|
68
|
+
// Handle result
|
|
69
|
+
println!("Got: {:?}", result);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Can bind with pattern matching
|
|
73
|
+
Ok(data) = fallible_operation() => {
|
|
74
|
+
process(data);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Conditional branches with if guards
|
|
78
|
+
msg = channel.recv(), if should_receive => {
|
|
79
|
+
handle_message(msg);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// else branch for when all futures are disabled
|
|
83
|
+
else => {
|
|
84
|
+
println!("All branches disabled");
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Cancellation Behavior
|
|
90
|
+
|
|
91
|
+
```rust
|
|
92
|
+
async fn select_example() {
|
|
93
|
+
select! {
|
|
94
|
+
_ = operation_a() => {
|
|
95
|
+
println!("A completed first");
|
|
96
|
+
// operation_b() is dropped/cancelled
|
|
97
|
+
}
|
|
98
|
+
_ = operation_b() => {
|
|
99
|
+
println!("B completed first");
|
|
100
|
+
// operation_a() is dropped/cancelled
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Futures are cancelled at their next .await point
|
|
106
|
+
// For immediate cancellation, futures must be cancel-safe
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Biased Selection
|
|
110
|
+
|
|
111
|
+
```rust
|
|
112
|
+
// By default, select! randomly picks when multiple are ready
|
|
113
|
+
// Use biased mode for deterministic priority
|
|
114
|
+
select! {
|
|
115
|
+
biased; // Check branches in order
|
|
116
|
+
|
|
117
|
+
msg = high_priority.recv() => handle_high(msg),
|
|
118
|
+
msg = low_priority.recv() => handle_low(msg),
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Without biased, both channels have equal chance
|
|
122
|
+
// when both have messages ready
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Loop with select!
|
|
126
|
+
|
|
127
|
+
```rust
|
|
128
|
+
async fn event_loop(
|
|
129
|
+
mut commands: mpsc::Receiver<Command>,
|
|
130
|
+
shutdown: CancellationToken,
|
|
131
|
+
) {
|
|
132
|
+
loop {
|
|
133
|
+
select! {
|
|
134
|
+
_ = shutdown.cancelled() => {
|
|
135
|
+
println!("Shutting down");
|
|
136
|
+
break;
|
|
137
|
+
}
|
|
138
|
+
Some(cmd) = commands.recv() => {
|
|
139
|
+
process_command(cmd).await;
|
|
140
|
+
}
|
|
141
|
+
else => {
|
|
142
|
+
// commands channel closed
|
|
143
|
+
break;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## Racing Multiple of Same Type
|
|
151
|
+
|
|
152
|
+
```rust
|
|
153
|
+
// Race multiple servers for fastest response
|
|
154
|
+
async fn fastest_response(servers: &[String]) -> Result<Response> {
|
|
155
|
+
let futures = servers.iter()
|
|
156
|
+
.map(|s| fetch_from(s))
|
|
157
|
+
.collect::<Vec<_>>();
|
|
158
|
+
|
|
159
|
+
// select! requires static branches, use select_all for dynamic
|
|
160
|
+
let (result, _index, _remaining) =
|
|
161
|
+
futures::future::select_all(futures).await;
|
|
162
|
+
|
|
163
|
+
result
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## Common Patterns
|
|
168
|
+
|
|
169
|
+
```rust
|
|
170
|
+
// Timeout
|
|
171
|
+
select! {
|
|
172
|
+
result = operation() => result,
|
|
173
|
+
_ = sleep(Duration::from_secs(5)) => Err(Timeout),
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Cancellation
|
|
177
|
+
select! {
|
|
178
|
+
result = operation() => result,
|
|
179
|
+
_ = cancel_token.cancelled() => Err(Cancelled),
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Interval with cancellation
|
|
183
|
+
let mut interval = tokio::time::interval(Duration::from_secs(1));
|
|
184
|
+
loop {
|
|
185
|
+
select! {
|
|
186
|
+
_ = shutdown.cancelled() => break,
|
|
187
|
+
_ = interval.tick() => {
|
|
188
|
+
do_periodic_work().await;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## See Also
|
|
195
|
+
|
|
196
|
+
- [async-cancellation-token](./async-cancellation-token.md) - Cancellation patterns
|
|
197
|
+
- [async-join-parallel](./async-join-parallel.md) - All futures, not racing
|
|
198
|
+
- [async-bounded-channel](./async-bounded-channel.md) - Channel operations in select
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# async-spawn-blocking
|
|
2
|
+
|
|
3
|
+
> Use `spawn_blocking` for CPU-intensive work
|
|
4
|
+
|
|
5
|
+
## Why It Matters
|
|
6
|
+
|
|
7
|
+
Async runtimes like Tokio use a small number of threads to handle many tasks. CPU-intensive or blocking operations on these threads starve other tasks. `spawn_blocking` moves such work to a dedicated thread pool.
|
|
8
|
+
|
|
9
|
+
## Bad
|
|
10
|
+
|
|
11
|
+
```rust
|
|
12
|
+
// BAD: Blocks the async runtime thread
|
|
13
|
+
async fn process_image(data: &[u8]) -> ProcessedImage {
|
|
14
|
+
// CPU-intensive work on async thread!
|
|
15
|
+
let resized = resize_image(data); // Blocks!
|
|
16
|
+
let compressed = compress(resized); // Blocks!
|
|
17
|
+
compressed
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// BAD: Synchronous file I/O in async context
|
|
21
|
+
async fn read_large_file(path: &Path) -> Vec<u8> {
|
|
22
|
+
std::fs::read(path).unwrap() // Blocks the runtime!
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Good
|
|
27
|
+
|
|
28
|
+
```rust
|
|
29
|
+
use tokio::task;
|
|
30
|
+
|
|
31
|
+
// GOOD: Offload CPU work to blocking pool
|
|
32
|
+
async fn process_image(data: Vec<u8>) -> ProcessedImage {
|
|
33
|
+
task::spawn_blocking(move || {
|
|
34
|
+
let resized = resize_image(&data);
|
|
35
|
+
compress(resized)
|
|
36
|
+
})
|
|
37
|
+
.await
|
|
38
|
+
.expect("spawn_blocking failed")
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// GOOD: Use async file I/O
|
|
42
|
+
async fn read_large_file(path: &Path) -> tokio::io::Result<Vec<u8>> {
|
|
43
|
+
tokio::fs::read(path).await
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// GOOD: Or spawn_blocking for unavoidable sync I/O
|
|
47
|
+
async fn read_with_sync_lib(path: PathBuf) -> Vec<u8> {
|
|
48
|
+
task::spawn_blocking(move || {
|
|
49
|
+
sync_library::read_file(&path)
|
|
50
|
+
})
|
|
51
|
+
.await
|
|
52
|
+
.unwrap()
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## What Counts as Blocking
|
|
57
|
+
|
|
58
|
+
```rust
|
|
59
|
+
// CPU-intensive operations
|
|
60
|
+
- Cryptographic operations (hashing, encryption)
|
|
61
|
+
- Image/video processing
|
|
62
|
+
- Compression/decompression
|
|
63
|
+
- Complex parsing
|
|
64
|
+
- Mathematical computations
|
|
65
|
+
|
|
66
|
+
// Blocking I/O
|
|
67
|
+
- std::fs operations
|
|
68
|
+
- Synchronous database drivers
|
|
69
|
+
- Synchronous HTTP clients
|
|
70
|
+
- Thread::sleep
|
|
71
|
+
|
|
72
|
+
// Example thresholds (rough guidelines):
|
|
73
|
+
// < 10µs: OK on async thread
|
|
74
|
+
// 10µs - 1ms: Consider spawn_blocking
|
|
75
|
+
// > 1ms: Definitely spawn_blocking
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Practical Examples
|
|
79
|
+
|
|
80
|
+
```rust
|
|
81
|
+
// Password hashing (CPU-intensive)
|
|
82
|
+
async fn hash_password(password: String) -> String {
|
|
83
|
+
task::spawn_blocking(move || {
|
|
84
|
+
bcrypt::hash(password, bcrypt::DEFAULT_COST).unwrap()
|
|
85
|
+
})
|
|
86
|
+
.await
|
|
87
|
+
.unwrap()
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// JSON parsing of large documents
|
|
91
|
+
async fn parse_large_json(data: String) -> serde_json::Value {
|
|
92
|
+
task::spawn_blocking(move || {
|
|
93
|
+
serde_json::from_str(&data).unwrap()
|
|
94
|
+
})
|
|
95
|
+
.await
|
|
96
|
+
.unwrap()
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Compression
|
|
100
|
+
async fn compress_data(data: Vec<u8>) -> Vec<u8> {
|
|
101
|
+
task::spawn_blocking(move || {
|
|
102
|
+
let mut encoder = flate2::write::GzEncoder::new(
|
|
103
|
+
Vec::new(),
|
|
104
|
+
flate2::Compression::default(),
|
|
105
|
+
);
|
|
106
|
+
encoder.write_all(&data).unwrap();
|
|
107
|
+
encoder.finish().unwrap()
|
|
108
|
+
})
|
|
109
|
+
.await
|
|
110
|
+
.unwrap()
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## spawn_blocking vs spawn
|
|
115
|
+
|
|
116
|
+
```rust
|
|
117
|
+
// spawn: Runs async code on runtime threads
|
|
118
|
+
tokio::spawn(async {
|
|
119
|
+
// Async code here
|
|
120
|
+
some_async_operation().await;
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// spawn_blocking: Runs sync code on blocking thread pool
|
|
124
|
+
tokio::task::spawn_blocking(|| {
|
|
125
|
+
// Synchronous, possibly CPU-intensive code
|
|
126
|
+
heavy_computation();
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
// spawn_blocking returns JoinHandle that can be awaited
|
|
130
|
+
let result = tokio::task::spawn_blocking(|| {
|
|
131
|
+
expensive_sync_operation()
|
|
132
|
+
}).await?;
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Rayon for Parallel CPU Work
|
|
136
|
+
|
|
137
|
+
```rust
|
|
138
|
+
// For parallel CPU work, consider Rayon inside spawn_blocking
|
|
139
|
+
async fn parallel_process(items: Vec<Item>) -> Vec<Output> {
|
|
140
|
+
task::spawn_blocking(move || {
|
|
141
|
+
use rayon::prelude::*;
|
|
142
|
+
items.par_iter()
|
|
143
|
+
.map(|item| cpu_intensive_transform(item))
|
|
144
|
+
.collect()
|
|
145
|
+
})
|
|
146
|
+
.await
|
|
147
|
+
.unwrap()
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## See Also
|
|
152
|
+
|
|
153
|
+
- [async-tokio-fs](async-tokio-fs.md) - Use tokio::fs for async file I/O
|
|
154
|
+
- [async-no-lock-await](async-no-lock-await.md) - Don't hold locks across await
|