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}