Skip to main content

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