Expand description
§Deloxide
A cross-language deadlock detector with visualization support.
Deloxide provides tools for detecting potential deadlocks in multithreaded applications by tracking lock acquisitions and releases, and visualizing the thread-lock relationships.
§Overview
Deadlocks are a common concurrency issue that can be challenging to debug and reproduce. Deloxide helps by tracking mutex and reader-writer lock interactions between threads and detecting potential deadlock scenarios in real-time before they cause your application to hang.
§Features
- Real-time deadlock detection: Monitors thread-lock interactions to detect deadlocks as they happen
- Multiple sync primitives: Supports
Mutex,RwLock, andCondvarfor comprehensive deadlock detection - Lock operation logging: Records all lock operations for later analysis
- Web-based visualization: Visualize thread-lock relationships to understand deadlock patterns
- Cross-language support: Core implementation in Rust with C FFI bindings
- Custom deadlock callbacks: Execute custom actions when deadlocks are detected
§Usage Examples
§Mutex Example
use deloxide::{Deloxide, Mutex, thread};
use std::sync::Arc;
use std::time::Duration;
// Initialize the detector with a deadlock callback
Deloxide::new()
.callback(|info| {
println!("Deadlock detected! Cycle: {:?}", info.thread_cycle);
})
.start()
.expect("Failed to initialize detector");
// Create two mutexes
let mutex_a = Arc::new(Mutex::new("Resource A"));
let mutex_b = Arc::new(Mutex::new("Resource B"));
// First thread: Lock A, then try to lock B
let a_clone = Arc::clone(&mutex_a);
let b_clone = Arc::clone(&mutex_b);
let t1 = thread::spawn(move || {
let lock_a = a_clone.lock();
thread::sleep(Duration::from_millis(100));
let lock_b = b_clone.lock();
});
// Second thread: Lock B, then try to lock A (potential deadlock)
let a_clone = Arc::clone(&mutex_a);
let b_clone = Arc::clone(&mutex_b);
let t2 = thread::spawn(move || {
let lock_b = b_clone.lock();
thread::sleep(Duration::from_millis(100));
let lock_a = a_clone.lock();
});§RwLock Example
use deloxide::{Deloxide, RwLock, thread};
use std::sync::Arc;
use std::time::Duration;
// Initialize the detector with a deadlock callback
Deloxide::new()
.callback(|info| {
println!("Deadlock detected! Cycle: {:?}", info.thread_cycle);
})
.start()
.expect("Failed to initialize detector");
// Create an RwLock
let rwlock = Arc::new(RwLock::new("Shared Resource"));
// Multiple reader threads
for i in 0..3 {
let rwlock_clone = Arc::clone(&rwlock);
thread::spawn(move || {
let read_guard = rwlock_clone.read();
println!("Reader {} acquired read lock", i);
thread::sleep(Duration::from_millis(50));
// Read lock is automatically released when guard is dropped
});
}
// Writer thread that tries to upgrade (potential deadlock with readers)
let rwlock_clone = Arc::clone(&rwlock);
thread::spawn(move || {
let read_guard = rwlock_clone.read();
println!("Writer acquired read lock, attempting to upgrade...");
thread::sleep(Duration::from_millis(25));
let write_guard = rwlock_clone.write(); // This will deadlock!
println!("Writer acquired write lock");
});§Condvar Example
use deloxide::{Deloxide, Mutex, Condvar, thread};
use std::sync::Arc;
use std::time::Duration;
// Initialize the detector (omitted callback for brevity)
let _ = Deloxide::new().start();
let pair = Arc::new((Mutex::new(false), Condvar::new()));
let pair2 = pair.clone();
// Thread waiting on condition
thread::spawn(move || {
let (mutex, condvar) = (&pair2.0, &pair2.1);
let mut ready = mutex.lock();
while !*ready {
condvar.wait(&mut ready);
}
});
// Notifier thread
let pair3 = pair.clone();
thread::spawn(move || {
thread::sleep(Duration::from_millis(50));
let (mutex, condvar) = (&pair3.0, &pair3.1);
let mut ready = mutex.lock();
*ready = true;
condvar.notify_one();
});
thread::sleep(Duration::from_millis(150));§Visualization (Showcase)
You can open the interactive visualization in your browser for a given log file,
or for the currently active log if you initialized logging with with_log().
use deloxide::{Deloxide, showcase, showcase_this};
// Initialize with logging enabled (default is "deloxide.log")
Deloxide::new()
.with_log("logs/deadlock_{timestamp}.json") // Optional: override default log path
.callback(|info| {
eprintln!("Deadlock: {:?}", info.thread_cycle);
// Optionally open the current log automatically
showcase_this().expect("Failed to launch visualization");
})
.start()
.unwrap();
// Or later, open a specific log file
showcase("logs/deadlock_20250101_120000.json").unwrap();§Lock Order Graph (optional feature)
Enable the lock-order-graph feature to detect potential deadlocks by tracking
lock acquisition ordering patterns, even when threads don’t actually block.
# Cargo.toml
[dependencies]
deloxide = { version = "1.0.0", features = ["lock-order-graph"] }#[cfg(feature = "lock-order-graph")]
{
use deloxide::Deloxide;
// Enable lock order checking for development (enabled by default if feature is on)
Deloxide::new()
// .no_lock_order_checking() // Optional: disable if needed
.callback(|info| {
use deloxide::DeadlockSource;
match info.source {
DeadlockSource::WaitForGraph => {
println!("🚨 ACTUAL DEADLOCK! Threads are blocked.");
}
DeadlockSource::LockOrderViolation => {
println!("⚠️ SUSPECTED DEADLOCK! Dangerous lock ordering pattern.");
}
}
})
.start()
.unwrap();
}§Stress Testing (optional feature)
Enable the stress-test feature to increase the probability of deadlocks by
strategically delaying threads before lock attempts.
# Cargo.toml
[dependencies]
deloxide = { version = "1.0.0", features = ["stress-test"] }#[cfg(feature = "stress-test")]
{
use deloxide::{Deloxide, StressConfig};
// Random preemption strategy with default config
Deloxide::new()
.with_random_stress()
.start()
.unwrap();
// Component-based strategy with custom config
Deloxide::new()
.with_component_stress()
.with_stress_config(StressConfig {
preemption_probability: 0.7,
min_delay_us: 200,
max_delay_us: 1500,
preempt_after_release: true,
})
.start()
.unwrap();
}Modules§
Structs§
- Condvar
- A wrapper around a condition variable that tracks operations for deadlock detection
- Deadlock
Info - Represents the result of deadlock detection
- Deloxide
- Deloxide configuration builder struct
- Mutex
- A wrapper around a mutex that tracks lock operations for deadlock detection
- Mutex
Guard - Guard for a Mutex, reports lock release when dropped
- RwLock
- A wrapper around a reader-writer lock that tracks operations for deadlock detection
- RwLock
Read Guard - Guard for a shared (read) lock, reports release when dropped
- RwLock
Write Guard - Guard for an exclusive (write) lock, reports release when dropped
Enums§
- Deadlock
Source - Source of deadlock detection