• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

qubit-ltd / rust-concurrent / 53a83ac2-4e27-4ade-80fc-d8a40935e243

10 Apr 2026 05:27PM UTC coverage: 98.258%. Remained the same
53a83ac2-4e27-4ade-80fc-d8a40935e243

push

circleci

Haixing-Hu
chore(cargo): bump package version to 0.2.2

- update Cargo.toml package version to 0.2.2 to reflect the new release
- refresh Cargo.lock so dependency revisions match the updated toolkit versions

564 of 574 relevant lines covered (98.26%)

104.04 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.33
/src/lock/async_lock.rs
1
/*******************************************************************************
2
 *
3
 *    Copyright (c) 2025 - 2026.
4
 *    Haixing Hu, Qubit Co. Ltd.
5
 *
6
 *    All rights reserved.
7
 *
8
 ******************************************************************************/
9
//! # Asynchronous Lock Trait
10
//!
11
//! Defines an asynchronous lock abstraction that supports acquiring
12
//! locks without blocking threads.
13
//!
14
//! # Author
15
//!
16
//! Haixing Hu
17
use std::future::Future;
18

19
use tokio::sync::{
20
    Mutex as AsyncMutex,
21
    RwLock as AsyncRwLock,
22
};
23

24
/// Unified asynchronous lock trait
25
///
26
/// Provides a unified interface for different types of asynchronous
27
/// locks, supporting both read and write operations. This trait allows
28
/// locks to be used in async contexts through closures, avoiding the
29
/// complexity of explicitly managing lock guards and their lifetimes.
30
///
31
/// # Design Philosophy
32
///
33
/// This trait unifies both exclusive async locks (like `tokio::sync::Mutex`)
34
/// and read-write async locks (like `tokio::sync::RwLock`) under a single
35
/// interface. The key insight is that all async locks can be viewed as
36
/// supporting two operations:
37
///
38
/// - **Read operations**: Provide immutable access (`&T`) to the data
39
/// - **Write operations**: Provide mutable access (`&mut T`) to the data
40
///
41
/// For exclusive async locks (Mutex), both read and write operations
42
/// acquire the same exclusive lock, but the API clearly indicates the
43
/// intended usage. For read-write async locks (RwLock), read operations
44
/// use shared locks while write operations use exclusive locks.
45
///
46
/// This design enables:
47
/// - Unified API across different async lock types
48
/// - Clear semantic distinction between read and write operations
49
/// - Generic async code that works with any lock type
50
/// - Performance optimization through appropriate lock selection
51
/// - Non-blocking async operations
52
///
53
/// # Performance Characteristics
54
///
55
/// Different async lock implementations have different performance
56
/// characteristics:
57
///
58
/// ## Mutex-based async locks (ArcAsyncMutex, AsyncMutex)
59
/// - `read`: Acquires exclusive lock, same performance as write
60
/// - `write`: Acquires exclusive lock, same performance as read
61
/// - **Use case**: When you need exclusive access or don't know access
62
///   patterns
63
///
64
/// ## RwLock-based async locks (ArcAsyncRwLock, AsyncRwLock)
65
/// - `read`: Acquires shared lock, allows concurrent readers
66
/// - `write`: Acquires exclusive lock, blocks all other operations
67
/// - **Use case**: Read-heavy async workloads where multiple readers can
68
///   proceed concurrently
69
///
70
/// # Type Parameters
71
///
72
/// * `T` - The type of data protected by the lock
73
///
74
/// # Author
75
///
76
/// Haixing Hu
77
pub trait AsyncLock<T: ?Sized> {
78
    /// Acquires a read lock asynchronously and executes a closure
79
    ///
80
    /// This method awaits until a read lock can be acquired without
81
    /// blocking the thread, then executes the provided closure with
82
    /// immutable access to the protected data. For exclusive async
83
    /// locks (Mutex), this acquires the same exclusive lock as write
84
    /// operations. For read-write async locks (RwLock), this acquires
85
    /// a shared lock allowing concurrent readers.
86
    ///
87
    /// # Use Cases
88
    ///
89
    /// - **Data inspection**: Reading values, checking state, validation
90
    /// - **Read-only operations**: Computing derived values, formatting
91
    ///   output
92
    /// - **Condition checking**: Evaluating predicates without modification
93
    /// - **Logging and debugging**: Accessing data for diagnostic purposes
94
    ///
95
    /// # Performance Notes
96
    ///
97
    /// - **Mutex-based async locks**: Same performance as write operations
98
    /// - **RwLock-based async locks**: Allows concurrent readers, better
99
    ///   for read-heavy async workloads
100
    ///
101
    /// # Arguments
102
    ///
103
    /// * `f` - Closure that receives an immutable reference (`&T`) to
104
    ///   the protected data
105
    ///
106
    /// # Returns
107
    ///
108
    /// Returns a future that resolves to the result produced by the closure
109
    ///
110
    /// # Example
111
    ///
112
    /// ```rust,ignore
113
    /// use qubit_concurrent::lock::{AsyncLock, ArcAsyncRwLock};
114
    ///
115
    /// #[tokio::main]
116
    /// async fn main() {
117
    ///     let lock = ArcAsyncRwLock::new(vec![1, 2, 3]);
118
    ///
119
    ///     // Read operation - allows concurrent readers with RwLock
120
    ///     let len = lock.read(|data| data.len()).await;
121
    ///     assert_eq!(len, 3);
122
    ///
123
    ///     // Multiple concurrent readers possible with RwLock
124
    ///     let sum = lock.read(|data|
125
    ///         data.iter().sum::<i32>()
126
    ///     ).await;
127
    ///     assert_eq!(sum, 6);
128
    /// }
129
    /// ```
130
    fn read<R, F>(&self, f: F) -> impl Future<Output = R> + Send
131
    where
132
        F: FnOnce(&T) -> R + Send,
133
        R: Send;
134

135
    /// Acquires a write lock asynchronously and executes a closure
136
    ///
137
    /// This method awaits until a write lock can be acquired without
138
    /// blocking the thread, then executes the provided closure with
139
    /// mutable access to the protected data. For all async lock types,
140
    /// this acquires an exclusive lock that blocks all other operations
141
    /// until the closure completes.
142
    ///
143
    /// # Use Cases
144
    ///
145
    /// - **Data modification**: Updating values, adding/removing elements
146
    /// - **State changes**: Transitioning between different states
147
    /// - **Initialization**: Setting up data structures
148
    /// - **Cleanup operations**: Releasing resources, resetting state
149
    ///
150
    /// # Performance Notes
151
    ///
152
    /// - **All async lock types**: Exclusive access, blocks all other
153
    ///   operations
154
    /// - **RwLock advantage**: Only blocks during actual writes, not reads
155
    ///
156
    /// # Arguments
157
    ///
158
    /// * `f` - Closure that receives a mutable reference (`&mut T`) to
159
    ///   the protected data
160
    ///
161
    /// # Returns
162
    ///
163
    /// Returns a future that resolves to the result produced by the closure
164
    ///
165
    /// # Example
166
    ///
167
    /// ```rust,ignore
168
    /// use qubit_concurrent::lock::{AsyncLock, ArcAsyncRwLock};
169
    ///
170
    /// #[tokio::main]
171
    /// async fn main() {
172
    ///     let lock = ArcAsyncRwLock::new(vec![1, 2, 3]);
173
    ///
174
    ///     // Write operation - exclusive access
175
    ///     lock.write(|data| {
176
    ///         data.push(4);
177
    ///         data.sort();
178
    ///     }).await;
179
    ///
180
    ///     // Verify the changes
181
    ///     let result = lock.read(|data| data.clone()).await;
182
    ///     assert_eq!(result, vec![1, 2, 3, 4]);
183
    /// }
184
    /// ```
185
    fn write<R, F>(&self, f: F) -> impl Future<Output = R> + Send
186
    where
187
        F: FnOnce(&mut T) -> R + Send,
188
        R: Send;
189

190
    /// Attempts to acquire a read lock without waiting
191
    ///
192
    /// This method tries to acquire a read lock immediately. If the lock
193
    /// is currently held by another task in write mode, it returns `None`
194
    /// without waiting. Otherwise, it executes the closure and returns
195
    /// `Some` containing the result.
196
    ///
197
    /// # Arguments
198
    ///
199
    /// * `f` - Closure that receives an immutable reference (`&T`) to
200
    ///   the protected data if the lock is successfully acquired
201
    ///
202
    /// # Returns
203
    ///
204
    /// * `Some(R)` - If the lock was acquired and closure executed
205
    /// * `None` - If the lock is currently held in write mode
206
    ///
207
    /// # Example
208
    ///
209
    /// ```rust,ignore
210
    /// use qubit_concurrent::lock::{AsyncLock, ArcAsyncRwLock};
211
    ///
212
    /// let lock = ArcAsyncRwLock::new(42);
213
    /// if let Some(value) = lock.try_read(|data| *data) {
214
    ///     println!("Got value: {}", value);
215
    /// } else {
216
    ///     println!("Lock is busy with write operation");
217
    /// }
218
    /// ```
219
    fn try_read<R, F>(&self, f: F) -> Option<R>
220
    where
221
        F: FnOnce(&T) -> R;
222

223
    /// Attempts to acquire a write lock without waiting
224
    ///
225
    /// This method tries to acquire a write lock immediately. If the lock
226
    /// is currently held by another task (in either read or write mode),
227
    /// it returns `None` without waiting. Otherwise, it executes the
228
    /// closure and returns `Some` containing the result.
229
    ///
230
    /// # Arguments
231
    ///
232
    /// * `f` - Closure that receives a mutable reference (`&mut T`) to
233
    ///   the protected data if the lock is successfully acquired
234
    ///
235
    /// # Returns
236
    ///
237
    /// * `Some(R)` - If the lock was acquired and closure executed
238
    /// * `None` - If the lock is currently held by another task
239
    ///
240
    /// # Example
241
    ///
242
    /// ```rust,ignore
243
    /// use qubit_concurrent::lock::{AsyncLock, ArcAsyncMutex};
244
    ///
245
    /// let lock = ArcAsyncMutex::new(42);
246
    /// if let Some(result) = lock.try_write(|data| {
247
    ///     *data += 1;
248
    ///     *data
249
    /// }) {
250
    ///     println!("New value: {}", result);
251
    /// } else {
252
    ///     println!("Lock is busy");
253
    /// }
254
    /// ```
255
    fn try_write<R, F>(&self, f: F) -> Option<R>
256
    where
257
        F: FnOnce(&mut T) -> R;
258
}
259

