Skip to content

Commit 4d79e54

Browse files
committed
feat (rwlock): add capability to atomically downgrade a guard
1 parent ca39585 commit 4d79e54

File tree

1 file changed

+117
-14
lines changed

1 file changed

+117
-14
lines changed

async-rwlock/src/lib.rs

Lines changed: 117 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -369,7 +369,7 @@ impl<T: ?Sized> RwLock<T> {
369369

370370
// If there are no readers, grab the write lock.
371371
if self.state.compare_and_swap(0, WRITER_BIT, Ordering::AcqRel) == 0 {
372-
Some(RwLockWriteGuard(self, lock))
372+
Some(RwLockWriteGuard { writer: RwLockWriteGuardInner(self), reserved: lock })
373373
} else {
374374
None
375375
}
@@ -397,7 +397,7 @@ impl<T: ?Sized> RwLock<T> {
397397

398398
// Set `WRITER_BIT` and create a guard that unsets it in case this future is canceled.
399399
self.state.fetch_or(WRITER_BIT, Ordering::SeqCst);
400-
let guard = RwLockWriteGuard(self, lock);
400+
let guard = RwLockWriteGuard { writer: RwLockWriteGuardInner(self), reserved: lock };
401401

402402
// If there are readers, we need to wait for them to finish.
403403
while self.state.load(Ordering::SeqCst) != WRITER_BIT {
@@ -512,11 +512,36 @@ unsafe impl<T: Sync + ?Sized> Sync for RwLockUpgradableReadGuard<'_, T> {}
512512
impl<'a, T: ?Sized> RwLockUpgradableReadGuard<'a, T> {
513513
/// Converts this guard into a write guard.
514514
fn into_writer(self) -> RwLockWriteGuard<'a, T> {
515-
let writer = RwLockWriteGuard(self.reader.0, self.reserved);
515+
let writer = RwLockWriteGuard { writer: RwLockWriteGuardInner(self.reader.0), reserved: self.reserved };
516516
mem::forget(self.reader);
517517
writer
518518
}
519519

520+
/// Converts this guard into a reader guard.
521+
///
522+
/// # Examples
523+
///
524+
/// ```
525+
/// # futures_lite::future::block_on(async {
526+
/// use async_rwlock::{RwLock, RwLockUpgradableReadGuard};
527+
///
528+
/// let lock = RwLock::new(1);
529+
///
530+
/// let reader = lock.upgradable_read().await;
531+
/// assert_eq!(*reader, 1);
532+
///
533+
/// assert!(lock.try_upgradable_read().is_none());
534+
///
535+
/// let reader = RwLockUpgradableReadGuard::downgrade(reader);
536+
///
537+
/// assert!(lock.try_upgradable_read().is_some());
538+
/// # })
539+
/// ```
540+
#[inline]
541+
pub fn downgrade(guard: Self) -> RwLockReadGuard<'a, T> {
542+
guard.reader
543+
}
544+
520545
/// Attempts to upgrade into a write lock.
521546
///
522547
/// If a write lock could not be acquired at this time, then [`None`] is returned. Otherwise,
@@ -586,12 +611,12 @@ impl<'a, T: ?Sized> RwLockUpgradableReadGuard<'a, T> {
586611
let guard = guard.into_writer();
587612

588613
// If there are readers, we need to wait for them to finish.
589-
while guard.0.state.load(Ordering::SeqCst) != WRITER_BIT {
614+
while guard.writer.0.state.load(Ordering::SeqCst) != WRITER_BIT {
590615
// Start listening for "no readers" events.
591-
let listener = guard.0.no_readers.listen();
616+
let listener = guard.writer.0.no_readers.listen();
592617

593618
// Check again if there are readers.
594-
if guard.0.state.load(Ordering::Acquire) != WRITER_BIT {
619+
if guard.writer.0.state.load(Ordering::Acquire) != WRITER_BIT {
595620
// Wait for the readers to finish.
596621
listener.await;
597622
}
@@ -621,13 +646,9 @@ impl<T: ?Sized> Deref for RwLockUpgradableReadGuard<'_, T> {
621646
}
622647
}
623648

624-
/// A guard that releases the write lock when dropped.
625-
pub struct RwLockWriteGuard<'a, T: ?Sized>(&'a RwLock<T>, MutexGuard<'a, ()>);
626-
627-
unsafe impl<T: Send + ?Sized> Send for RwLockWriteGuard<'_, T> {}
628-
unsafe impl<T: Sync + ?Sized> Sync for RwLockWriteGuard<'_, T> {}
649+
struct RwLockWriteGuardInner<'a, T: ?Sized>(&'a RwLock<T>);
629650

630-
impl<T: ?Sized> Drop for RwLockWriteGuard<'_, T> {
651+
impl<T: ?Sized> Drop for RwLockWriteGuardInner<'_, T> {
631652
fn drop(&mut self) {
632653
// Unset `WRITER_BIT`.
633654
self.0.state.fetch_and(!WRITER_BIT, Ordering::SeqCst);
@@ -636,6 +657,88 @@ impl<T: ?Sized> Drop for RwLockWriteGuard<'_, T> {
636657
}
637658
}
638659

660+
/// A guard that releases the write lock when dropped.
661+
pub struct RwLockWriteGuard<'a, T: ?Sized> {
662+
writer: RwLockWriteGuardInner<'a, T>,
663+
reserved: MutexGuard<'a, ()>,
664+
}
665+
666+
unsafe impl<T: Send + ?Sized> Send for RwLockWriteGuard<'_, T> {}
667+
unsafe impl<T: Sync + ?Sized> Sync for RwLockWriteGuard<'_, T> {}
668+
669+
impl<'a, T: ?Sized> RwLockWriteGuard<'a, T> {
670+
/// Converts this guard into a reader guard.
671+
///
672+
/// # Examples
673+
///
674+
/// ```
675+
/// # futures_lite::future::block_on(async {
676+
/// use async_rwlock::{RwLock, RwLockWriteGuard};
677+
///
678+
/// let lock = RwLock::new(1);
679+
///
680+
/// let mut writer = lock.write().await;
681+
/// *writer += 1;
682+
///
683+
/// assert!(lock.try_read().is_none());
684+
///
685+
/// let reader = RwLockWriteGuard::downgrade(writer);
686+
/// assert_eq!(*reader, 2);
687+
///
688+
/// assert!(lock.try_read().is_some());
689+
/// # })
690+
/// ```
691+
#[inline]
692+
pub fn downgrade(write_guard: Self) -> RwLockReadGuard<'a, T> {
693+
// Atomically downgrade state.
694+
write_guard.writer.0.state.fetch_add(ONE_READER - WRITER_BIT, Ordering::SeqCst);
695+
// Trigger the "no writer" event.
696+
write_guard.writer.0.no_writer.notify(1);
697+
// Create and return the read guard
698+
let read_guard = RwLockReadGuard(write_guard.writer.0);
699+
mem::forget(write_guard.writer); // RwLockWriteGuardInner::drop should not be called !
700+
read_guard
701+
}
702+
703+
/// Atomically downgrades a write lock into an upgradable read lock
704+
/// without allowing any writers to take exclusive access of the lock in the meantime.
705+
///
706+
/// # Examples
707+
///
708+
/// ```
709+
/// # futures_lite::future::block_on(async {
710+
/// use async_rwlock::{RwLock, RwLockUpgradableReadGuard, RwLockWriteGuard};
711+
///
712+
/// let lock = RwLock::new(1);
713+
///
714+
/// let mut writer = lock.write().await;
715+
/// *writer += 1;
716+
///
717+
/// assert!(lock.try_read().is_none());
718+
///
719+
/// let reader = RwLockWriteGuard::downgrade_to_upgradable(writer);
720+
/// assert_eq!(*reader, 2);
721+
///
722+
/// assert!(lock.try_write().is_none());
723+
/// assert!(lock.try_read().is_some());
724+
///
725+
/// assert!(RwLockUpgradableReadGuard::try_upgrade(reader).is_ok())
726+
/// # })
727+
/// ```
728+
pub fn downgrade_to_upgradable(guard: Self) -> RwLockUpgradableReadGuard<'a, T> {
729+
// Atomically downgrade state.
730+
guard.writer.0.state.fetch_add(ONE_READER - WRITER_BIT, Ordering::SeqCst);
731+
// Create and return the upgradable read guard
732+
let reader = RwLockReadGuard(guard.writer.0);
733+
mem::forget(guard.writer); // RwLockWriteGuardInner::drop should not be called !
734+
735+
RwLockUpgradableReadGuard {
736+
reader,
737+
reserved: guard.reserved,
738+
}
739+
}
740+
}
741+
639742
impl<T: fmt::Debug + ?Sized> fmt::Debug for RwLockWriteGuard<'_, T> {
640743
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
641744
fmt::Debug::fmt(&**self, f)
@@ -652,12 +755,12 @@ impl<T: ?Sized> Deref for RwLockWriteGuard<'_, T> {
652755
type Target = T;
653756

654757
fn deref(&self) -> &T {
655-
unsafe { &*self.0.value.get() }
758+
unsafe { &*self.writer.0.value.get() }
656759
}
657760
}
658761

659762
impl<T: ?Sized> DerefMut for RwLockWriteGuard<'_, T> {
660763
fn deref_mut(&mut self) -> &mut T {
661-
unsafe { &mut *self.0.value.get() }
764+
unsafe { &mut *self.writer.0.value.get() }
662765
}
663766
}

0 commit comments

Comments
 (0)