Skip to main content

qubit_lock/lock/
arc_async_rw_lock.rs

1/*******************************************************************************
2 *
3 *    Copyright (c) 2025 - 2026.
4 *    Haixing Hu, Qubit Co. Ltd.
5 *
6 *    All rights reserved.
7 *
8 ******************************************************************************/
9//! # Asynchronous Read-Write Lock Wrapper
10//!
11//! Provides an Arc-wrapped asynchronous read-write lock for
12//! protecting shared data with multiple concurrent readers or a
13//! single writer in async environments.
14//!
15//! # Author
16//!
17//! Haixing Hu
18use std::sync::Arc;
19
20use tokio::sync::RwLock as AsyncRwLock;
21
22use crate::lock::{
23    AsyncLock,
24    TryLockError,
25};
26
27/// Asynchronous Read-Write Lock Wrapper
28///
29/// Provides an encapsulation of asynchronous read-write lock,
30/// supporting multiple read operations or a single write operation.
31/// Read operations can execute concurrently, while write operations
32/// have exclusive access.
33///
34/// # Features
35///
36/// - Supports multiple concurrent read operations
37/// - Write operations have exclusive access, mutually exclusive with
38///   read operations
39/// - Asynchronously acquires locks, does not block threads
40/// - Thread-safe, supports multi-threaded sharing
41/// - Automatic lock management through RAII ensures proper lock
42///   release
43///
44/// # Use Cases
45///
46/// Suitable for read-heavy scenarios such as caching, configuration
47/// management, etc.
48///
49/// # Usage Example
50///
51/// ```rust
52/// use qubit_lock::lock::{ArcAsyncRwLock, AsyncLock};
53///
54/// let rt = tokio::runtime::Builder::new_current_thread()
55///     .enable_all()
56///     .build()
57///     .unwrap();
58/// rt.block_on(async {
59///     let data = ArcAsyncRwLock::new(String::from("Hello"));
60///
61///     // Multiple read operations can execute concurrently
62///     data.read(|s| {
63///         println!("Read: {}", s);
64///     }).await;
65///
66///     // Write operations have exclusive access
67///     data.write(|s| {
68///         s.push_str(" World!");
69///         println!("Write: {}", s);
70///     }).await;
71/// });
72/// ```
73///
74/// # Author
75///
76/// Haixing Hu
77///
78pub struct ArcAsyncRwLock<T> {
79    /// Shared Tokio read-write lock protecting the wrapped value.
80    inner: Arc<AsyncRwLock<T>>,
81}
82
83impl<T> ArcAsyncRwLock<T> {
84    /// Creates a new asynchronous read-write lock
85    ///
86    /// # Arguments
87    ///
88    /// * `data` - The data to be protected
89    ///
90    /// # Returns
91    ///
92    /// Returns a new `ArcAsyncRwLock` instance
93    ///
94    /// # Example
95    ///
96    /// ```rust
97    /// use qubit_lock::lock::ArcAsyncRwLock;
98    ///
99    /// let rw_lock = ArcAsyncRwLock::new(vec![1, 2, 3]);
100    /// ```
101    #[inline]
102    pub fn new(data: T) -> Self {
103        Self {
104            inner: Arc::new(AsyncRwLock::new(data)),
105        }
106    }
107}
108
109impl<T> AsyncLock<T> for ArcAsyncRwLock<T>
110where
111    T: Send + Sync,
112{
113    /// Acquires the read lock and executes an operation
114    ///
115    /// Asynchronously acquires the read lock, executes the provided
116    /// closure, and then automatically releases the lock. Multiple
117    /// read operations can execute concurrently.
118    ///
119    /// # Arguments
120    ///
121    /// * `f` - The closure to be executed while holding the read
122    ///   lock, can only read data
123    ///
124    /// # Returns
125    ///
126    /// Returns a future that resolves to the result of executing
127    /// the closure
128    ///
129    /// # Example
130    ///
131    /// ```rust
132    /// use qubit_lock::lock::{ArcAsyncRwLock, AsyncLock};
133    ///
134    /// let rt = tokio::runtime::Builder::new_current_thread()
135    ///     .enable_all()
136    ///     .build()
137    ///     .unwrap();
138    /// rt.block_on(async {
139    ///     let data = ArcAsyncRwLock::new(vec![1, 2, 3]);
140    ///
141    ///     let length = data.read(|v| v.len()).await;
142    ///     println!("Vector length: {}", length);
143    /// });
144    /// ```
145    #[inline]
146    async fn read<R, F>(&self, f: F) -> R
147    where
148        F: FnOnce(&T) -> R + Send,
149        R: Send,
150    {
151        let guard = self.inner.read().await;
152        f(&*guard)
153    }
154
155    /// Acquires the write lock and executes an operation
156    ///
157    /// Asynchronously acquires the write lock, executes the provided
158    /// closure, and then automatically releases the lock. Write
159    /// operations have exclusive access, mutually exclusive with
160    /// read operations.
161    ///
162    /// # Arguments
163    ///
164    /// * `f` - The closure to be executed while holding the write
165    ///   lock, can modify data
166    ///
167    /// # Returns
168    ///
169    /// Returns a future that resolves to the result of executing
170    /// the closure
171    ///
172    /// # Example
173    ///
174    /// ```rust
175    /// use qubit_lock::lock::{ArcAsyncRwLock, AsyncLock};
176    ///
177    /// let rt = tokio::runtime::Builder::new_current_thread()
178    ///     .enable_all()
179    ///     .build()
180    ///     .unwrap();
181    /// rt.block_on(async {
182    ///     let data = ArcAsyncRwLock::new(vec![1, 2, 3]);
183    ///
184    ///     data.write(|v| {
185    ///         v.push(4);
186    ///         println!("Added element, new length: {}", v.len());
187    ///     }).await;
188    /// });
189    /// ```
190    #[inline]
191    async fn write<R, F>(&self, f: F) -> R
192    where
193        F: FnOnce(&mut T) -> R + Send,
194        R: Send,
195    {
196        let mut guard = self.inner.write().await;
197        f(&mut *guard)
198    }
199
200    /// Attempts to acquire the read lock without waiting.
201    ///
202    /// # Arguments
203    ///
204    /// * `f` - Closure receiving immutable access when a read lock is
205    ///   available.
206    ///
207    /// # Returns
208    ///
209    /// `Ok(result)` if a read lock was acquired, or
210    /// [`TryLockError::WouldBlock`] if the lock was busy.
211    #[inline]
212    fn try_read<R, F>(&self, f: F) -> Result<R, TryLockError>
213    where
214        F: FnOnce(&T) -> R,
215    {
216        self.inner
217            .try_read()
218            .map(|guard| f(&*guard))
219            .map_err(|_| TryLockError::WouldBlock)
220    }
221
222    /// Attempts to acquire the write lock without waiting.
223    ///
224    /// # Arguments
225    ///
226    /// * `f` - Closure receiving mutable access when a write lock is available.
227    ///
228    /// # Returns
229    ///
230    /// `Ok(result)` if a write lock was acquired, or
231    /// [`TryLockError::WouldBlock`] if the lock was busy.
232    #[inline]
233    fn try_write<R, F>(&self, f: F) -> Result<R, TryLockError>
234    where
235        F: FnOnce(&mut T) -> R,
236    {
237        self.inner
238            .try_write()
239            .map(|mut guard| f(&mut *guard))
240            .map_err(|_| TryLockError::WouldBlock)
241    }
242}
243
244impl<T> Clone for ArcAsyncRwLock<T> {
245    /// Clones the asynchronous read-write lock
246    ///
247    /// Creates a new `ArcAsyncRwLock` instance that shares the same
248    /// underlying lock with the original instance. This allows
249    /// multiple tasks to hold references to the same lock
250    /// simultaneously.
251    ///
252    /// # Returns
253    ///
254    /// A new handle sharing the same underlying async read-write lock and
255    /// protected value.
256    #[inline]
257    fn clone(&self) -> Self {
258        Self {
259            inner: self.inner.clone(),
260        }
261    }
262}