260
/// Asynchronous mutex implementation for tokio::sync::Mutex
261
///
262
/// This implementation uses Tokio's `Mutex` type to provide an
263
/// asynchronous lock that can be awaited without blocking threads.
264
/// Both read and write operations acquire the same exclusive lock,
265
/// ensuring thread safety at the cost of concurrent access.
266
///
267
/// # Type Parameters
268
///
269
/// * `T` - The type of data protected by the lock
270
///
271
/// # Author
272
///
273
/// Haixing Hu
274
impl<T: ?Sized + Send> AsyncLock<T> for AsyncMutex<T> {
275
    #[inline]
276
    async fn read<R, F>(&self, f: F) -> R
1✔
277
    where
1✔
278
        F: FnOnce(&T) -> R + Send,
1✔
279
        R: Send,
1✔
280
    {
1✔
281
        let guard = self.lock().await;
1✔
282
        f(&*guard)
1✔
283
    }
1✔
284

285
    #[inline]
286
    async fn write<R, F>(&self, f: F) -> R
1✔
287
    where
1✔
288
        F: FnOnce(&mut T) -> R + Send,
1✔
289
        R: Send,
1✔
290
    {
1✔
291
        let mut guard = self.lock().await;
1✔
292
        f(&mut *guard)
1✔
293
    }
1✔
294

295
    #[inline]
296
    fn try_read<R, F>(&self, f: F) -> Option<R>
1✔
297
    where
1✔
298
        F: FnOnce(&T) -> R,
1✔
299
    {
300
        if let Ok(guard) = self.try_lock() {
1✔
301
            Some(f(&*guard))
1✔
302
        } else {
303
            None
×
304
        }
305
    }
1✔
306

307
    #[inline]
308
    fn try_write<R, F>(&self, f: F) -> Option<R>
3✔
309
    where
3✔
310
        F: FnOnce(&mut T) -> R,
3✔
311
    {
312
        if let Ok(mut guard) = self.try_lock() {
3✔
313
            Some(f(&mut *guard))
3✔
314
        } else {
315
            None
×
316
        }
317
    }
3✔
318
}
319

