@@ -346,6 +346,83 @@ pub fn create_pool<S: Spawner + Metrics>(
346346 . build ( )
347347}
348348
349+ /// Async reader–writer lock.
350+ ///
351+ /// Powered by [async_lock::RwLock], `RwLock` provides both fair writer acquisition
352+ /// and `try_read` / `try_write` without waiting (without any runtime-specific dependencies).
353+ ///
354+ /// Usage:
355+ /// ```rust
356+ /// use commonware_runtime::{Spawner, Runner, Signaler, deterministic, RwLock};
357+ ///
358+ /// let executor = deterministic::Runner::default();
359+ /// executor.start(|context| async move {
360+ /// // Create a new RwLock
361+ /// let lock = RwLock::new(2);
362+ ///
363+ /// // many concurrent readers
364+ /// let r1 = lock.read().await;
365+ /// let r2 = lock.read().await;
366+ /// assert_eq!(*r1 + *r2, 4);
367+ ///
368+ /// // exclusive writer
369+ /// drop((r1, r2));
370+ /// let mut w = lock.write().await;
371+ /// *w += 1;
372+ /// });
373+ /// ```
374+ pub struct RwLock < T > ( async_lock:: RwLock < T > ) ;
375+
376+ /// Shared guard returned by [`RwLock::read`].
377+ pub type RwLockReadGuard < ' a , T > = async_lock:: RwLockReadGuard < ' a , T > ;
378+
379+ /// Exclusive guard returned by [`RwLock::write`].
380+ pub type RwLockWriteGuard < ' a , T > = async_lock:: RwLockWriteGuard < ' a , T > ;
381+
382+ impl < T > RwLock < T > {
383+ /// Create a new lock.
384+ #[ inline]
385+ pub const fn new ( value : T ) -> Self {
386+ Self ( async_lock:: RwLock :: new ( value) )
387+ }
388+
389+ /// Acquire a shared read guard.
390+ #[ inline]
391+ pub async fn read ( & self ) -> RwLockReadGuard < ' _ , T > {
392+ self . 0 . read ( ) . await
393+ }
394+
395+ /// Acquire an exclusive write guard.
396+ #[ inline]
397+ pub async fn write ( & self ) -> RwLockWriteGuard < ' _ , T > {
398+ self . 0 . write ( ) . await
399+ }
400+
401+ /// Try to get a read guard without waiting.
402+ #[ inline]
403+ pub fn try_read ( & self ) -> Option < RwLockReadGuard < ' _ , T > > {
404+ self . 0 . try_read ( )
405+ }
406+
407+ /// Try to get a write guard without waiting.
408+ #[ inline]
409+ pub fn try_write ( & self ) -> Option < RwLockWriteGuard < ' _ , T > > {
410+ self . 0 . try_write ( )
411+ }
412+
413+ /// Get mutable access without locking (requires `&mut self`).
414+ #[ inline]
415+ pub fn get_mut ( & mut self ) -> & mut T {
416+ self . 0 . get_mut ( )
417+ }
418+
419+ /// Consume the lock, returning the inner value.
420+ #[ inline]
421+ pub fn into_inner ( self ) -> T {
422+ self . 0 . into_inner ( )
423+ }
424+ }
425+
349426#[ cfg( test) ]
350427async fn task ( i : usize ) -> usize {
351428 for _ in 0 ..5 {
@@ -376,7 +453,7 @@ pub fn run_tasks(tasks: usize, runner: crate::deterministic::Runner) -> (String,
376453#[ cfg( test) ]
377454mod tests {
378455 use super :: * ;
379- use crate :: { tokio, Metrics } ;
456+ use crate :: { deterministic , tokio, Metrics } ;
380457 use commonware_macros:: test_traced;
381458 use rayon:: iter:: { IntoParallelRefIterator , ParallelIterator } ;
382459
@@ -396,4 +473,26 @@ mod tests {
396473 } ) ;
397474 } ) ;
398475 }
476+
477+ #[ test_traced]
478+ fn test_rwlock ( ) {
479+ let executor = deterministic:: Runner :: default ( ) ;
480+ executor. start ( |_| async move {
481+ // Create a new RwLock
482+ let lock = RwLock :: new ( 100 ) ;
483+
484+ // many concurrent readers
485+ let r1 = lock. read ( ) . await ;
486+ let r2 = lock. read ( ) . await ;
487+ assert_eq ! ( * r1 + * r2, 200 ) ;
488+
489+ // exclusive writer
490+ drop ( ( r1, r2) ) ; // all readers must go away
491+ let mut w = lock. write ( ) . await ;
492+ * w += 1 ;
493+
494+ // Check the value
495+ assert_eq ! ( * w, 101 ) ;
496+ } ) ;
497+ }
399498}
0 commit comments