Skip to main content

qubit_lock/lock/
lock.rs

1/*******************************************************************************
2 *
3 *    Copyright (c) 2025 - 2026.
4 *    Haixing Hu, Qubit Co. Ltd.
5 *
6 *    All rights reserved.
7 *
8 ******************************************************************************/
9//! # Lock Trait
10//!
11//! Defines an unified synchronous lock abstraction that supports acquiring locks
12//! and executing operations within the locked context. This trait allows locks to be
13//! used in a generic way through closures, avoiding the complexity of
14//! explicitly managing lock guards and their lifetimes.
15//!
16//! # Author
17//!
18//! Haixing Hu
19use std::sync::{
20    Mutex,
21    RwLock,
22};
23
24use parking_lot::Mutex as ParkingLotMutex;
25
26use super::try_lock_error::TryLockError;
27
28/// Unified synchronous lock trait
29///
30/// Provides a unified interface for different types of synchronous locks,
31/// supporting both read and write operations. This trait allows locks to be
32/// used in a generic way through closures, avoiding the complexity of
33/// explicitly managing lock guards and their lifetimes.
34///
35/// # Design Philosophy
36///
37/// This trait unifies both exclusive locks (like `Mutex`) and read-write
38/// locks (like `RwLock`) under a single interface. The key insight is that
39/// all locks can be viewed as supporting two operations:
40///
41/// - **Read operations**: Provide immutable access (`&T`) to the data
42/// - **Write operations**: Provide mutable access (`&mut T`) to the data
43///
44/// For exclusive locks (Mutex), both read and write operations acquire the
45/// same exclusive lock, but the API clearly indicates the intended usage.
46/// For read-write locks (RwLock), read operations use shared locks while
47/// write operations use exclusive locks.
48///
49/// This design enables:
50/// - Unified API across different lock types
51/// - Clear semantic distinction between read and write operations
52/// - Generic code that works with any lock type
53/// - Performance optimization through appropriate lock selection
54///
55/// # Performance Characteristics
56///
57/// Different lock implementations have different performance characteristics:
58///
59/// ## Mutex-based locks (ArcMutex, Mutex)
60/// - `read`: Acquires exclusive lock, same performance as write
61/// - `write`: Acquires exclusive lock, same performance as read
62/// - **Use case**: When you need exclusive access or don't know access patterns
63///
64/// ## RwLock-based locks (ArcRwLock, RwLock)
65/// - `read`: Acquires shared lock, allows concurrent readers
66/// - `write`: Acquires exclusive lock, blocks all other operations
67/// - **Use case**: Read-heavy workloads where multiple readers can proceed
68///   concurrently
69///
70/// # Type Parameters
71///
72/// * `T` - The type of data protected by the lock
73///
74/// # Author
75///
76/// Haixing Hu
77pub trait Lock<T: ?Sized> {
78    /// Acquires a read lock and executes a closure
79    ///
80    /// This method provides immutable access to the protected data. It ensures
81    /// proper memory barriers are established:
82    ///
83    /// - **Acquire semantics**: Ensures that all subsequent memory operations
84    ///   see the effects of previous operations released by the lock release.
85    /// - **Release semantics**: Ensures that all previous memory operations are
86    ///   visible to subsequent lock acquisitions when the lock is released.
87    ///
88    /// For exclusive locks (Mutex), this acquires the same exclusive lock as
89    /// write operations. For read-write locks (RwLock), this acquires a
90    /// shared lock allowing concurrent readers.
91    ///
92    /// # Use Cases
93    ///
94    /// - **Data inspection**: Reading values, checking state, validation
95    /// - **Read-only operations**: Computing derived values, formatting output
96    /// - **Condition checking**: Evaluating predicates without modification
97    /// - **Logging and debugging**: Accessing data for diagnostic purposes
98    ///
99    /// # Performance Notes
100    ///
101    /// - **Mutex-based locks**: Same performance as write operations
102    /// - **RwLock-based locks**: Allows concurrent readers, better for
103    ///   read-heavy workloads
104    ///
105    /// # Arguments
106    ///
107    /// * `f` - Closure that receives an immutable reference (`&T`) to the
108    ///   protected data
109    ///
110    /// # Returns
111    ///
112    /// Returns the result produced by the closure
113    ///
114    /// # Example
115    ///
116    /// ```rust
117    /// use qubit_lock::lock::{Lock, ArcRwLock};
118    ///
119    /// let lock = ArcRwLock::new(vec![1, 2, 3]);
120    ///
121    /// // Read operation - allows concurrent readers with RwLock
122    /// let len = lock.read(|data| data.len());
123    /// assert_eq!(len, 3);
124    ///
125    /// // Multiple concurrent readers possible with RwLock
126    /// let sum = lock.read(|data| data.iter().sum::<i32>());
127    /// assert_eq!(sum, 6);
128    /// ```
129    fn read<R, F>(&self, f: F) -> R
130    where
131        F: FnOnce(&T) -> R;
132
133    /// Acquires a write lock and executes a closure
134    ///
135    /// This method provides mutable access to the protected data. It ensures
136    /// proper memory barriers are established:
137    ///
138    /// - **Acquire semantics**: Ensures that all subsequent memory operations
139    ///   see the effects of previous operations released by the lock release.
140    /// - **Release semantics**: Ensures that all previous memory operations are
141    ///   visible to subsequent lock acquisitions when the lock is released.
142    ///
143    /// For all lock types, this acquires an exclusive lock that blocks all
144    /// other operations until the closure completes.
145    ///
146    /// # Use Cases
147    ///
148    /// - **Data modification**: Updating values, adding/removing elements
149    /// - **State changes**: Transitioning between different states
150    /// - **Initialization**: Setting up data structures
151    /// - **Cleanup operations**: Releasing resources, resetting state
152    ///
153    /// # Performance Notes
154    ///
155    /// - **All lock types**: Exclusive access, blocks all other operations
156    /// - **RwLock advantage**: Only blocks during actual writes, not reads
157    ///
158    /// # Arguments
159    ///
160    /// * `f` - Closure that receives a mutable reference (`&mut T`) to the
161    ///   protected data
162    ///
163    /// # Returns
164    ///
165    /// Returns the result produced by the closure
166    ///
167    /// # Example
168    ///
169    /// ```rust
170    /// use qubit_lock::lock::{Lock, ArcRwLock};
171    ///
172    /// let lock = ArcRwLock::new(vec![1, 2, 3]);
173    ///
174    /// // Write operation - exclusive access
175    /// lock.write(|data| {
176    ///     data.push(4);
177    ///     data.sort();
178    /// });
179    ///
180    /// // Verify the changes
181    /// let result = lock.read(|data| data.clone());
182    /// assert_eq!(result, vec![1, 2, 3, 4]);
183    /// ```
184    fn write<R, F>(&self, f: F) -> R
185    where
186        F: FnOnce(&mut T) -> R;
187
188    /// Attempts to acquire a read lock without blocking
189    ///
190    /// This method tries to acquire a read lock immediately. If the lock
191    /// cannot be acquired, it returns a detailed error. Otherwise, it executes
192    /// the closure and returns `Ok` containing the result.
193    ///
194    /// # Arguments
195    ///
196    /// * `f` - Closure that receives an immutable reference (`&T`) to the
197    ///   protected data if the lock is successfully acquired
198    ///
199    /// # Returns
200    ///
201    /// * `Ok(R)` - If the lock was acquired and closure executed
202    /// * `Err(TryLockError::WouldBlock)` - If the lock is currently held in write mode
203    /// * `Err(TryLockError::Poisoned)` - If the lock is poisoned
204    ///
205    /// # Errors
206    ///
207    /// Returns [`TryLockError::WouldBlock`] when the lock cannot be acquired
208    /// immediately. Returns [`TryLockError::Poisoned`] for standard-library
209    /// locks that were poisoned by a panic.
210    ///
211    /// # Example
212    ///
213    /// ```rust
214    /// use qubit_lock::lock::{Lock, ArcRwLock};
215    ///
216    /// let lock = ArcRwLock::new(42);
217    /// if let Ok(value) = lock.try_read(|data| *data) {
218    ///     println!("Got value: {}", value);
219    /// } else {
220    ///     println!("Lock is unavailable");
221    /// }
222    /// ```
223    fn try_read<R, F>(&self, f: F) -> Result<R, TryLockError>
224    where
225        F: FnOnce(&T) -> R;
226
227    /// Attempts to acquire a write lock without blocking
228    ///
229    /// This method tries to acquire a write lock immediately. If the lock
230    /// cannot be acquired, it returns a detailed error. Otherwise, it executes
231    /// the closure and returns `Ok` containing the result.
232    ///
233    /// # Arguments
234    ///
235    /// * `f` - Closure that receives a mutable reference (`&mut T`) to the
236    ///   protected data if the lock is successfully acquired
237    ///
238    /// # Returns
239    ///
240    /// * `Ok(R)` - If the lock was acquired and closure executed
241    /// * `Err(TryLockError::WouldBlock)` - If the lock is currently held by another thread
242    /// * `Err(TryLockError::Poisoned)` - If the lock is poisoned
243    ///
244    /// # Errors
245    ///
246    /// Returns [`TryLockError::WouldBlock`] when the lock cannot be acquired
247    /// immediately. Returns [`TryLockError::Poisoned`] for standard-library
248    /// locks that were poisoned by a panic.
249    ///
250    /// # Example
251    ///
252    /// ```rust
253    /// use qubit_lock::lock::{Lock, ArcMutex};
254    ///
255    /// let lock = ArcMutex::new(42);
256    /// if let Ok(result) = lock.try_write(|data| {
257    ///     *data += 1;
258    ///     *data
259    /// }) {
260    ///     println!("New value: {}", result);
261    /// } else {
262    ///     println!("Lock is unavailable");
263    /// }
264    /// ```
265    fn try_write<R, F>(&self, f: F) -> Result<R, TryLockError>
266    where
267        F: FnOnce(&mut T) -> R;
268}
269
270/// Synchronous mutex implementation of the Lock trait
271///
272/// This implementation uses the standard library's `Mutex` type to provide
273/// a synchronous lock. Both read and write operations acquire the same
274/// exclusive lock, ensuring thread safety at the cost of concurrent access.
275///
276/// # Type Parameters
277///
278/// * `T` - The type of data protected by the lock
279///
280/// # Author
281///
282/// Haixing Hu
283impl<T: ?Sized> Lock<T> for Mutex<T> {
284    /// Acquires the mutex and executes a read-only closure.
285    ///
286    /// # Arguments
287    ///
288    /// * `f` - Closure receiving immutable access to the protected value.
289    ///
290    /// # Returns
291    ///
292    /// The value returned by `f`.
293    ///
294    /// # Panics
295    ///
296    /// Panics if the mutex is poisoned.
297    #[inline]
298    fn read<R, F>(&self, f: F) -> R
299    where
300        F: FnOnce(&T) -> R,
301    {
302        let guard = self.lock().unwrap();
303        f(&*guard)
304    }
305
306    /// Acquires the mutex and executes a mutable closure.
307    ///
308    /// # Arguments
309    ///
310    /// * `f` - Closure receiving mutable access to the protected value.
311    ///
312    /// # Returns
313    ///
314    /// The value returned by `f`.
315    ///
316    /// # Panics
317    ///
318    /// Panics if the mutex is poisoned.
319    #[inline]
320    fn write<R, F>(&self, f: F) -> R
321    where
322        F: FnOnce(&mut T) -> R,
323    {
324        let mut guard = self.lock().unwrap();
325        f(&mut *guard)
326    }
327
328    /// Attempts to acquire the mutex without blocking for a read-only closure.
329    ///
330    /// # Arguments
331    ///
332    /// * `f` - Closure receiving immutable access when the mutex is acquired.
333    ///
334    /// # Returns
335    ///
336    /// `Ok(result)` if the mutex is acquired.
337    ///
338    /// # Errors
339    ///
340    /// Returns [`TryLockError::WouldBlock`] when the mutex is held by another
341    /// thread, or [`TryLockError::Poisoned`] when the mutex is poisoned.
342    #[inline]
343    fn try_read<R, F>(&self, f: F) -> Result<R, TryLockError>
344    where
345        F: FnOnce(&T) -> R,
346    {
347        match self.try_lock() {
348            Ok(guard) => Ok(f(&*guard)),
349            Err(std::sync::TryLockError::WouldBlock) => Err(TryLockError::WouldBlock),
350            Err(std::sync::TryLockError::Poisoned(_)) => Err(TryLockError::Poisoned),
351        }
352    }
353
354    /// Attempts to acquire the mutex without blocking for a mutable closure.
355    ///
356    /// # Arguments
357    ///
358    /// * `f` - Closure receiving mutable access when the mutex is acquired.
359    ///
360    /// # Returns
361    ///
362    /// `Ok(result)` if the mutex is acquired.
363    ///
364    /// # Errors
365    ///
366    /// Returns [`TryLockError::WouldBlock`] when the mutex is held by another
367    /// thread, or [`TryLockError::Poisoned`] when the mutex is poisoned.
368    #[inline]
369    fn try_write<R, F>(&self, f: F) -> Result<R, TryLockError>
370    where
371        F: FnOnce(&mut T) -> R,
372    {
373        match self.try_lock() {
374            Ok(mut guard) => Ok(f(&mut *guard)),
375            Err(std::sync::TryLockError::WouldBlock) => Err(TryLockError::WouldBlock),
376            Err(std::sync::TryLockError::Poisoned(_)) => Err(TryLockError::Poisoned),
377        }
378    }
379}
380
381/// Synchronous read-write lock implementation of the Lock trait
382///
383/// This implementation uses the standard library's `RwLock` type to provide
384/// a synchronous read-write lock. Read operations use shared locks allowing
385/// concurrent readers, while write operations use exclusive locks that
386/// block all other operations.
387///
388/// # Type Parameters
389///
390/// * `T` - The type of data protected by the lock
391///
392/// # Author
393///
394/// Haixing Hu
395impl<T: ?Sized> Lock<T> for RwLock<T> {
396    /// Acquires a shared read lock and executes a closure.
397    ///
398    /// # Arguments
399    ///
400    /// * `f` - Closure receiving immutable access to the protected value.
401    ///
402    /// # Returns
403    ///
404    /// The value returned by `f`.
405    ///
406    /// # Panics
407    ///
408    /// Panics if the read-write lock is poisoned.
409    #[inline]
410    fn read<R, F>(&self, f: F) -> R
411    where
412        F: FnOnce(&T) -> R,
413    {
414        let guard = self.read().unwrap();
415        f(&*guard)
416    }
417
418    /// Acquires an exclusive write lock and executes a closure.
419    ///
420    /// # Arguments
421    ///
422    /// * `f` - Closure receiving mutable access to the protected value.
423    ///
424    /// # Returns
425    ///
426    /// The value returned by `f`.
427    ///
428    /// # Panics
429    ///
430    /// Panics if the read-write lock is poisoned.
431    #[inline]
432    fn write<R, F>(&self, f: F) -> R
433    where
434        F: FnOnce(&mut T) -> R,
435    {
436        let mut guard = self.write().unwrap();
437        f(&mut *guard)
438    }
439
440    /// Attempts to acquire a shared read lock without blocking.
441    ///
442    /// # Arguments
443    ///
444    /// * `f` - Closure receiving immutable access when a read lock is acquired.
445    ///
446    /// # Returns
447    ///
448    /// `Ok(result)` if a read lock is acquired.
449    ///
450    /// # Errors
451    ///
452    /// Returns [`TryLockError::WouldBlock`] when the lock is unavailable, or
453    /// [`TryLockError::Poisoned`] when the lock is poisoned.
454    #[inline]
455    fn try_read<R, F>(&self, f: F) -> Result<R, TryLockError>
456    where
457        F: FnOnce(&T) -> R,
458    {
459        match self.try_read() {
460            Ok(guard) => Ok(f(&*guard)),
461            Err(std::sync::TryLockError::WouldBlock) => Err(TryLockError::WouldBlock),
462            Err(std::sync::TryLockError::Poisoned(_)) => Err(TryLockError::Poisoned),
463        }
464    }
465
466    /// Attempts to acquire an exclusive write lock without blocking.
467    ///
468    /// # Arguments
469    ///
470    /// * `f` - Closure receiving mutable access when a write lock is acquired.
471    ///
472    /// # Returns
473    ///
474    /// `Ok(result)` if a write lock is acquired.
475    ///
476    /// # Errors
477    ///
478    /// Returns [`TryLockError::WouldBlock`] when the lock is unavailable, or
479    /// [`TryLockError::Poisoned`] when the lock is poisoned.
480    #[inline]
481    fn try_write<R, F>(&self, f: F) -> Result<R, TryLockError>
482    where
483        F: FnOnce(&mut T) -> R,
484    {
485        match self.try_write() {
486            Ok(mut guard) => Ok(f(&mut *guard)),
487            Err(std::sync::TryLockError::WouldBlock) => Err(TryLockError::WouldBlock),
488            Err(std::sync::TryLockError::Poisoned(_)) => Err(TryLockError::Poisoned),
489        }
490    }
491}
492
493/// High-performance synchronous mutex implementation of the Lock trait
494///
495/// This implementation uses the `parking_lot` crate's `Mutex` type to provide
496/// a high-performance synchronous lock. Both read and write operations acquire
497/// the same exclusive lock, ensuring thread safety with better performance
498/// than the standard library's Mutex.
499///
500/// # Type Parameters
501///
502/// * `T` - The type of data protected by the lock
503///
504/// # Performance Characteristics
505///
506/// The parking_lot Mutex is generally faster than std::sync::Mutex due to:
507/// - More efficient lock acquisition and release
508/// - Better handling of contended locks
509/// - Reduced memory overhead
510/// - No risk of lock poisoning (panics don't poison the lock)
511///
512/// # Author
513///
514/// Haixing Hu
515impl<T: ?Sized> Lock<T> for ParkingLotMutex<T> {
516    /// Acquires the parking_lot mutex and executes a read-only closure.
517    ///
518    /// # Arguments
519    ///
520    /// * `f` - Closure receiving immutable access to the protected value.
521    ///
522    /// # Returns
523    ///
524    /// The value returned by `f`.
525    #[inline]
526    fn read<R, F>(&self, f: F) -> R
527    where
528        F: FnOnce(&T) -> R,
529    {
530        let guard = self.lock();
531        f(&*guard)
532    }
533
534    /// Acquires the parking_lot mutex and executes a mutable closure.
535    ///
536    /// # Arguments
537    ///
538    /// * `f` - Closure receiving mutable access to the protected value.
539    ///
540    /// # Returns
541    ///
542    /// The value returned by `f`.
543    #[inline]
544    fn write<R, F>(&self, f: F) -> R
545    where
546        F: FnOnce(&mut T) -> R,
547    {
548        let mut guard = self.lock();
549        f(&mut *guard)
550    }
551
552    /// Attempts to acquire the parking_lot mutex without blocking for reading.
553    ///
554    /// # Arguments
555    ///
556    /// * `f` - Closure receiving immutable access when the mutex is acquired.
557    ///
558    /// # Returns
559    ///
560    /// `Ok(result)` if the mutex is acquired.
561    ///
562    /// # Errors
563    ///
564    /// Returns [`TryLockError::WouldBlock`] when the mutex is held by another
565    /// thread. parking_lot mutexes are not poisoned.
566    #[inline]
567    fn try_read<R, F>(&self, f: F) -> Result<R, TryLockError>
568    where
569        F: FnOnce(&T) -> R,
570    {
571        self.try_lock()
572            .map(|guard| f(&*guard))
573            .ok_or(TryLockError::WouldBlock)
574    }
575
576    /// Attempts to acquire the parking_lot mutex without blocking for writing.
577    ///
578    /// # Arguments
579    ///
580    /// * `f` - Closure receiving mutable access when the mutex is acquired.
581    ///
582    /// # Returns
583    ///
584    /// `Ok(result)` if the mutex is acquired.
585    ///
586    /// # Errors
587    ///
588    /// Returns [`TryLockError::WouldBlock`] when the mutex is held by another
589    /// thread. parking_lot mutexes are not poisoned.
590    #[inline]
591    fn try_write<R, F>(&self, f: F) -> Result<R, TryLockError>
592    where
593        F: FnOnce(&mut T) -> R,
594    {
595        self.try_lock()
596            .map(|mut guard| f(&mut *guard))
597            .ok_or(TryLockError::WouldBlock)
598    }
599}