Skip to main content

qubit_lock/lock/
lock.rs

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