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 parking_lot 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::Arc;
18
19use parking_lot::RwLock;
20
21use crate::lock::{
22    Lock,
23    TryLockError,
24};
25
26/// Parking-lot read-write lock wrapper.
27///
28/// Provides an Arc-wrapped [`parking_lot::RwLock`] for synchronous shared state.
29/// Read operations can execute concurrently, while write operations have
30/// 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/// - Synchronously acquires locks, may block threads
38/// - Thread-safe, supports multi-threaded sharing
39/// - Automatic lock management through RAII ensures proper lock
40///   release
41/// - Does not use lock poisoning; panic while holding the lock does not make
42///   future acquisitions fail
43/// - Implements [`Deref`] and [`AsRef`] to expose the underlying
44///   [`parking_lot::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::{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 parking_lot 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::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 parking_lot 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 parking_lot 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    /// # Example
147    ///
148    /// ```rust
149    /// use qubit_lock::{ArcRwLock, Lock};
150    ///
151    /// let data = ArcRwLock::new(vec![1, 2, 3]);
152    ///
153    /// let length = data.read(|v| v.len());
154    /// println!("Vector length: {}", length);
155    /// ```
156    #[inline]
157    fn read<R, F>(&self, f: F) -> R
158    where
159        F: FnOnce(&T) -> R,
160    {
161        let guard = self.inner.read();
162        f(&*guard)
163    }
164
165    /// Acquires a write lock and executes an operation
166    ///
167    /// Synchronously acquires the write lock, executes the provided
168    /// closure, and then automatically releases the lock. Write
169    /// operations have exclusive access, mutually exclusive with
170    /// read operations.
171    ///
172    /// # Arguments
173    ///
174    /// * `f` - The closure to be executed while holding the write
175    ///   lock, can modify data
176    ///
177    /// # Returns
178    ///
179    /// Returns the result of executing the closure
180    ///
181    /// # Example
182    ///
183    /// ```rust
184    /// use qubit_lock::{ArcRwLock, Lock};
185    ///
186    /// let data = ArcRwLock::new(vec![1, 2, 3]);
187    ///
188    /// data.write(|v| {
189    ///     v.push(4);
190    ///     println!("Added element, new length: {}", v.len());
191    /// });
192    /// ```
193    #[inline]
194    fn write<R, F>(&self, f: F) -> R
195    where
196        F: FnOnce(&mut T) -> R,
197    {
198        let mut guard = self.inner.write();
199        f(&mut *guard)
200    }
201
202    /// Attempts to acquire a read lock without blocking
203    ///
204    /// Attempts to immediately acquire the read lock. If the lock is
205    /// unavailable, returns a detailed error. This is a non-blocking operation.
206    ///
207    /// # Arguments
208    ///
209    /// * `f` - The closure to be executed while holding the read lock
210    ///
211    /// # Returns
212    ///
213    /// * `Ok(R)` - If the lock was successfully acquired and the closure executed
214    /// * `Err(TryLockError::WouldBlock)` - If the lock is currently held in write mode
215    ///
216    /// # Example
217    ///
218    /// ```rust
219    /// use qubit_lock::{ArcRwLock, Lock};
220    ///
221    /// let data = ArcRwLock::new(vec![1, 2, 3]);
222    ///
223    /// if let Ok(length) = data.try_read(|v| v.len()) {
224    ///     println!("Vector length: {}", length);
225    /// } else {
226    ///     println!("Lock is unavailable");
227    /// }
228    /// ```
229    #[inline]
230    fn try_read<R, F>(&self, f: F) -> Result<R, TryLockError>
231    where
232        F: FnOnce(&T) -> R,
233    {
234        self.inner
235            .try_read()
236            .map(|guard| f(&*guard))
237            .ok_or(TryLockError::WouldBlock)
238    }
239
240    /// Attempts to acquire a write lock without blocking
241    ///
242    /// Attempts to immediately acquire the write lock. If the lock is
243    /// unavailable, returns a detailed error. This is a non-blocking operation.
244    ///
245    /// # Arguments
246    ///
247    /// * `f` - The closure to be executed while holding the write lock
248    ///
249    /// # Returns
250    ///
251    /// * `Ok(R)` - If the lock was successfully acquired and the closure executed
252    /// * `Err(TryLockError::WouldBlock)` - If the lock is unavailable
253    ///
254    /// # Example
255    ///
256    /// ```rust
257    /// use qubit_lock::{ArcRwLock, Lock};
258    ///
259    /// let data = ArcRwLock::new(vec![1, 2, 3]);
260    ///
261    /// if let Ok(new_length) = data.try_write(|v| {
262    ///     v.push(4);
263    ///     v.len()
264    /// }) {
265    ///     println!("New length: {}", new_length);
266    /// } else {
267    ///     println!("Lock is unavailable");
268    /// }
269    /// ```
270    #[inline]
271    fn try_write<R, F>(&self, f: F) -> Result<R, TryLockError>
272    where
273        F: FnOnce(&mut T) -> R,
274    {
275        self.inner
276            .try_write()
277            .map(|mut guard| f(&mut *guard))
278            .ok_or(TryLockError::WouldBlock)
279    }
280}
281
282impl<T> From<T> for ArcRwLock<T> {
283    /// Creates an Arc-wrapped parking_lot read-write lock from a value.
284    ///
285    /// # Arguments
286    ///
287    /// * `value` - The value to protect.
288    ///
289    /// # Returns
290    ///
291    /// A new [`ArcRwLock`] protecting `value`.
292    #[inline]
293    fn from(value: T) -> Self {
294        Self::new(value)
295    }
296}
297
298impl<T: Default> Default for ArcRwLock<T> {
299    /// Creates an Arc-wrapped parking_lot read-write lock containing
300    /// `T::default()`.
301    ///
302    /// # Returns
303    ///
304    /// A new [`ArcRwLock`] protecting the default value for `T`.
305    #[inline]
306    fn default() -> Self {
307        Self::new(T::default())
308    }
309}
310
311impl<T> Clone for ArcRwLock<T> {
312    /// Clones the synchronous read-write lock
313    ///
314    /// Creates a new `ArcRwLock` instance that shares the same
315    /// underlying lock with the original instance. This allows
316    /// multiple threads to hold references to the same lock
317    /// simultaneously.
318    ///
319    /// # Returns
320    ///
321    /// A new handle sharing the same underlying read-write lock and protected
322    /// value.
323    #[inline]
324    fn clone(&self) -> Self {
325        Self {
326            inner: self.inner.clone(),
327        }
328    }
329}