320
/// Asynchronous read-write lock implementation for tokio::sync::RwLock
321
///
322
/// This implementation uses Tokio's `RwLock` type to provide an
323
/// asynchronous read-write lock that supports multiple concurrent
324
/// readers or a single writer without blocking threads. Read operations
325
/// use shared locks allowing concurrent readers, while write operations
326
/// use exclusive locks that block all other operations.
327
///
328
/// # Type Parameters
329
///
330
/// * `T` - The type of data protected by the lock
331
///
332
/// # Author
333
///
334
/// Haixing Hu
335
impl<T: ?Sized + Send + Sync> AsyncLock<T> for AsyncRwLock<T> {
336
    #[inline]
337
    async fn read<R, F>(&self, f: F) -> R
1✔
338
    where
1✔
339
        F: FnOnce(&T) -> R + Send,
1✔
340
        R: Send,
1✔
341
    {
1✔
342
        let guard = self.read().await;
1✔
343
        f(&*guard)
1✔
344
    }
1✔
345

346
    #[inline]
347
    async fn write<R, F>(&self, f: F) -> R
1✔
348
    where
1✔
349
        F: FnOnce(&mut T) -> R + Send,
1✔
350
        R: Send,
1✔
351
    {
1✔
352
        let mut guard = self.write().await;
1✔
353
        f(&mut *guard)
1✔
354
    }
1✔
355

356
    #[inline]
357
    fn try_read<R, F>(&self, f: F) -> Option<R>
3✔
358
    where
3✔
359
        F: FnOnce(&T) -> R,
3✔
360
    {
361
        if let Ok(guard) = self.try_read() {
3✔
362
            Some(f(&*guard))
3✔
363
        } else {
364
            None
×
365
        }
366
    }
3✔
367

368
    #[inline]
369
    fn try_write<R, F>(&self, f: F) -> Option<R>
3✔
370
    where
3✔
371
        F: FnOnce(&mut T) -> R,
3✔
372
    {
373
        if let Ok(mut guard) = self.try_write() {
3✔
374
            Some(f(&mut *guard))
3✔
375
        } else {
376
            None
×
377
        }
378
    }
3✔
379
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc