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 [`Deref`] and [`AsRef`] to expose the underlying
45///   [`tokio::sync::RwLock`] API when guard-based access is needed
46///
47/// # Use Cases
48///
49/// Suitable for read-heavy scenarios such as caching, configuration
50/// management, etc.
51///
52/// # Usage Example
53///
54/// ```rust
55/// use qubit_lock::lock::{ArcAsyncRwLock, AsyncLock};
56///
57/// let rt = tokio::runtime::Builder::new_current_thread()
58///     .enable_all()
59///     .build()
60///     .unwrap();
61/// rt.block_on(async {
62///     let data = ArcAsyncRwLock::new(String::from("Hello"));
63///
64///     // Multiple read operations can execute concurrently
65///     data.read(|s| {
66///         println!("Read: {}", s);
67///     }).await;
68///
69///     // Write operations have exclusive access
70///     data.write(|s| {
71///         s.push_str(" World!");
72///         println!("Write: {}", s);
73///     }).await;
74/// });
75/// ```
76///
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> AsRef<AsyncRwLock<T>> for ArcAsyncRwLock<T> {
110    /// Returns a reference to the underlying Tokio read-write lock.
111    ///
112    /// This is useful when callers need guard-based APIs such as
113    /// [`AsyncRwLock::read`] or [`AsyncRwLock::write`] instead of the
114    /// closure-based [`AsyncLock`] methods.
115    #[inline]
116    fn as_ref(&self) -> &AsyncRwLock<T> {
117        self.inner.as_ref()
118    }
119}
120
121impl<T> Deref for ArcAsyncRwLock<T> {
122    type Target = AsyncRwLock<T>;
123
124    /// Dereferences this wrapper to the underlying Tokio read-write lock.
125    ///
126    /// When [`AsyncLock`] is in scope, `read` and `write` with closure
127    /// arguments still call the trait methods on this wrapper. Use explicit
128    /// dereferencing or [`AsRef::as_ref`] when you want the native guard-based
129    /// [`AsyncRwLock`] methods.
130    #[inline]
131    fn deref(&self) -> &Self::Target {
132        self.inner.as_ref()
133    }
134}
135
136impl<T> AsyncLock<T> for ArcAsyncRwLock<T>
137where
138    T: Send + Sync,
139{
140    /// Acquires the read lock and executes an operation
141    ///
142    /// Asynchronously acquires the read lock, executes the provided
143    /// closure, and then automatically releases the lock. Multiple
144    /// read operations can execute concurrently.
145    ///
146    /// # Arguments
147    ///
148    /// * `f` - The closure to be executed while holding the read
149    ///   lock, can only read data
150    ///
151    /// # Returns
152    ///
153    /// Returns a future that resolves to the result of executing
154    /// the closure
155    ///
156    /// # Example
157    ///
158    /// ```rust
159    /// use qubit_lock::lock::{ArcAsyncRwLock, AsyncLock};
160    ///
161    /// let rt = tokio::runtime::Builder::new_current_thread()
162    ///     .enable_all()
163    ///     .build()
164    ///     .unwrap();
165    /// rt.block_on(async {
166    ///     let data = ArcAsyncRwLock::new(vec![1, 2, 3]);
167    ///
168    ///     let length = data.read(|v| v.len()).await;
169    ///     println!("Vector length: {}", length);
170    /// });
171    /// ```
172    #[inline]
173    async fn read<R, F>(&self, f: F) -> R
174    where
175        F: FnOnce(&T) -> R + Send,
176        R: Send,
177    {
178        let guard = self.inner.read().await;
179        f(&*guard)
180    }
181
182    /// Acquires the write lock and executes an operation
183    ///
184    /// Asynchronously acquires the write lock, executes the provided
185    /// closure, and then automatically releases the lock. Write
186    /// operations have exclusive access, mutually exclusive with
187    /// read operations.
188    ///
189    /// # Arguments
190    ///
191    /// * `f` - The closure to be executed while holding the write
192    ///   lock, can modify data
193    ///
194    /// # Returns
195    ///
196    /// Returns a future that resolves to the result of executing
197    /// the closure
198    ///
199    /// # Example
200    ///
201    /// ```rust
202    /// use qubit_lock::lock::{ArcAsyncRwLock, AsyncLock};
203    ///
204    /// let rt = tokio::runtime::Builder::new_current_thread()
205    ///     .enable_all()
206    ///     .build()
207    ///     .unwrap();
208    /// rt.block_on(async {
209    ///     let data = ArcAsyncRwLock::new(vec![1, 2, 3]);
210    ///
211    ///     data.write(|v| {
212    ///         v.push(4);
213    ///         println!("Added element, new length: {}", v.len());
214    ///     }).await;
215    /// });
216    /// ```
217    #[inline]
218    async fn write<R, F>(&self, f: F) -> R
219    where
220        F: FnOnce(&mut T) -> R + Send,
221        R: Send,
222    {
223        let mut guard = self.inner.write().await;
224        f(&mut *guard)
225    }
226
227    /// Attempts to acquire the read lock without waiting.
228    ///
229    /// # Arguments
230    ///
231    /// * `f` - Closure receiving immutable access when a read lock is
232    ///   available.
233    ///
234    /// # Returns
235    ///
236    /// `Ok(result)` if a read lock was acquired, or
237    /// [`TryLockError::WouldBlock`] if the lock was busy.
238    #[inline]
239    fn try_read<R, F>(&self, f: F) -> Result<R, TryLockError>
240    where
241        F: FnOnce(&T) -> R,
242    {
243        self.inner
244            .try_read()
245            .map(|guard| f(&*guard))
246            .map_err(|_| TryLockError::WouldBlock)
247    }
248
249    /// Attempts to acquire the write lock without waiting.
250    ///
251    /// # Arguments
252    ///
253    /// * `f` - Closure receiving mutable access when a write lock is available.
254    ///
255    /// # Returns
256    ///
257    /// `Ok(result)` if a write lock was acquired, or
258    /// [`TryLockError::WouldBlock`] if the lock was busy.
259    #[inline]
260    fn try_write<R, F>(&self, f: F) -> Result<R, TryLockError>
261    where
262        F: FnOnce(&mut T) -> R,
263    {
264        self.inner
265            .try_write()
266            .map(|mut guard| f(&mut *guard))
267            .map_err(|_| TryLockError::WouldBlock)
268    }
269}
270
271impl<T> Clone for ArcAsyncRwLock<T> {
272    /// Clones the asynchronous read-write lock
273    ///
274    /// Creates a new `ArcAsyncRwLock` instance that shares the same
275    /// underlying lock with the original instance. This allows
276    /// multiple tasks to hold references to the same lock
277    /// simultaneously.
278    ///
279    /// # Returns
280    ///
281    /// A new handle sharing the same underlying async read-write lock and
282    /// protected value.
283    #[inline]
284    fn clone(&self) -> Self {
285        Self {
286            inner: self.inner.clone(),
287        }
288    }
289}