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
| Error |
Don't Just Say |
Ask 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:
-
What's the workload?
- CPU-bound β threads (std::thread, rayon)
- I/O-bound β async (tokio, async-std)
- Mixed β hybrid approach
-
What's the sharing model?
- No sharing β message passing (channels)
- Immutable sharing β Arc
- Mutable sharing β Arc<Mutex> or Arc<RwLock>
-
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 Keywords |
Load Domain Skill |
Key Constraint |
| Web API, HTTP, axum, actix, handler |
domain-web |
Handlers run on any thread |
| δΊ€ζ, ζ―δ», trading, payment |
domain-fintech |
Audit + thread safety |
| gRPC, kubernetes, microservice |
domain-cloud-native |
Distributed tracing |
| CLI, terminal, clap |
domain-cli |
Usually 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?)
| Situation |
Trace To |
Question |
| Send/Sync in Web |
domain-web |
What's the state management pattern? |
| Send/Sync in CLI |
domain-cli |
Is multi-thread really needed? |
| Mutex vs channels |
m09-domain |
Shared state or message passing? |
| Async vs threads |
m10-performance |
What'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
| Marker |
Meaning |
Example |
Send |
Can transfer ownership between threads |
Most types |
Sync |
Can share references between threads |
Arc<T> |
!Send |
Must stay on one thread |
Rc<T> |
!Sync |
No shared refs across threads |
RefCell<T> |
Quick Reference
| Pattern |
Thread-Safe |
Blocking |
Use When |
std::thread |
Yes |
Yes |
CPU-bound parallelism |
async/await |
Yes |
No |
I/O-bound concurrency |
Mutex<T> |
Yes |
Yes |
Shared mutable state |
RwLock<T> |
Yes |
Yes |
Read-heavy shared state |
mpsc::channel |
Yes |
Optional |
Message passing |
Arc<Mutex<T>> |
Yes |
Yes |
Shared 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
| Error |
Cause |
Fix |
E0277 Send not satisfied |
Non-Send in async |
Use Arc or spawn_local |
E0277 Sync not satisfied |
Non-Sync shared |
Wrap with Mutex |
| Deadlock |
Lock ordering |
Consistent lock order |
future is not Send |
Non-Send across await |
Drop before await |
MutexGuard across await |
Guard held during suspend |
Scope guard properly |
Anti-Patterns
| Anti-Pattern |
Why Bad |
Better |
| Arc<Mutex> everywhere |
Contention, complexity |
Message passing |
| thread::sleep in async |
Blocks executor |
tokio::time::sleep |
| Holding locks across await |
Blocks other tasks |
Scope locks tightly |
| Ignoring deadlock risk |
Hard to debug |
Lock ordering, try_lock |
Async-Specific Patterns
Avoid MutexGuard Across Await
let guard = mutex.lock().await;
do_async().await;
{
let guard = mutex.lock().await;
}
do_async().await;
Non-Send Types in Async
Related Skills
| When |
See |
| Smart pointer choice |
m02-resource |
| Interior mutability |
m03-mutability |
| Performance tuning |
m10-performance |
| Domain concurrency needs |
domain-* |