Skip to main content

qubit_lock/lock/
arc_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//! # Synchronous Read-Write Lock Wrapper
11//!
12//! Provides an Arc-wrapped synchronous read-write lock for protecting
13//! shared data with multiple concurrent readers or a single writer.
14//!
15
16use std::ops::Deref;
17use std::sync::{
18    Arc,
19    RwLock,
20};
21
22use crate::lock::{
23    Lock,
24    TryLockError,
25};
26
27/// Synchronous Read-Write Lock Wrapper
28///
29/// Provides an encapsulation of synchronous read-write lock,
30/// supporting multiple read operations or a single write operation.
31/// Read operations can execute concurrently, while write operations
32/// have exclusive access.
33///
34/// # Features
35///
36/// - Supports multiple concurrent read operations
37/// - Write operations have exclusive access, mutually exclusive with
38///   read operations
39/// - Synchronously acquires locks, may block threads
40/// - Thread-safe, supports multi-threaded sharing
41/// - Automatic lock management through RAII ensures proper lock
42///   release
43/// - Implements [`Deref`] and [`AsRef`] to expose the underlying
44///   [`std::sync::RwLock`] API when guard-based access is needed
45///
46/// # Use Cases
47///
48/// Suitable for read-heavy scenarios such as caching, configuration
49/// management, etc.
50///
51/// # Usage Example
52///
53/// ```rust
54/// use qubit_lock::lock::{ArcRwLock, Lock};
55///
56/// let data = ArcRwLock::new(String::from("Hello"));
57///
58/// // Multiple read operations can execute concurrently
59/// data.read(|s| {
60///     println!("Read: {}", s);
61/// });
62///
63/// // Write operations have exclusive access
64/// data.write(|s| {
65///     s.push_str(" World!");
66///     println!("Write: {}", s);
67/// });
68/// ```
69///
70///
71pub struct ArcRwLock<T> {
72    /// Shared standard read-write lock protecting the wrapped value.
73    inner: Arc<RwLock<T>>,
74}
75
76impl<T> ArcRwLock<T> {
77    /// Creates a new synchronous read-write lock
78    ///
79    /// # Arguments
80    ///
81    /// * `data` - The data to be protected
82    ///
83    /// # Returns
84    ///
85    /// Returns a new `ArcRwLock` instance
86    ///
87    /// # Example
88    ///
89    /// ```rust
90    /// use qubit_lock::lock::ArcRwLock;
91    ///
92    /// let rw_lock = ArcRwLock::new(vec![1, 2, 3]);
93    /// ```
94    #[inline]
95    pub fn new(data: T) -> Self {
96        Self {
97            inner: Arc::new(RwLock::new(data)),
98        }
99    }
100}
101
102impl<T> AsRef<RwLock<T>> for ArcRwLock<T> {
103    /// Returns a reference to the underlying standard read-write lock.
104    ///
105    /// This is useful when callers need guard-based APIs such as
106    /// [`RwLock::read`] or [`RwLock::write`] instead of the closure-based
107    /// [`Lock`] methods.
108    #[inline]
109    fn as_ref(&self) -> &RwLock<T> {
110        self.inner.as_ref()
111    }
112}
113
114impl<T> Deref for ArcRwLock<T> {
115    type Target = RwLock<T>;
116
117    /// Dereferences this wrapper to the underlying standard read-write lock.
118    ///
119    /// When [`Lock`] is in scope, `read` and `write` with closure arguments
120    /// still call the trait methods on this wrapper. Use explicit
121    /// dereferencing or [`AsRef::as_ref`] when you want the native guard-based
122    /// [`RwLock`] methods.
123    #[inline]
124    fn deref(&self) -> &Self::Target {
125        self.inner.as_ref()
126    }
127}
128
129impl<T> Lock<T> for ArcRwLock<T> {
130    /// Acquires a read lock and executes an operation
131    ///
132    /// Synchronously acquires the read lock, executes the provided
133    /// closure, and then automatically releases the lock. Multiple
134    /// read operations can execute concurrently, providing better
135    /// performance for read-heavy workloads.
136    ///
137    /// # Arguments
138    ///
139    /// * `f` - The closure to be executed while holding the read
140    ///   lock, can only read data
141    ///
142    /// # Returns
143    ///
144    /// Returns the result of executing the closure
145    ///
146    /// # Panics
147    ///
148    /// Panics if the underlying standard read-write lock is poisoned.
149    ///
150    /// # Example
151    ///
152    /// ```rust
153    /// use qubit_lock::lock::{ArcRwLock, Lock};
154    ///
155    /// let data = ArcRwLock::new(vec![1, 2, 3]);
156    ///
157    /// let length = data.read(|v| v.len());
158    /// println!("Vector length: {}", length);
159    /// ```
160    #[inline]
161    fn read<R, F>(&self, f: F) -> R
162    where
163        F: FnOnce(&T) -> R,
164    {
165        let guard = self.inner.read().unwrap();
166        f(&*guard)
167    }
168
169    /// Acquires a write lock and executes an operation
170    ///
171    /// Synchronously acquires the write lock, executes the provided
172    /// closure, and then automatically releases the lock. Write
173    /// operations have exclusive access, mutually exclusive with
174    /// read operations.
175    ///
176    /// # Arguments
177    ///
178    /// * `f` - The closure to be executed while holding the write
179    ///   lock, can modify data
180    ///
181    /// # Returns
182    ///
183    /// Returns the result of executing the closure
184    ///
185    /// # Panics
186    ///
187    /// Panics if the underlying standard read-write lock is poisoned.
188    ///
189    /// # Example
190    ///
191    /// ```rust
192    /// use qubit_lock::lock::{ArcRwLock, Lock};
193    ///
194    /// let data = ArcRwLock::new(vec![1, 2, 3]);
195    ///
196    /// data.write(|v| {
197    ///     v.push(4);
198    ///     println!("Added element, new length: {}", v.len());
199    /// });
200    /// ```
201    #[inline]
202    fn write<R, F>(&self, f: F) -> R
203    where
204        F: FnOnce(&mut T) -> R,
205    {
206        let mut guard = self.inner.write().unwrap();
207        f(&mut *guard)
208    }
209
210    /// Attempts to acquire a read lock without blocking
211    ///
212    /// Attempts to immediately acquire the read lock. If the lock is
213    /// unavailable, returns a detailed error. This is a non-blocking operation.
214    ///
215    /// # Arguments
216    ///
217    /// * `f` - The closure to be executed while holding the read lock
218    ///
219    /// # Returns
220    ///
221    /// * `Ok(R)` - If the lock was successfully acquired and the closure executed
222    /// * `Err(TryLockError::WouldBlock)` - If the lock is currently held in write mode
223    /// * `Err(TryLockError::Poisoned)` - If the lock is poisoned
224    ///
225    /// # Example
226    ///
227    /// ```rust
228    /// use qubit_lock::lock::{ArcRwLock, Lock};
229    ///
230    /// let data = ArcRwLock::new(vec![1, 2, 3]);
231    ///
232    /// if let Ok(length) = data.try_read(|v| v.len()) {
233    ///     println!("Vector length: {}", length);
234    /// } else {
235    ///     println!("Lock is unavailable");
236    /// }
237    /// ```
238    #[inline]
239    fn try_read<R, F>(&self, f: F) -> Result<R, TryLockError>
240    where
241        F: FnOnce(&T) -> R,
242    {
243        match self.inner.try_read() {
244            Ok(guard) => Ok(f(&*guard)),
245            Err(std::sync::TryLockError::WouldBlock) => Err(TryLockError::WouldBlock),
246            Err(std::sync::TryLockError::Poisoned(_)) => Err(TryLockError::Poisoned),
247        }
248    }
249
250    /// Attempts to acquire a write lock without blocking
251    ///
252    /// Attempts to immediately acquire the write lock. If the lock is
253    /// unavailable, returns a detailed error. This is a non-blocking operation.
254    ///
255    /// # Arguments
256    ///
257    /// * `f` - The closure to be executed while holding the write lock
258    ///
259    /// # Returns
260    ///
261    /// * `Ok(R)` - If the lock was successfully acquired and the closure executed
262    /// * `Err(TryLockError::WouldBlock)` - If the lock is currently held by another thread
263    /// * `Err(TryLockError::Poisoned)` - If the lock is poisoned
264    ///
265    /// # Example
266    ///
267    /// ```rust
268    /// use qubit_lock::lock::{ArcRwLock, Lock};
269    ///
270    /// let data = ArcRwLock::new(vec![1, 2, 3]);
271    ///
272    /// if let Ok(new_length) = data.try_write(|v| {
273    ///     v.push(4);
274    ///     v.len()
275    /// }) {
276    ///     println!("New length: {}", new_length);
277    /// } else {
278    ///     println!("Lock is unavailable");
279    /// }
280    /// ```
281    #[inline]
282    fn try_write<R, F>(&self, f: F) -> Result<R, TryLockError>
283    where
284        F: FnOnce(&mut T) -> R,
285    {
286        match self.inner.try_write() {
287            Ok(mut guard) => Ok(f(&mut *guard)),
288            Err(std::sync::TryLockError::WouldBlock) => Err(TryLockError::WouldBlock),
289            Err(std::sync::TryLockError::Poisoned(_)) => Err(TryLockError::Poisoned),
290        }
291    }
292}
293
294impl<T> Clone for ArcRwLock<T> {
295    /// Clones the synchronous read-write lock
296    ///
297    /// Creates a new `ArcRwLock` instance that shares the same
298    /// underlying lock with the original instance. This allows
299    /// multiple threads to hold references to the same lock
300    /// simultaneously.
301    ///
302    /// # Returns
303    ///
304    /// A new handle sharing the same underlying read-write lock and protected
305    /// value.
306    #[inline]
307    fn clone(&self) -> Self {
308        Self {
309            inner: self.inner.clone(),
310        }
311    }
312}