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