agy-superpowers 5.2.1 → 5.2.3
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/README.md +47 -150
- package/package.json +1 -1
- package/template/agent/patches/skills-patches.md +23 -0
- package/template/agent/rules/scratch-scripts.md +37 -0
- package/template/agent/rules/superpowers.md +6 -50
- package/template/agent/skills/brainstorming/SKILL.md +4 -3
- package/template/agent/skills/brainstorming/visual-companion.md +2 -3
- package/template/agent/skills/finishing-a-development-branch/SKILL.md +11 -16
- package/template/agent/skills/subagent-driven-development/SKILL.md +16 -0
- package/template/agent/skills/subagent-driven-development/implementer-prompt.md +4 -3
- package/template/agent/skills/using-git-worktrees/SKILL.md +3 -2
- package/template/agent/skills/using-superpowers/SKILL.md +8 -6
- package/template/agent/skills/using-superpowers/references/copilot-tools.md +52 -0
- package/template/agent/skills/writing-plans/SKILL.md +5 -3
- package/template/agent/skills/writing-skills/SKILL.md +1 -1
- package/template/agent/superpowers-version.json +2 -2
- package/template/agent/tmp/agent-config-backup.yml +9 -0
- package/template/agent/skills/ai-integrated-product/SKILL.md +0 -57
- package/template/agent/skills/analytics-setup/SKILL.md +0 -51
- package/template/agent/skills/api-design/SKILL.md +0 -193
- package/template/agent/skills/app-store-optimizer/SKILL.md +0 -127
- package/template/agent/skills/auth-and-identity/SKILL.md +0 -167
- package/template/agent/skills/backend-developer/SKILL.md +0 -148
- package/template/agent/skills/bootstrapper-finance/SKILL.md +0 -55
- package/template/agent/skills/chrome-extension-developer/SKILL.md +0 -53
- package/template/agent/skills/community-manager/SKILL.md +0 -115
- package/template/agent/skills/content-marketer/SKILL.md +0 -111
- package/template/agent/skills/conversion-optimizer/SKILL.md +0 -142
- package/template/agent/skills/cto-architect/SKILL.md +0 -133
- package/template/agent/skills/customer-success-manager/SKILL.md +0 -126
- package/template/agent/skills/data-analyst/SKILL.md +0 -147
- package/template/agent/skills/devops-engineer/SKILL.md +0 -117
- package/template/agent/skills/email-infrastructure/SKILL.md +0 -164
- package/template/agent/skills/game-design/SKILL.md +0 -194
- package/template/agent/skills/game-developer/SKILL.md +0 -175
- package/template/agent/skills/growth-hacker/SKILL.md +0 -122
- package/template/agent/skills/idea-validator/SKILL.md +0 -55
- package/template/agent/skills/indie-legal/SKILL.md +0 -53
- package/template/agent/skills/influencer-marketer/SKILL.md +0 -141
- package/template/agent/skills/landing-page-builder/SKILL.md +0 -59
- package/template/agent/skills/launch-strategist/SKILL.md +0 -62
- package/template/agent/skills/market-researcher/SKILL.md +0 -53
- package/template/agent/skills/micro-saas-builder/SKILL.md +0 -56
- package/template/agent/skills/monetization-strategist/SKILL.md +0 -119
- package/template/agent/skills/paid-acquisition-specialist/SKILL.md +0 -119
- package/template/agent/skills/pricing-psychologist/SKILL.md +0 -58
- package/template/agent/skills/real-time-features/SKILL.md +0 -194
- package/template/agent/skills/retention-specialist/SKILL.md +0 -123
- package/template/agent/skills/rust-developer/SKILL.md +0 -281
- package/template/agent/skills/rust-developer/references/rust-rules/_sections.md +0 -231
- package/template/agent/skills/rust-developer/references/rust-rules/anti-clone-excessive.md +0 -124
- package/template/agent/skills/rust-developer/references/rust-rules/anti-collect-intermediate.md +0 -131
- package/template/agent/skills/rust-developer/references/rust-rules/anti-empty-catch.md +0 -132
- package/template/agent/skills/rust-developer/references/rust-rules/anti-expect-lazy.md +0 -95
- package/template/agent/skills/rust-developer/references/rust-rules/anti-format-hot-path.md +0 -141
- package/template/agent/skills/rust-developer/references/rust-rules/anti-index-over-iter.md +0 -125
- package/template/agent/skills/rust-developer/references/rust-rules/anti-lock-across-await.md +0 -127
- package/template/agent/skills/rust-developer/references/rust-rules/anti-over-abstraction.md +0 -120
- package/template/agent/skills/rust-developer/references/rust-rules/anti-panic-expected.md +0 -131
- package/template/agent/skills/rust-developer/references/rust-rules/anti-premature-optimize.md +0 -156
- package/template/agent/skills/rust-developer/references/rust-rules/anti-string-for-str.md +0 -122
- package/template/agent/skills/rust-developer/references/rust-rules/anti-stringly-typed.md +0 -167
- package/template/agent/skills/rust-developer/references/rust-rules/anti-type-erasure.md +0 -134
- package/template/agent/skills/rust-developer/references/rust-rules/anti-unwrap-abuse.md +0 -143
- package/template/agent/skills/rust-developer/references/rust-rules/anti-vec-for-slice.md +0 -121
- package/template/agent/skills/rust-developer/references/rust-rules/api-builder-must-use.md +0 -143
- package/template/agent/skills/rust-developer/references/rust-rules/api-builder-pattern.md +0 -187
- package/template/agent/skills/rust-developer/references/rust-rules/api-common-traits.md +0 -165
- package/template/agent/skills/rust-developer/references/rust-rules/api-default-impl.md +0 -177
- package/template/agent/skills/rust-developer/references/rust-rules/api-extension-trait.md +0 -163
- package/template/agent/skills/rust-developer/references/rust-rules/api-from-not-into.md +0 -146
- package/template/agent/skills/rust-developer/references/rust-rules/api-impl-asref.md +0 -142
- package/template/agent/skills/rust-developer/references/rust-rules/api-impl-into.md +0 -160
- package/template/agent/skills/rust-developer/references/rust-rules/api-must-use.md +0 -125
- package/template/agent/skills/rust-developer/references/rust-rules/api-newtype-safety.md +0 -162
- package/template/agent/skills/rust-developer/references/rust-rules/api-non-exhaustive.md +0 -177
- package/template/agent/skills/rust-developer/references/rust-rules/api-parse-dont-validate.md +0 -184
- package/template/agent/skills/rust-developer/references/rust-rules/api-sealed-trait.md +0 -168
- package/template/agent/skills/rust-developer/references/rust-rules/api-serde-optional.md +0 -182
- package/template/agent/skills/rust-developer/references/rust-rules/api-typestate.md +0 -199
- package/template/agent/skills/rust-developer/references/rust-rules/async-bounded-channel.md +0 -175
- package/template/agent/skills/rust-developer/references/rust-rules/async-broadcast-pubsub.md +0 -185
- package/template/agent/skills/rust-developer/references/rust-rules/async-cancellation-token.md +0 -203
- package/template/agent/skills/rust-developer/references/rust-rules/async-clone-before-await.md +0 -171
- package/template/agent/skills/rust-developer/references/rust-rules/async-join-parallel.md +0 -158
- package/template/agent/skills/rust-developer/references/rust-rules/async-joinset-structured.md +0 -195
- package/template/agent/skills/rust-developer/references/rust-rules/async-mpsc-queue.md +0 -171
- package/template/agent/skills/rust-developer/references/rust-rules/async-no-lock-await.md +0 -156
- package/template/agent/skills/rust-developer/references/rust-rules/async-oneshot-response.md +0 -191
- package/template/agent/skills/rust-developer/references/rust-rules/async-select-racing.md +0 -198
- package/template/agent/skills/rust-developer/references/rust-rules/async-spawn-blocking.md +0 -154
- package/template/agent/skills/rust-developer/references/rust-rules/async-tokio-fs.md +0 -167
- package/template/agent/skills/rust-developer/references/rust-rules/async-tokio-runtime.md +0 -169
- package/template/agent/skills/rust-developer/references/rust-rules/async-try-join.md +0 -172
- package/template/agent/skills/rust-developer/references/rust-rules/async-watch-latest.md +0 -189
- package/template/agent/skills/rust-developer/references/rust-rules/doc-all-public.md +0 -113
- package/template/agent/skills/rust-developer/references/rust-rules/doc-cargo-metadata.md +0 -147
- package/template/agent/skills/rust-developer/references/rust-rules/doc-errors-section.md +0 -122
- package/template/agent/skills/rust-developer/references/rust-rules/doc-examples-section.md +0 -161
- package/template/agent/skills/rust-developer/references/rust-rules/doc-hidden-setup.md +0 -149
- package/template/agent/skills/rust-developer/references/rust-rules/doc-intra-links.md +0 -138
- package/template/agent/skills/rust-developer/references/rust-rules/doc-link-types.md +0 -169
- package/template/agent/skills/rust-developer/references/rust-rules/doc-module-inner.md +0 -116
- package/template/agent/skills/rust-developer/references/rust-rules/doc-panics-section.md +0 -128
- package/template/agent/skills/rust-developer/references/rust-rules/doc-question-mark.md +0 -136
- package/template/agent/skills/rust-developer/references/rust-rules/doc-safety-section.md +0 -131
- package/template/agent/skills/rust-developer/references/rust-rules/err-anyhow-app.md +0 -179
- package/template/agent/skills/rust-developer/references/rust-rules/err-context-chain.md +0 -144
- package/template/agent/skills/rust-developer/references/rust-rules/err-custom-type.md +0 -152
- package/template/agent/skills/rust-developer/references/rust-rules/err-doc-errors.md +0 -145
- package/template/agent/skills/rust-developer/references/rust-rules/err-expect-bugs-only.md +0 -133
- package/template/agent/skills/rust-developer/references/rust-rules/err-from-impl.md +0 -152
- package/template/agent/skills/rust-developer/references/rust-rules/err-lowercase-msg.md +0 -124
- package/template/agent/skills/rust-developer/references/rust-rules/err-no-unwrap-prod.md +0 -115
- package/template/agent/skills/rust-developer/references/rust-rules/err-question-mark.md +0 -151
- package/template/agent/skills/rust-developer/references/rust-rules/err-result-over-panic.md +0 -130
- package/template/agent/skills/rust-developer/references/rust-rules/err-source-chain.md +0 -155
- package/template/agent/skills/rust-developer/references/rust-rules/err-thiserror-lib.md +0 -171
- package/template/agent/skills/rust-developer/references/rust-rules/lint-cargo-metadata.md +0 -138
- package/template/agent/skills/rust-developer/references/rust-rules/lint-deny-correctness.md +0 -107
- package/template/agent/skills/rust-developer/references/rust-rules/lint-missing-docs.md +0 -154
- package/template/agent/skills/rust-developer/references/rust-rules/lint-pedantic-selective.md +0 -118
- package/template/agent/skills/rust-developer/references/rust-rules/lint-rustfmt-check.md +0 -157
- package/template/agent/skills/rust-developer/references/rust-rules/lint-unsafe-doc.md +0 -133
- package/template/agent/skills/rust-developer/references/rust-rules/lint-warn-complexity.md +0 -131
- package/template/agent/skills/rust-developer/references/rust-rules/lint-warn-perf.md +0 -136
- package/template/agent/skills/rust-developer/references/rust-rules/lint-warn-style.md +0 -135
- package/template/agent/skills/rust-developer/references/rust-rules/lint-warn-suspicious.md +0 -122
- package/template/agent/skills/rust-developer/references/rust-rules/lint-workspace-lints.md +0 -172
- package/template/agent/skills/rust-developer/references/rust-rules/mem-arena-allocator.md +0 -168
- package/template/agent/skills/rust-developer/references/rust-rules/mem-arrayvec.md +0 -142
- package/template/agent/skills/rust-developer/references/rust-rules/mem-assert-type-size.md +0 -168
- package/template/agent/skills/rust-developer/references/rust-rules/mem-avoid-format.md +0 -147
- package/template/agent/skills/rust-developer/references/rust-rules/mem-box-large-variant.md +0 -158
- package/template/agent/skills/rust-developer/references/rust-rules/mem-boxed-slice.md +0 -139
- package/template/agent/skills/rust-developer/references/rust-rules/mem-clone-from.md +0 -147
- package/template/agent/skills/rust-developer/references/rust-rules/mem-compact-string.md +0 -149
- package/template/agent/skills/rust-developer/references/rust-rules/mem-reuse-collections.md +0 -174
- package/template/agent/skills/rust-developer/references/rust-rules/mem-smaller-integers.md +0 -159
- package/template/agent/skills/rust-developer/references/rust-rules/mem-smallvec.md +0 -138
- package/template/agent/skills/rust-developer/references/rust-rules/mem-thinvec.md +0 -142
- package/template/agent/skills/rust-developer/references/rust-rules/mem-with-capacity.md +0 -156
- package/template/agent/skills/rust-developer/references/rust-rules/mem-write-over-format.md +0 -172
- package/template/agent/skills/rust-developer/references/rust-rules/mem-zero-copy.md +0 -164
- package/template/agent/skills/rust-developer/references/rust-rules/name-acronym-word.md +0 -99
- package/template/agent/skills/rust-developer/references/rust-rules/name-as-free.md +0 -104
- package/template/agent/skills/rust-developer/references/rust-rules/name-consts-screaming.md +0 -94
- package/template/agent/skills/rust-developer/references/rust-rules/name-crate-no-rs.md +0 -78
- package/template/agent/skills/rust-developer/references/rust-rules/name-funcs-snake.md +0 -76
- package/template/agent/skills/rust-developer/references/rust-rules/name-into-ownership.md +0 -123
- package/template/agent/skills/rust-developer/references/rust-rules/name-is-has-bool.md +0 -127
- package/template/agent/skills/rust-developer/references/rust-rules/name-iter-convention.md +0 -129
- package/template/agent/skills/rust-developer/references/rust-rules/name-iter-method.md +0 -131
- package/template/agent/skills/rust-developer/references/rust-rules/name-iter-type-match.md +0 -142
- package/template/agent/skills/rust-developer/references/rust-rules/name-lifetime-short.md +0 -86
- package/template/agent/skills/rust-developer/references/rust-rules/name-no-get-prefix.md +0 -154
- package/template/agent/skills/rust-developer/references/rust-rules/name-to-expensive.md +0 -118
- package/template/agent/skills/rust-developer/references/rust-rules/name-type-param-single.md +0 -92
- package/template/agent/skills/rust-developer/references/rust-rules/name-types-camel.md +0 -65
- package/template/agent/skills/rust-developer/references/rust-rules/name-variants-camel.md +0 -101
- package/template/agent/skills/rust-developer/references/rust-rules/opt-bounds-check.md +0 -161
- package/template/agent/skills/rust-developer/references/rust-rules/opt-cache-friendly.md +0 -187
- package/template/agent/skills/rust-developer/references/rust-rules/opt-codegen-units.md +0 -142
- package/template/agent/skills/rust-developer/references/rust-rules/opt-cold-unlikely.md +0 -152
- package/template/agent/skills/rust-developer/references/rust-rules/opt-inline-always-rare.md +0 -141
- package/template/agent/skills/rust-developer/references/rust-rules/opt-inline-never-cold.md +0 -181
- package/template/agent/skills/rust-developer/references/rust-rules/opt-inline-small.md +0 -160
- package/template/agent/skills/rust-developer/references/rust-rules/opt-likely-hint.md +0 -171
- package/template/agent/skills/rust-developer/references/rust-rules/opt-lto-release.md +0 -130
- package/template/agent/skills/rust-developer/references/rust-rules/opt-pgo-profile.md +0 -167
- package/template/agent/skills/rust-developer/references/rust-rules/opt-simd-portable.md +0 -144
- package/template/agent/skills/rust-developer/references/rust-rules/opt-target-cpu.md +0 -154
- package/template/agent/skills/rust-developer/references/rust-rules/own-arc-shared.md +0 -141
- package/template/agent/skills/rust-developer/references/rust-rules/own-borrow-over-clone.md +0 -95
- package/template/agent/skills/rust-developer/references/rust-rules/own-clone-explicit.md +0 -135
- package/template/agent/skills/rust-developer/references/rust-rules/own-copy-small.md +0 -124
- package/template/agent/skills/rust-developer/references/rust-rules/own-cow-conditional.md +0 -135
- package/template/agent/skills/rust-developer/references/rust-rules/own-lifetime-elision.md +0 -134
- package/template/agent/skills/rust-developer/references/rust-rules/own-move-large.md +0 -134
- package/template/agent/skills/rust-developer/references/rust-rules/own-mutex-interior.md +0 -105
- package/template/agent/skills/rust-developer/references/rust-rules/own-rc-single-thread.md +0 -65
- package/template/agent/skills/rust-developer/references/rust-rules/own-refcell-interior.md +0 -97
- package/template/agent/skills/rust-developer/references/rust-rules/own-rwlock-readers.md +0 -122
- package/template/agent/skills/rust-developer/references/rust-rules/own-slice-over-vec.md +0 -119
- package/template/agent/skills/rust-developer/references/rust-rules/perf-black-box-bench.md +0 -153
- package/template/agent/skills/rust-developer/references/rust-rules/perf-chain-avoid.md +0 -136
- package/template/agent/skills/rust-developer/references/rust-rules/perf-collect-into.md +0 -133
- package/template/agent/skills/rust-developer/references/rust-rules/perf-collect-once.md +0 -120
- package/template/agent/skills/rust-developer/references/rust-rules/perf-drain-reuse.md +0 -137
- package/template/agent/skills/rust-developer/references/rust-rules/perf-entry-api.md +0 -134
- package/template/agent/skills/rust-developer/references/rust-rules/perf-extend-batch.md +0 -150
- package/template/agent/skills/rust-developer/references/rust-rules/perf-iter-lazy.md +0 -123
- package/template/agent/skills/rust-developer/references/rust-rules/perf-iter-over-index.md +0 -113
- package/template/agent/skills/rust-developer/references/rust-rules/perf-profile-first.md +0 -175
- package/template/agent/skills/rust-developer/references/rust-rules/perf-release-profile.md +0 -149
- package/template/agent/skills/rust-developer/references/rust-rules/proj-bin-dir.md +0 -142
- package/template/agent/skills/rust-developer/references/rust-rules/proj-flat-small.md +0 -133
- package/template/agent/skills/rust-developer/references/rust-rules/proj-lib-main-split.md +0 -148
- package/template/agent/skills/rust-developer/references/rust-rules/proj-mod-by-feature.md +0 -130
- package/template/agent/skills/rust-developer/references/rust-rules/proj-mod-rs-dir.md +0 -120
- package/template/agent/skills/rust-developer/references/rust-rules/proj-prelude-module.md +0 -155
- package/template/agent/skills/rust-developer/references/rust-rules/proj-pub-crate-internal.md +0 -139
- package/template/agent/skills/rust-developer/references/rust-rules/proj-pub-super-parent.md +0 -135
- package/template/agent/skills/rust-developer/references/rust-rules/proj-pub-use-reexport.md +0 -162
- package/template/agent/skills/rust-developer/references/rust-rules/proj-workspace-deps.md +0 -186
- package/template/agent/skills/rust-developer/references/rust-rules/proj-workspace-large.md +0 -162
- package/template/agent/skills/rust-developer/references/rust-rules/test-arrange-act-assert.md +0 -160
- package/template/agent/skills/rust-developer/references/rust-rules/test-cfg-test-module.md +0 -151
- package/template/agent/skills/rust-developer/references/rust-rules/test-criterion-bench.md +0 -171
- package/template/agent/skills/rust-developer/references/rust-rules/test-descriptive-names.md +0 -142
- package/template/agent/skills/rust-developer/references/rust-rules/test-doctest-examples.md +0 -168
- package/template/agent/skills/rust-developer/references/rust-rules/test-fixture-raii.md +0 -151
- package/template/agent/skills/rust-developer/references/rust-rules/test-integration-dir.md +0 -144
- package/template/agent/skills/rust-developer/references/rust-rules/test-mock-traits.md +0 -189
- package/template/agent/skills/rust-developer/references/rust-rules/test-mockall-mocking.md +0 -226
- package/template/agent/skills/rust-developer/references/rust-rules/test-proptest-properties.md +0 -161
- package/template/agent/skills/rust-developer/references/rust-rules/test-should-panic.md +0 -130
- package/template/agent/skills/rust-developer/references/rust-rules/test-tokio-async.md +0 -154
- package/template/agent/skills/rust-developer/references/rust-rules/test-use-super.md +0 -127
- package/template/agent/skills/rust-developer/references/rust-rules/type-enum-states.md +0 -154
- package/template/agent/skills/rust-developer/references/rust-rules/type-generic-bounds.md +0 -142
- package/template/agent/skills/rust-developer/references/rust-rules/type-never-diverge.md +0 -146
- package/template/agent/skills/rust-developer/references/rust-rules/type-newtype-ids.md +0 -160
- package/template/agent/skills/rust-developer/references/rust-rules/type-newtype-validated.md +0 -159
- package/template/agent/skills/rust-developer/references/rust-rules/type-no-stringly.md +0 -144
- package/template/agent/skills/rust-developer/references/rust-rules/type-option-nullable.md +0 -137
- package/template/agent/skills/rust-developer/references/rust-rules/type-phantom-marker.md +0 -188
- package/template/agent/skills/rust-developer/references/rust-rules/type-repr-transparent.md +0 -143
- package/template/agent/skills/rust-developer/references/rust-rules/type-result-fallible.md +0 -131
- package/template/agent/skills/saas-architect/SKILL.md +0 -139
- package/template/agent/skills/security-engineer/SKILL.md +0 -133
- package/template/agent/skills/seo-specialist/SKILL.md +0 -130
- package/template/agent/skills/solo-founder-ops/SKILL.md +0 -56
|
@@ -1,198 +0,0 @@
|
|
|
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
|
|
@@ -1,154 +0,0 @@
|
|
|
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
|
|
@@ -1,167 +0,0 @@
|
|
|
1
|
-
# async-tokio-fs
|
|
2
|
-
|
|
3
|
-
> Use `tokio::fs` instead of `std::fs` in async code
|
|
4
|
-
|
|
5
|
-
## Why It Matters
|
|
6
|
-
|
|
7
|
-
`std::fs` operations are blocking—they stop the current thread until the syscall completes. In async code, this blocks the executor thread, preventing it from running other tasks. `tokio::fs` wraps filesystem operations in `spawn_blocking`, keeping the executor responsive.
|
|
8
|
-
|
|
9
|
-
## Bad
|
|
10
|
-
|
|
11
|
-
```rust
|
|
12
|
-
async fn process_files(paths: &[PathBuf]) -> Result<Vec<String>> {
|
|
13
|
-
let mut contents = Vec::new();
|
|
14
|
-
|
|
15
|
-
for path in paths {
|
|
16
|
-
// BLOCKS the entire executor thread!
|
|
17
|
-
let data = std::fs::read_to_string(path)?;
|
|
18
|
-
contents.push(data);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
Ok(contents)
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// While reading a file, NO other tasks can run on this thread
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
## Good
|
|
28
|
-
|
|
29
|
-
```rust
|
|
30
|
-
use tokio::fs;
|
|
31
|
-
|
|
32
|
-
async fn process_files(paths: &[PathBuf]) -> Result<Vec<String>> {
|
|
33
|
-
let mut contents = Vec::new();
|
|
34
|
-
|
|
35
|
-
for path in paths {
|
|
36
|
-
// Non-blocking: allows other tasks to run
|
|
37
|
-
let data = fs::read_to_string(path).await?;
|
|
38
|
-
contents.push(data);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
Ok(contents)
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// Even better: concurrent reads
|
|
45
|
-
async fn process_files_concurrent(paths: &[PathBuf]) -> Result<Vec<String>> {
|
|
46
|
-
let futures: Vec<_> = paths.iter()
|
|
47
|
-
.map(|path| fs::read_to_string(path))
|
|
48
|
-
.collect();
|
|
49
|
-
|
|
50
|
-
futures::future::try_join_all(futures).await
|
|
51
|
-
}
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
## tokio::fs API
|
|
55
|
-
|
|
56
|
-
```rust
|
|
57
|
-
use tokio::fs;
|
|
58
|
-
|
|
59
|
-
// Reading
|
|
60
|
-
let contents = fs::read_to_string("file.txt").await?;
|
|
61
|
-
let bytes = fs::read("file.bin").await?;
|
|
62
|
-
|
|
63
|
-
// Writing
|
|
64
|
-
fs::write("output.txt", "contents").await?;
|
|
65
|
-
|
|
66
|
-
// File operations
|
|
67
|
-
let file = fs::File::open("file.txt").await?;
|
|
68
|
-
let file = fs::File::create("new.txt").await?;
|
|
69
|
-
|
|
70
|
-
// Directory operations
|
|
71
|
-
fs::create_dir("new_dir").await?;
|
|
72
|
-
fs::create_dir_all("nested/dir/path").await?;
|
|
73
|
-
fs::remove_dir("empty_dir").await?;
|
|
74
|
-
fs::remove_dir_all("dir_with_contents").await?;
|
|
75
|
-
|
|
76
|
-
// Metadata
|
|
77
|
-
let metadata = fs::metadata("file.txt").await?;
|
|
78
|
-
let canonical = fs::canonicalize("./relative").await?;
|
|
79
|
-
|
|
80
|
-
// Rename/remove
|
|
81
|
-
fs::rename("old.txt", "new.txt").await?;
|
|
82
|
-
fs::remove_file("file.txt").await?;
|
|
83
|
-
|
|
84
|
-
// Read directory
|
|
85
|
-
let mut entries = fs::read_dir("some_dir").await?;
|
|
86
|
-
while let Some(entry) = entries.next_entry().await? {
|
|
87
|
-
println!("{}", entry.path().display());
|
|
88
|
-
}
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
## Async File I/O
|
|
92
|
-
|
|
93
|
-
```rust
|
|
94
|
-
use tokio::fs::File;
|
|
95
|
-
use tokio::io::{AsyncReadExt, AsyncWriteExt, AsyncBufReadExt, BufReader};
|
|
96
|
-
|
|
97
|
-
// Read with buffer
|
|
98
|
-
let mut file = File::open("large.bin").await?;
|
|
99
|
-
let mut buffer = vec![0u8; 4096];
|
|
100
|
-
let bytes_read = file.read(&mut buffer).await?;
|
|
101
|
-
|
|
102
|
-
// Read all
|
|
103
|
-
let mut contents = Vec::new();
|
|
104
|
-
file.read_to_end(&mut contents).await?;
|
|
105
|
-
|
|
106
|
-
// Write
|
|
107
|
-
let mut file = File::create("output.bin").await?;
|
|
108
|
-
file.write_all(b"data").await?;
|
|
109
|
-
file.flush().await?;
|
|
110
|
-
|
|
111
|
-
// Buffered line reading
|
|
112
|
-
let file = File::open("lines.txt").await?;
|
|
113
|
-
let reader = BufReader::new(file);
|
|
114
|
-
let mut lines = reader.lines();
|
|
115
|
-
|
|
116
|
-
while let Some(line) = lines.next_line().await? {
|
|
117
|
-
println!("{}", line);
|
|
118
|
-
}
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
## When std::fs is Acceptable
|
|
122
|
-
|
|
123
|
-
```rust
|
|
124
|
-
// Startup/initialization (before async runtime)
|
|
125
|
-
fn main() {
|
|
126
|
-
let config = std::fs::read_to_string("config.toml")
|
|
127
|
-
.expect("config file required");
|
|
128
|
-
|
|
129
|
-
tokio::runtime::Runtime::new()
|
|
130
|
-
.unwrap()
|
|
131
|
-
.block_on(run_with_config(config));
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// Single-threaded current_thread runtime (less impact)
|
|
135
|
-
#[tokio::main(flavor = "current_thread")]
|
|
136
|
-
async fn main() {
|
|
137
|
-
// Still prefer tokio::fs, but impact is lower
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
// When file operations are rare and quick
|
|
141
|
-
// (e.g., reading small config once per hour)
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
## Performance Considerations
|
|
145
|
-
|
|
146
|
-
```rust
|
|
147
|
-
// tokio::fs uses spawn_blocking internally
|
|
148
|
-
// For many small files, the overhead adds up
|
|
149
|
-
|
|
150
|
-
// Batch operations when possible
|
|
151
|
-
let paths: Vec<_> = entries.iter()
|
|
152
|
-
.map(|e| e.path())
|
|
153
|
-
.collect();
|
|
154
|
-
|
|
155
|
-
let contents = futures::future::try_join_all(
|
|
156
|
-
paths.iter().map(|p| fs::read_to_string(p))
|
|
157
|
-
).await?;
|
|
158
|
-
|
|
159
|
-
// For heavy I/O, consider memory-mapped files
|
|
160
|
-
// (requires unsafe or mmap crate)
|
|
161
|
-
```
|
|
162
|
-
|
|
163
|
-
## See Also
|
|
164
|
-
|
|
165
|
-
- [async-spawn-blocking](./async-spawn-blocking.md) - How tokio::fs works internally
|
|
166
|
-
- [async-tokio-runtime](./async-tokio-runtime.md) - Runtime configuration
|
|
167
|
-
- [err-context-chain](./err-context-chain.md) - Adding path context to IO errors
|
|
@@ -1,169 +0,0 @@
|
|
|
1
|
-
# async-tokio-runtime
|
|
2
|
-
|
|
3
|
-
> Configure Tokio runtime appropriately for your workload
|
|
4
|
-
|
|
5
|
-
## Why It Matters
|
|
6
|
-
|
|
7
|
-
Tokio's default multi-threaded runtime isn't always optimal. CPU-bound work needs different configuration than IO-bound work. Incorrect configuration leads to poor performance, blocked workers, or resource exhaustion. Understanding runtime options lets you tune for your specific use case.
|
|
8
|
-
|
|
9
|
-
## Bad
|
|
10
|
-
|
|
11
|
-
```rust
|
|
12
|
-
// Default runtime for everything - not optimal
|
|
13
|
-
#[tokio::main]
|
|
14
|
-
async fn main() {
|
|
15
|
-
// CPU-heavy work on async executor starves IO tasks
|
|
16
|
-
for data in datasets {
|
|
17
|
-
let result = heavy_computation(data).await;
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
// Single-threaded when multi-threaded is needed
|
|
22
|
-
#[tokio::main(flavor = "current_thread")]
|
|
23
|
-
async fn main() {
|
|
24
|
-
// Can't utilize multiple cores for concurrent tasks
|
|
25
|
-
for _ in 0..1000 {
|
|
26
|
-
tokio::spawn(async { /* IO work */ });
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
## Good
|
|
32
|
-
|
|
33
|
-
```rust
|
|
34
|
-
// Multi-threaded for concurrent IO (default)
|
|
35
|
-
#[tokio::main]
|
|
36
|
-
async fn main() {
|
|
37
|
-
// Good for many concurrent network connections
|
|
38
|
-
let handles: Vec<_> = urls.iter()
|
|
39
|
-
.map(|url| tokio::spawn(fetch(url.clone())))
|
|
40
|
-
.collect();
|
|
41
|
-
|
|
42
|
-
futures::future::join_all(handles).await;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// Current-thread for single-threaded scenarios
|
|
46
|
-
#[tokio::main(flavor = "current_thread")]
|
|
47
|
-
async fn main() {
|
|
48
|
-
// Good for single-connection clients, simpler debugging
|
|
49
|
-
let client = Client::new();
|
|
50
|
-
client.run().await;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// Custom configuration
|
|
54
|
-
#[tokio::main(worker_threads = 4)]
|
|
55
|
-
async fn main() {
|
|
56
|
-
// Limit to 4 worker threads
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// Or manual setup for more control
|
|
60
|
-
fn main() {
|
|
61
|
-
let runtime = tokio::runtime::Builder::new_multi_thread()
|
|
62
|
-
.worker_threads(4)
|
|
63
|
-
.enable_all()
|
|
64
|
-
.thread_name("my-worker")
|
|
65
|
-
.build()
|
|
66
|
-
.unwrap();
|
|
67
|
-
|
|
68
|
-
runtime.block_on(async_main());
|
|
69
|
-
}
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
## Runtime Types
|
|
73
|
-
|
|
74
|
-
| Runtime | Use Case | Configuration |
|
|
75
|
-
|---------|----------|---------------|
|
|
76
|
-
| Multi-thread | IO-bound, many connections | `#[tokio::main]` (default) |
|
|
77
|
-
| Current-thread | CLI tools, tests, single connection | `flavor = "current_thread"` |
|
|
78
|
-
| Custom | Fine-tuned performance | `Builder::new_*()` |
|
|
79
|
-
|
|
80
|
-
## Worker Thread Tuning
|
|
81
|
-
|
|
82
|
-
```rust
|
|
83
|
-
use tokio::runtime::Builder;
|
|
84
|
-
|
|
85
|
-
// IO-bound: more threads than cores can help
|
|
86
|
-
let io_runtime = Builder::new_multi_thread()
|
|
87
|
-
.worker_threads(num_cpus::get() * 2) // IO can benefit from oversubscription
|
|
88
|
-
.max_blocking_threads(32) // For spawn_blocking calls
|
|
89
|
-
.enable_io()
|
|
90
|
-
.enable_time()
|
|
91
|
-
.build()?;
|
|
92
|
-
|
|
93
|
-
// CPU-bound: match core count
|
|
94
|
-
let cpu_runtime = Builder::new_multi_thread()
|
|
95
|
-
.worker_threads(num_cpus::get()) // No benefit from more than cores
|
|
96
|
-
.build()?;
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
## Multiple Runtimes
|
|
100
|
-
|
|
101
|
-
```rust
|
|
102
|
-
// Separate runtimes for different workloads
|
|
103
|
-
struct App {
|
|
104
|
-
io_runtime: Runtime,
|
|
105
|
-
cpu_runtime: Runtime,
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
impl App {
|
|
109
|
-
fn new() -> Self {
|
|
110
|
-
Self {
|
|
111
|
-
io_runtime: Builder::new_multi_thread()
|
|
112
|
-
.worker_threads(8)
|
|
113
|
-
.thread_name("io-worker")
|
|
114
|
-
.build()
|
|
115
|
-
.unwrap(),
|
|
116
|
-
cpu_runtime: Builder::new_multi_thread()
|
|
117
|
-
.worker_threads(4)
|
|
118
|
-
.thread_name("cpu-worker")
|
|
119
|
-
.build()
|
|
120
|
-
.unwrap(),
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
fn spawn_io<F>(&self, future: F)
|
|
125
|
-
where F: Future + Send + 'static, F::Output: Send + 'static
|
|
126
|
-
{
|
|
127
|
-
self.io_runtime.spawn(future);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
fn spawn_cpu<F>(&self, task: F)
|
|
131
|
-
where F: FnOnce() + Send + 'static
|
|
132
|
-
{
|
|
133
|
-
self.cpu_runtime.spawn_blocking(task);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
## Runtime in Tests
|
|
139
|
-
|
|
140
|
-
```rust
|
|
141
|
-
// Single test runtime
|
|
142
|
-
#[tokio::test]
|
|
143
|
-
async fn test_single() {
|
|
144
|
-
assert!(true);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// Multi-threaded test
|
|
148
|
-
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
|
149
|
-
async fn test_concurrent() {
|
|
150
|
-
let (tx, rx) = tokio::sync::oneshot::channel();
|
|
151
|
-
tokio::spawn(async move { tx.send(42).unwrap() });
|
|
152
|
-
assert_eq!(rx.await.unwrap(), 42);
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
// Custom runtime in test
|
|
156
|
-
#[test]
|
|
157
|
-
fn test_with_custom_runtime() {
|
|
158
|
-
let rt = Builder::new_current_thread().build().unwrap();
|
|
159
|
-
rt.block_on(async {
|
|
160
|
-
// test code
|
|
161
|
-
});
|
|
162
|
-
}
|
|
163
|
-
```
|
|
164
|
-
|
|
165
|
-
## See Also
|
|
166
|
-
|
|
167
|
-
- [async-spawn-blocking](./async-spawn-blocking.md) - Handling blocking code
|
|
168
|
-
- [async-no-lock-await](./async-no-lock-await.md) - Avoiding lock issues
|
|
169
|
-
- [async-joinset-structured](./async-joinset-structured.md) - Managing spawned tasks
|