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}