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}