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