PluginBench
Skill
Pass
Audit score 90

m07-concurrency

actionbook/rust-skills

Master Rust concurrency: threads, async/await, channels, and thread-safe primitives.

What is m07-concurrency?

Diagnoses concurrency errors (Send/Sync, deadlocks, race conditions) and guides you to the right primitive—threads for CPU work, async for I/O, channels for message passing, or Mutex/RwLock for shared mutable state. Use when you see E0277, thread/spawn/async errors, or need to design concurrent systems.

  • Identifies whether workload is CPU-bound (threads), I/O-bound (async), or mixed
  • Explains Send/Sync marker traits and when types can cross thread boundaries
  • Recommends correct primitive: std::thread, tokio, channels, Arc, Mutex, RwLock, or Atomic types
  • Detects anti-patterns: holding locks across await, Arc<Mutex<T>> overuse, thread::sleep in async
  • Traces errors up to domain context (web, fintech, CLI) to find root design issues
  • Provides decision flowchart for choosing between threads, async, message passing, and shared-state patterns

How to install m07-concurrency

npx skills add https://github.com/actionbook/rust-skills --skill m07-concurrency
Claude Code
Cursor
Windsurf
Cline

How to use m07-concurrency

  1. 1.Identify the error type (E0277 Send/Sync, deadlock, future not Send, etc.) or concurrency design question
  2. 2.Ask the core question: Is this CPU-bound or I/O-bound, and what data needs sharing?
  3. 3.Trace up to domain context if applicable (web API, fintech, CLI) using the Domain Detection Table
  4. 4.Use the decision flowchart to select the right primitive: threads, async, channels, Arc, Mutex, RwLock, or Atomic
  5. 5.Apply the pattern or fix from the Common Errors or Anti-Patterns table
  6. 6.Verify Send/Sync bounds are satisfied and locks are scoped tightly (not held across await)

Use cases

Good for
  • Fix E0277 'Send not satisfied' by determining if type should cross threads or use spawn_local instead
  • Design web API state management: replace Rc with Arc<T> in handlers that run on any thread
  • Choose between Mutex and channels: Mutex for shared mutable state, channels for message passing
  • Prevent deadlocks by enforcing consistent lock ordering and scoping guards tightly
  • Handle non-Send types in async: drop before await, use spawn_local, or switch to Arc
Who it's for
  • Rust developers hitting Send/Sync compiler errors
  • Backend engineers building concurrent web services or APIs
  • Systems programmers parallelizing CPU-bound work
  • Anyone designing multi-threaded or async Rust applications

m07-concurrency FAQ

When should I use threads vs. async/await?

Use std::thread or rayon for CPU-bound work (parallelism). Use async/await with tokio for I/O-bound work (concurrency). Use spawn_blocking in async contexts to run blocking CPU code without blocking the executor.

Why does my Rc fail with 'cannot be sent between threads'?

Rc is !Send because it uses non-atomic reference counting. In multi-threaded or async contexts, use Arc instead. If you're in a single-threaded async runtime, use spawn_local.

Should I use Mutex or channels for shared state?

Use channels for message passing (decoupled, no contention). Use Mutex/RwLock when you need shared mutable state that multiple threads access concurrently. RwLock is better for read-heavy workloads; Mutex for write-heavy.

Why does holding a MutexGuard across await cause problems?

The guard is held while the task suspends, blocking other tasks from acquiring the lock. Scope the lock tightly: acquire, use, and drop before calling .await.

How do I prevent deadlocks?

Enforce consistent lock ordering (always acquire locks in the same order), use try_lock to detect contention, scope locks tightly, and consider message passing instead of shared mutable state.

Full instructions (SKILL.md)

Source of truth, from actionbook/rust-skills.


name: m07-concurrency description: "CRITICAL: Use for concurrency/async. Triggers: E0277 Send Sync, cannot be sent between threads, thread, spawn, channel, mpsc, Mutex, RwLock, Atomic, async, await, Future, tokio, deadlock, race condition, 并发, 线程, 异步, 死锁" user-invocable: false

Concurrency

Layer 1: Language Mechanics

Core Question

Is this CPU-bound or I/O-bound, and what's the sharing model?

Before choosing concurrency primitives:

  • What's the workload type?
  • What data needs to be shared?
  • What's the thread safety requirement?

Error → Design Question

ErrorDon't Just SayAsk Instead
E0277 Send"Add Send bound"Should this type cross threads?
E0277 Sync"Wrap in Mutex"Is shared access really needed?
Future not Send"Use spawn_local"Is async the right choice?
Deadlock"Reorder locks"Is the locking design correct?

Thinking Prompt

