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}