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}