Before adding concurrency:

  1. What's the workload?

    • CPU-bound → threads (std::thread, rayon)
    • I/O-bound → async (tokio, async-std)
    • Mixed → hybrid approach
  2. What's the sharing model?

    • No sharing → message passing (channels)
    • Immutable sharing → Arc<T>
    • Mutable sharing → Arc<Mutex<T>> or Arc<RwLock<T>>
  3. What are the Send/Sync requirements?

    • Cross-thread ownership → Send
    • Cross-thread references → Sync
    • Single-thread async → spawn_local

Trace Up ↑ (MANDATORY)

CRITICAL: Don't just fix the error. Trace UP to find domain constraints.

Domain Detection Table

Context KeywordsLoad Domain SkillKey Constraint
Web API, HTTP, axum, actix, handlerdomain-webHandlers run on any thread
交易, 支付, trading, paymentdomain-fintechAudit + thread safety
gRPC, kubernetes, microservicedomain-cloud-nativeDistributed tracing
CLI, terminal, clapdomain-cliUsually single-thread OK

Example: Web API + Rc Error

"Rc cannot be sent between threads" in Web API context
    ↑ DETECT: "Web API" → Load domain-web
    ↑ FIND: domain-web says "Shared state must be thread-safe"
    ↑ FIND: domain-web says "Rc in state" is Common Mistake
    ↓ DESIGN: Use Arc<T> with State extractor
    ↓ IMPL: axum::extract::State<Arc<AppConfig>>

Generic Trace

"Send not satisfied for my type"
    ↑ Ask: What domain is this? Load domain-* skill
    ↑ Ask: Does this type need to cross thread boundaries?
    ↑ Check: m09-domain (is the data model correct?)
SituationTrace ToQuestion
Send/Sync in Webdomain-webWhat's the state management pattern?
Send/Sync in CLIdomain-cliIs multi-thread really needed?
Mutex vs channelsm09-domainShared state or message passing?
Async vs threadsm10-performanceWhat's the workload profile?

Trace Down ↓

From design to implementation:

"Need parallelism for CPU work"
    ↓ Use: std::thread or rayon

"Need concurrency for I/O"
    ↓ Use: async/await with tokio

"Need to share immutable data across threads"
    ↓ Use: Arc<T>

"Need to share mutable data across threads"
    ↓ Use: Arc<Mutex<T>> or Arc<RwLock<T>>
    ↓ Or: channels for message passing

"Need simple atomic operations"
    ↓ Use: AtomicBool, AtomicUsize, etc.

Send/Sync Markers

MarkerMeaningExample
SendCan transfer ownership between threadsMost types
SyncCan share references between threadsArc<T>
!SendMust stay on one threadRc<T>
!SyncNo shared refs across threadsRefCell<T>

Quick Reference

PatternThread-SafeBlockingUse When
std::threadYesYesCPU-bound parallelism
async/awaitYesNoI/O-bound concurrency
Mutex<T>YesYesShared mutable state
RwLock<T>YesYesRead-heavy shared state
mpsc::channelYesOptionalMessage passing
Arc<Mutex<T>>YesYesShared mutable across threads

Decision Flowchart

What type of work?
├─ CPU-bound → std::thread or rayon
├─ I/O-bound → async/await
└─ Mixed → hybrid (spawn_blocking)

Need to share data?
├─ No → message passing (channels)
├─ Immutable → Arc<T>
└─ Mutable →
   ├─ Read-heavy → Arc<RwLock<T>>
   └─ Write-heavy → Arc<Mutex<T>>
   └─ Simple counter → AtomicUsize

Async context?
├─ Type is Send → tokio::spawn
├─ Type is !Send → spawn_local
└─ Blocking code → spawn_blocking

Common Errors

ErrorCauseFix
E0277 Send not satisfiedNon-Send in asyncUse Arc or spawn_local
E0277 Sync not satisfiedNon-Sync sharedWrap with Mutex
DeadlockLock orderingConsistent lock order
future is not SendNon-Send across awaitDrop before await
MutexGuard across awaitGuard held during suspendScope guard properly

Anti-Patterns

Anti-PatternWhy BadBetter
Arc<Mutex<T>> everywhereContention, complexityMessage passing
thread::sleep in asyncBlocks executortokio::time::sleep
Holding locks across awaitBlocks other tasksScope locks tightly
Ignoring deadlock riskHard to debugLock ordering, try_lock

Async-Specific Patterns

Avoid MutexGuard Across Await

// Bad: guard held across await
let guard = mutex.lock().await;
do_async().await;  // guard still held!

// Good: scope the lock
{
    let guard = mutex.lock().await;
    // use guard
}  // guard dropped
do_async().await;

Non-Send Types in Async

// Rc is !Send, can't cross await in spawned task
// Option 1: use Arc instead
// Option 2: use spawn_local (single-thread runtime)
// Option 3: ensure Rc is dropped before .await

Related Skills

WhenSee
Smart pointer choicem02-resource
Interior mutabilitym03-mutability
Performance tuningm10-performance
Domain concurrency needsdomain-*