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}