Skip to main content

qubit_lock/lock/
async_lock.rs

1/*******************************************************************************
2 *
3 *    Copyright (c) 2025 - 2026.
4 *    Haixing Hu, Qubit Co. Ltd.
5 *
6 *    All rights reserved.
7 *
8 ******************************************************************************/
9//! # Asynchronous Lock Trait
10//!
11//! Defines an asynchronous lock abstraction that supports acquiring
12//! locks without blocking threads.
13//!
14//! # Author
15//!
16//! Haixing Hu
17use std::future::Future;
18
19use tokio::sync::{
20    Mutex as AsyncMutex,
21    RwLock as AsyncRwLock,
22};
23
24/// Unified asynchronous lock trait
25///
26/// Provides a unified interface for different types of asynchronous
27/// locks, supporting both read and write operations. This trait allows
28/// locks to be used in async contexts through closures, avoiding the
29/// complexity of explicitly managing lock guards and their lifetimes.
30///
31/// # Design Philosophy
32///
33/// This trait unifies both exclusive async locks (like `tokio::sync::Mutex`)
34/// and read-write async locks (like `tokio::sync::RwLock`) under a single
35/// interface. The key insight is that all async locks can be viewed as
36/// supporting two operations:
37///
38/// - **Read operations**: Provide immutable access (`&T`) to the data
39/// - **Write operations**: Provide mutable access (`&mut T`) to the data
40///
41/// For exclusive async locks (Mutex), both read and write operations
42/// acquire the same exclusive lock, but the API clearly indicates the
43/// intended usage. For read-write async locks (RwLock), read operations
44/// use shared locks while write operations use exclusive locks.
45///
46/// This design enables:
47/// - Unified API across different async lock types
48/// - Clear semantic distinction between read and write operations
49/// - Generic async code that works with any lock type
50/// - Performance optimization through appropriate lock selection
51/// - Non-blocking async operations
52///
53/// # Performance Characteristics
54///
55/// Different async lock implementations have different performance
56/// characteristics:
57///
58/// ## Mutex-based async locks (ArcAsyncMutex, AsyncMutex)
59/// - `read`: Acquires exclusive lock, same performance as write
60/// - `write`: Acquires exclusive lock, same performance as read
61/// - **Use case**: When you need exclusive access or don't know access
62///   patterns
63///
64/// ## RwLock-based async locks (ArcAsyncRwLock, AsyncRwLock)
65/// - `read`: Acquires shared lock, allows concurrent readers
66/// - `write`: Acquires exclusive lock, blocks all other operations
67/// - **Use case**: Read-heavy async workloads where multiple readers can
68///   proceed 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 AsyncLock<T: ?Sized> {
78    /// Acquires a read lock asynchronously and executes a closure
79    ///
80    /// This method awaits until a read lock can be acquired without
81    /// blocking the thread, then executes the provided closure with
82    /// immutable access to the protected data. For exclusive async
83    /// locks (Mutex), this acquires the same exclusive lock as write
84    /// operations. For read-write async locks (RwLock), this acquires
85    /// a 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
91    ///   output
92    /// - **Condition checking**: Evaluating predicates without modification
93    /// - **Logging and debugging**: Accessing data for diagnostic purposes
94    ///
95    /// # Performance Notes
96    ///
97    /// - **Mutex-based async locks**: Same performance as write operations
98    /// - **RwLock-based async locks**: Allows concurrent readers, better
99    ///   for read-heavy async workloads
100    ///
101    /// # Arguments
102    ///
103    /// * `f` - Closure that receives an immutable reference (`&T`) to
104    ///   the protected data
105    ///
106    /// # Returns
107    ///
108    /// Returns a future that resolves to the result produced by the closure
109    ///
110    /// # Example
111    ///
112    /// ```rust
113    /// use qubit_lock::lock::{AsyncLock, ArcAsyncRwLock};
114    ///
115    /// let rt = tokio::runtime::Builder::new_current_thread()
116    ///     .enable_all()
117    ///     .build()
118    ///     .unwrap();
119    /// rt.block_on(async {
120    ///     let lock = ArcAsyncRwLock::new(vec![1, 2, 3]);
121    ///
122    ///     // Read operation - allows concurrent readers with RwLock
123    ///     let len = lock.read(|data| data.len()).await;
124    ///     assert_eq!(len, 3);
125    ///
126    ///     // Multiple concurrent readers possible with RwLock
127    ///     let sum = lock.read(|data|
128    ///         data.iter().sum::<i32>()
129    ///     ).await;
130    ///     assert_eq!(sum, 6);
131    /// });
132    /// ```
133    fn read<R, F>(&self, f: F) -> impl Future<Output = R> + Send
134    where
135        F: FnOnce(&T) -> R + Send,
136        R: Send;
137
138    /// Acquires a write lock asynchronously and executes a closure
139    ///
140    /// This method awaits until a write lock can be acquired without
141    /// blocking the thread, then executes the provided closure with
142    /// mutable access to the protected data. For all async lock types,
143    /// this acquires an exclusive lock that blocks all other operations
144    /// 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 async lock types**: Exclusive access, blocks all other
156    ///   operations
157    /// - **RwLock advantage**: Only blocks during actual writes, not reads
158    ///
159    /// # Arguments
160    ///
161    /// * `f` - Closure that receives a mutable reference (`&mut T`) to
162    ///   the protected data
163    ///
164    /// # Returns
165    ///
166    /// Returns a future that resolves to the result produced by the closure
167    ///
168    /// # Example
169    ///
170    /// ```rust
171    /// use qubit_lock::lock::{AsyncLock, ArcAsyncRwLock};
172    ///
173    /// let rt = tokio::runtime::Builder::new_current_thread()
174    ///     .enable_all()
175    ///     .build()
176    ///     .unwrap();
177    /// rt.block_on(async {
178    ///     let lock = ArcAsyncRwLock::new(vec![1, 2, 3]);
179    ///
180    ///     // Write operation - exclusive access
181    ///     lock.write(|data| {
182    ///         data.push(4);
183    ///         data.sort();
184    ///     }).await;
185    ///
186    ///     // Verify the changes
187    ///     let result = lock.read(|data| data.clone()).await;
188    ///     assert_eq!(result, vec![1, 2, 3, 4]);
189    /// });
190    /// ```
191    fn write<R, F>(&self, f: F) -> impl Future<Output = R> + Send
192    where
193        F: FnOnce(&mut T) -> R + Send,
194        R: Send;
195
196    /// Attempts to acquire a read lock without waiting
197    ///
198    /// This method tries to acquire a read lock immediately. If the lock
199    /// is currently held by another task in write mode, it returns `None`
200    /// without waiting. Otherwise, it executes the closure and returns
201    /// `Some` containing the result.
202    ///
203    /// # Arguments
204    ///
205    /// * `f` - Closure that receives an immutable reference (`&T`) to
206    ///   the protected data if the lock is successfully acquired
207    ///
208    /// # Returns
209    ///
210    /// * `Some(R)` - If the lock was acquired and closure executed
211    /// * `None` - If the lock is currently held in write mode
212    ///
213    /// # Example
214    ///
215    /// ```rust
216    /// use qubit_lock::lock::{AsyncLock, ArcAsyncRwLock};
217    ///
218    /// let lock = ArcAsyncRwLock::new(42);
219    /// if let Some(value) = lock.try_read(|data| *data) {
220    ///     println!("Got value: {}", value);
221    /// } else {
222    ///     println!("Lock is busy with write operation");
223    /// }
224    /// ```
225    fn try_read<R, F>(&self, f: F) -> Option<R>
226    where
227        F: FnOnce(&T) -> R;
228
229    /// Attempts to acquire a write lock without waiting
230    ///
231    /// This method tries to acquire a write lock immediately. If the lock
232    /// is currently held by another task (in either read or write mode),
233    /// it returns `None` without waiting. Otherwise, it executes the
234    /// closure and returns `Some` containing the result.
235    ///
236    /// # Arguments
237    ///
238    /// * `f` - Closure that receives a mutable reference (`&mut T`) to
239    ///   the protected data if the lock is successfully acquired
240    ///
241    /// # Returns
242    ///
243    /// * `Some(R)` - If the lock was acquired and closure executed
244    /// * `None` - If the lock is currently held by another task
245    ///
246    /// # Example
247    ///
248    /// ```rust
249    /// use qubit_lock::lock::{AsyncLock, ArcAsyncMutex};
250    ///
251    /// let lock = ArcAsyncMutex::new(42);
252    /// if let Some(result) = lock.try_write(|data| {
253    ///     *data += 1;
254    ///     *data
255    /// }) {
256    ///     println!("New value: {}", result);
257    /// } else {
258    ///     println!("Lock is busy");
259    /// }
260    /// ```
261    fn try_write<R, F>(&self, f: F) -> Option<R>
262    where
263        F: FnOnce(&mut T) -> R;
264}
265
266/// Asynchronous mutex implementation for tokio::sync::Mutex
267///
268/// This implementation uses Tokio's `Mutex` type to provide an
269/// asynchronous lock that can be awaited without blocking threads.
270/// Both read and write operations acquire the same exclusive lock,
271/// ensuring thread safety at the cost of concurrent access.
272///
273/// # Type Parameters
274///
275/// * `T` - The type of data protected by the lock
276///
277/// # Author
278///
279/// Haixing Hu
280impl<T: ?Sized + Send> AsyncLock<T> for AsyncMutex<T> {
281    /// Acquires the mutex and executes a read-only closure.
282    ///
283    /// # Arguments
284    ///
285    /// * `f` - Closure receiving immutable access to the protected value.
286    ///
287    /// # Returns
288    ///
289    /// A future resolving to the value returned by `f`.
290    #[inline]
291    async fn read<R, F>(&self, f: F) -> R
292    where
293        F: FnOnce(&T) -> R + Send,
294        R: Send,
295    {
296        let guard = self.lock().await;
297        f(&*guard)
298    }
299
300    /// Acquires the mutex and executes a mutable closure.
301    ///
302    /// # Arguments
303    ///
304    /// * `f` - Closure receiving mutable access to the protected value.
305    ///
306    /// # Returns
307    ///
308    /// A future resolving to the value returned by `f`.
309    #[inline]
310    async fn write<R, F>(&self, f: F) -> R
311    where
312        F: FnOnce(&mut T) -> R + Send,
313        R: Send,
314    {
315        let mut guard = self.lock().await;
316        f(&mut *guard)
317    }
318
319    /// Attempts to acquire the mutex without waiting for a read-only closure.
320    ///
321    /// # Arguments
322    ///
323    /// * `f` - Closure receiving immutable access when the mutex is acquired.
324    ///
325    /// # Returns
326    ///
327    /// `Some(result)` if the mutex is acquired, or `None` if it is busy.
328    #[inline]
329    fn try_read<R, F>(&self, f: F) -> Option<R>
330    where
331        F: FnOnce(&T) -> R,
332    {
333        if let Ok(guard) = self.try_lock() {
334            Some(f(&*guard))
335        } else {
336            None
337        }
338    }
339
340    /// Attempts to acquire the mutex without waiting for a mutable closure.
341    ///
342    /// # Arguments
343    ///
344    /// * `f` - Closure receiving mutable access when the mutex is acquired.
345    ///
346    /// # Returns
347    ///
348    /// `Some(result)` if the mutex is acquired, or `None` if it is busy.
349    #[inline]
350    fn try_write<R, F>(&self, f: F) -> Option<R>
351    where
352        F: FnOnce(&mut T) -> R,
353    {
354        if let Ok(mut guard) = self.try_lock() {
355            Some(f(&mut *guard))
356        } else {
357            None
358        }
359    }
360}
361
362/// Asynchronous read-write lock implementation for tokio::sync::RwLock
363///
364/// This implementation uses Tokio's `RwLock` type to provide an
365/// asynchronous read-write lock that supports multiple concurrent
366/// readers or a single writer without blocking threads. Read operations
367/// use shared locks allowing concurrent readers, while write operations
368/// use exclusive locks that block all other operations.
369///
370/// # Type Parameters
371///
372/// * `T` - The type of data protected by the lock
373///
374/// # Author
375///
376/// Haixing Hu
377impl<T: ?Sized + Send + Sync> AsyncLock<T> for AsyncRwLock<T> {
378    /// Acquires a shared read lock and executes a closure.
379    ///
380    /// # Arguments
381    ///
382    /// * `f` - Closure receiving immutable access to the protected value.
383    ///
384    /// # Returns
385    ///
386    /// A future resolving to the value returned by `f`.
387    #[inline]
388    async fn read<R, F>(&self, f: F) -> R
389    where
390        F: FnOnce(&T) -> R + Send,
391        R: Send,
392    {
393        let guard = self.read().await;
394        f(&*guard)
395    }
396
397    /// Acquires an exclusive write lock and executes a closure.
398    ///
399    /// # Arguments
400    ///
401    /// * `f` - Closure receiving mutable access to the protected value.
402    ///
403    /// # Returns
404    ///
405    /// A future resolving to the value returned by `f`.
406    #[inline]
407    async fn write<R, F>(&self, f: F) -> R
408    where
409        F: FnOnce(&mut T) -> R + Send,
410        R: Send,
411    {
412        let mut guard = self.write().await;
413        f(&mut *guard)
414    }
415
416    /// Attempts to acquire a shared read lock without waiting.
417    ///
418    /// # Arguments
419    ///
420    /// * `f` - Closure receiving immutable access when the read lock is
421    ///   acquired.
422    ///
423    /// # Returns
424    ///
425    /// `Some(result)` if a read lock is acquired, or `None` if it is busy.
426    #[inline]
427    fn try_read<R, F>(&self, f: F) -> Option<R>
428    where
429        F: FnOnce(&T) -> R,
430    {
431        if let Ok(guard) = self.try_read() {
432            Some(f(&*guard))
433        } else {
434            None
435        }
436    }
437
438    /// Attempts to acquire an exclusive write lock without waiting.
439    ///
440    /// # Arguments
441    ///
442    /// * `f` - Closure receiving mutable access when the write lock is
443    ///   acquired.
444    ///
445    /// # Returns
446    ///
447    /// `Some(result)` if a write lock is acquired, or `None` if it is busy.
448    #[inline]
449    fn try_write<R, F>(&self, f: F) -> Option<R>
450    where
451        F: FnOnce(&mut T) -> R,
452    {
453        if let Ok(mut guard) = self.try_write() {
454            Some(f(&mut *guard))
455        } else {
456            None
457        }
458    }
459}