rust.blocking_in_async
Performance
High
Causes Production Outages
Detects blocking operations like std::fs, std::thread::sleep, and synchronous network calls inside async functions.
Why It Matters
Section titled “Why It Matters”Async runtimes like Tokio use a limited number of worker threads:
- Thread starvation — Blocking one thread blocks all tasks on it
- Performance collapse — Async benefits disappear
- Deadlocks — Can deadlock if all workers are blocked
- Hidden latency — Other tasks wait unexpectedly
The async executor can’t run other tasks while a thread is blocked.
Example
Section titled “Example”// ❌ Beforeasync fn read_config() -> String { std::fs::read_to_string("config.toml").unwrap() // Blocks!}
async fn slow_operation() { std::thread::sleep(Duration::from_secs(5)); // Blocks!}// ✅ Afterasync fn read_config() -> String { tokio::fs::read_to_string("config.toml").await.unwrap()}
async fn slow_operation() { tokio::time::sleep(Duration::from_secs(5)).await;}What Unfault Detects
Section titled “What Unfault Detects”std::fs::*in async functionsstd::thread::sleepin async context- Synchronous HTTP clients (reqwest blocking)
std::net::*in async functions- Blocking mutex locks (
std::sync::Mutex)
Auto-Fix
Section titled “Auto-Fix”Unfault can convert blocking calls to their async equivalents when Tokio or async-std equivalents are available.
Tokio Equivalents
Section titled “Tokio Equivalents”| Blocking | Async |
|---|---|
std::fs::read_to_string | tokio::fs::read_to_string |
std::fs::write | tokio::fs::write |
std::thread::sleep | tokio::time::sleep |
std::sync::Mutex | tokio::sync::Mutex |
std::net::TcpStream | tokio::net::TcpStream |
When Blocking Is Needed
Section titled “When Blocking Is Needed”// Use spawn_blocking for CPU-heavy worklet result = tokio::task::spawn_blocking(move || { expensive_computation(data)}).await?;
// Or use a dedicated thread poollet pool = rayon::ThreadPoolBuilder::new() .num_threads(4) .build()?;
let result = pool.install(|| parallel_work(data));