Loading page…
Rust walkthroughs
Loading page…
parking_lot is a crate that provides more performant and feature-rich synchronization primitives compared to the standard library. It offers smaller memory footprints, faster operations, and additional features like fair locking, timeout support, and deadlock detection.
Key concepts:
When to use parking_lot:
When NOT to use parking_lot:
use parking_lot::Mutex;
use std::sync::Arc;
use std::thread;
fn main() {
let data = Arc::new(Mutex::new(vec![1, 2, 3]));
let mut handles = vec![];
for i in 0..3 {
let data = Arc::clone(&data);
handles.push(thread::spawn(move || {
let mut guard = data.lock();
guard.push(i + 10);
}));
}
for handle in handles {
handle.join().unwrap();
}
println!("Final data: {:?}", *data.lock());
}use parking_lot::RwLock;
use std::sync::Arc;
use std::thread;
fn main() {
let data = Arc::new(RwLock::new(vec![1, 2, 3, 4, 5]));
let mut handles = vec![];
// Multiple readers can hold the lock simultaneously
for i in 0..5 {
let data = Arc::clone(&data);
handles.push(thread::spawn(move || {
let guard = data.read();
println!("Reader {}: {:?}", i, *guard);
}));
}
// Writer needs exclusive access
let data_writer = Arc::clone(&data);
handles.push(thread::spawn(move || {
let mut guard = data_writer.write();
guard.push(6);
println!("Writer added element");
}));
for handle in handles {
handle.join().unwrap();
}
}use parking_lot::Mutex;
use std::sync::Arc;
use std::thread;
use std::time::Duration;
fn main() {
let mutex = Arc::new(Mutex::new(0));
// Try to lock immediately
match mutex.try_lock() {
Some(guard) => println!("Got lock immediately: {}", *guard),
None => println!("Lock is held by another thread"),
}
// Try with timeout
let mutex_clone = Arc::clone(&mutex);
let handle = thread::spawn(move || {
let _guard = mutex_clone.lock();
thread::sleep(Duration::from_millis(200));
});
thread::sleep(Duration::from_millis(10));
match mutex.try_lock_for(Duration::from_millis(50)) {
Some(guard) => println!("Got lock with timeout"),
None => println!("Timeout waiting for lock"),
}
handle.join().unwrap();
}use parking_lot::{Mutex, Condvar};
use std::sync::Arc;
use std::thread;
fn main() {
let pair = Arc::new((Mutex::new(false), Condvar::new()));
let pair_clone = Arc::clone(&pair);
let waiter = thread::spawn(move || {
let (lock, cvar) = &*pair_clone;
let mut started = lock.lock();
while !*started {
cvar.wait(&mut started);
}
println!("Waiter thread proceeding!");
});
thread::sleep(std::time::Duration::from_millis(100));
{
let (lock, cvar) = &*pair;
let mut started = lock.lock();
*started = true;
cvar.notify_one();
}
waiter.join().unwrap();
}use parking_lot::Once;
use std::thread;
static INIT: Once = Once::new();
fn expensive_setup() {
println!("Running expensive setup...");
}
fn main() {
let mut handles = vec![];
for i in 0..5 {
handles.push(thread::spawn(move || {
INIT.call_once(|| {
expensive_setup();
});
println!("Thread {} proceeding", i);
}));
}
for handle in handles {
handle.join().unwrap();
}
}use parking_lot::FairMutex;
use std::sync::Arc;
use std::thread;
fn main() {
// FairMutex ensures threads get the lock in FIFO order
let mutex = Arc::new(FairMutex::new(0));
let mut handles = vec![];
for i in 0..5 {
let mutex = Arc::clone(&mutex);
handles.push(thread::spawn(move || {
let guard = mutex.lock();
println!("Thread {} got lock", i);
thread::sleep(std::time::Duration::from_millis(50));
}));
}
for handle in handles {
handle.join().unwrap();
}
}// Enable deadlock detection in Cargo.toml:
// [dependencies]
// parking_lot = { version = "0.12", features = ["deadlock_detection"] }
use parking_lot::Mutex;
use std::thread;
fn main() {
let a = Mutex::new(0);
let b = Mutex::new(0);
let handle1 = thread::spawn(move || {
let _a = a.lock();
thread::sleep(std::time::Duration::from_millis(10));
// This would cause a deadlock if we try to lock b
// let _b = b.lock();
});
handle1.join().unwrap();
}
// Check for deadlocks in a background thread:
#[cfg(feature = "deadlock_detection")]
fn check_deadlocks() {
thread::spawn(move || {
loop {
let deadlocks = parking_lot::deadlock::check_deadlock();
if !deadlocks.is_empty() {
for deadlock in deadlocks {
println!("Deadlock detected!");
for thread in deadlock {
println!("Thread {:?}", thread.thread_id());
}
}
}
thread::sleep(std::time::Duration::from_secs(1));
}
});
}use parking_lot::Mutex;
use std::sync::Arc;
struct AppState {
users: Vec<String>,
settings: Settings,
}
struct Settings {
debug: bool,
max_connections: usize,
}
fn main() {
let state = Arc::new(Mutex::new(AppState {
users: vec!["alice".to_string()],
settings: Settings {
debug: true,
max_connections: 100,
},
}));
// Lock just the settings portion
let guard = state.lock();
let settings = MutexGuard::map(guard, |s| &mut s.settings);
println!("Debug: {}", settings.debug);
}use parking_lot::RwLock;
use std::sync::Arc;
use std::thread;
fn main() {
let lock = Arc::new(RwLock::new(10));
// Upgradable read allows upgrading to write later
let read_guard = lock.upgradable_read();
println!("Current value: {}", *read_guard);
// Upgrade to write guard (exclusive access)
let write_guard = RwLockUpgradableReadGuard::upgrade(read_guard);
*write_guard = 20;
println!("Updated value: {}", *write_guard);
}use parking_lot::Barrier;
use std::sync::Arc;
use std::thread;
fn main() {
let barrier = Arc::new(Barrier::new(3));
let mut handles = vec![];
for i in 0..3 {
let barrier = Arc::clone(&barrier);
handles.push(thread::spawn(move || {
println!("Thread {} before barrier", i);
barrier.wait();
println!("Thread {} after barrier", i);
}));
}
for handle in handles {
handle.join().unwrap();
}
}use parking_lot::Semaphore;
use std::sync::Arc;
use std::thread;
fn main() {
// Limit to 3 concurrent threads
let semaphore = Arc::new(Semaphore::new(3));
let mut handles = vec![];
for i in 0..10 {
let sem = Arc::clone(&semaphore);
handles.push(thread::spawn(move || {
let _permit = sem.acquire();
println!("Thread {} acquired permit", i);
thread::sleep(std::time::Duration::from_millis(100));
println!("Thread {} releasing permit", i);
}));
}
for handle in handles {
handle.join().unwrap();
}
}use std::sync::Mutex as StdMutex;
use parking_lot::Mutex as PlMutex;
use std::sync::Arc;
use std::thread;
fn main() {
// std::sync::Mutex - can panic on poisoned lock
let std_mutex = Arc::new(StdMutex::new(0));
{
let guard = std_mutex.lock().unwrap();
println!("std Mutex: {}", *guard);
}
// parking_lot::Mutex - no poisoning, simpler API
let pl_mutex = Arc::new(PlMutex::new(0));
{
let guard = pl_mutex.lock();
println!("parking_lot Mutex: {}", *guard);
}
}use parking_lot::ReentrantMutex;
fn main() {
let mutex = ReentrantMutex::new(0);
{
let guard1 = mutex.lock();
{
// Same thread can acquire the lock again
let guard2 = mutex.lock();
println!("Nested locks: {}", *guard2);
}
println!("Still holding outer lock: {}", *guard1);
}
}use parking_lot::RawMutex;
fn main() {
let mutex = RawMutex::new();
// Manual lock/unlock (unsafe)
mutex.lock();
println!("Locked");
mutex.unlock();
println!("Unlocked");
}use parking_lot::Mutex;
use std::sync::Arc;
use std::thread;
use std::time::Instant;
fn main() {
let mutex = Arc::new(Mutex::new(0));
let mutex_clone = Arc::clone(&mutex);
let handle = thread::spawn(move || {
let _guard = mutex_clone.lock();
thread::sleep(std::time::Duration::from_millis(100));
});
thread::sleep(std::time::Duration::from_millis(10));
let start = Instant::now();
match mutex.try_lock_until(start + std::time::Duration::from_millis(200)) {
Some(_guard) => println!("Got lock!"),
None => println!("Failed to get lock"),
}
handle.join().unwrap();
}parking_lot Types:
| Type | Description |
|------|-------------|
| Mutex<T> | Fast mutex, no poisoning |
| RwLock<T> | Reader-writer lock |
| Condvar | Condition variable |
| Once | One-time initialization |
| Barrier | Thread coordination |
| Semaphore | Resource limiting |
| FairMutex<T> | FIFO-ordered mutex |
| ReentrantMutex<T> | Same-thread re-locking |
parking_lot vs std::sync:
| Feature | std::sync | parking_lot | |---------|-----------|-------------| | Mutex size | 40 bytes | 20 bytes | | Poisoning | Yes | No | | Fair locking | No | Yes (FairMutex) | | Deadlock detection | No | Yes (feature) | | Timeout support | Limited | Full | | Condvar wait | Needs MutexGuard | Direct API |
Advantages of parking_lot:
| Advantage | Description | |-----------|-------------| | Smaller | 2x smaller mutex size | | Faster | Optimized algorithms | | Fair | No thread starvation | | Feature-rich | Timeouts, try_lock_until | | Debug friendly | Deadlock detection | | No poisoning | Simpler error handling |
Key Points:
parking_lot to Cargo.tomlMutex::lock() returns a MutexGuard, no Resulttry_lock(), try_lock_for(), try_lock_until() for non-blockingRwLock has upgradable_read() for read-then-write patternsFairMutex prevents thread starvationdeadlock_detection feature for debug buildsReentrantMutex allows same-thread re-locking