Rust lock

2025-05-21
pub static PINGCANG_LOCK: Lazy<Mutex<()>> = Lazy::new(|| Mutex::new(()));

    let _lock = PINGCANG_LOCK
        .lock()
        .map_err(|_| BinanceError::Other("Failed to acquire lock".into()))?;

Here’s a detailed breakdown of what happens with your lock implementation in Rust:

Lock Acquisition Process

  1. Static Mutex Initialization:
    pub static PINGCANG_LOCK: Lazy<Mutex<()>> = Lazy::new(|| Mutex::new(()));
    
    • The Lazy ensures the Mutex is initialized on first use (thread-safe initialization)
    • Creates a mutex protecting a unit value () (common pattern for pure synchronization)
  2. Lock Attempt:
    let _lock = PINGCANG_LOCK
        .lock()
        .map_err(|_| BinanceError::Other("Failed to acquire lock".into()))?;
    

Step-by-Step Execution Flow

  1. First Thread Arrives:
    • Checks PINGCANG_LOCK (unlocked)
    • Immediately acquires the lock
    • lock() returns Ok(MutexGuard)
    • _lock now holds the guard
  2. Second Thread Arrives While Locked:
    • Attempts to call lock()
    • If the mutex is still held by first thread:
      • Blocks (waits) until the first thread releases the lock
      • No CPU spin - uses OS-level blocking
    • Once first thread drops _lock (goes out of scope):
      • Wakes waiting thread
      • Second thread acquires lock
  3. Poisoned Mutex Case:
    • If a previous thread panicked while holding the lock:
    • lock() returns Err(PoisonError)
    • Your code maps this to BinanceError::Other
  4. Lock Release:
    • Automatically occurs when _lock goes out of scope
    • Implemented via Drop trait on MutexGuard

Key Characteristics

  1. Blocking Behavior:
    • The .lock() call will:
      • Return immediately if unlocked
      • Block (wait) if currently locked
      • No busy-waiting (efficient thread suspension)
  2. Error Handling:
    • Your version properly handles:
      • Poisoning (thread panic during lock)
      • Converts to your error type via ? operator
  3. RAII Guarantees:
    • The lock cannot be accidentally held indefinitely
    • Release is tied to _lock’s scope

Visual Timeline

Thread 1: [lock()] -> [Critical Section] -> [drop(_lock)]
Thread 2:           [lock() blocks]     -> [Acquires lock]

When To Use This Pattern

This is ideal for:

  • Protecting short critical sections
  • Simple exclusive access requirements
  • Cases where poisoning should fail the operation

For async contexts, you’d want tokio::sync::Mutex instead.