diff --git a/ostd/src/sync/mod.rs b/ostd/src/sync/mod.rs index e2696b1a..a61bea2c 100644 --- a/ostd/src/sync/mod.rs +++ b/ostd/src/sync/mod.rs @@ -15,7 +15,7 @@ pub(crate) use self::rcu::finish_grace_period; pub use self::{ guard::{GuardTransfer, LocalIrqDisabled, PreemptDisabled, SpinGuardian, WriteIrqDisabled}, mutex::{ArcMutexGuard, Mutex, MutexGuard}, - rcu::{non_null, Rcu, RcuOption, RcuOptionReadGuard, RcuReadGuard}, + rcu::{non_null, Rcu, RcuDrop, RcuOption, RcuOptionReadGuard, RcuReadGuard}, rwarc::{RoArc, RwArc}, rwlock::{ ArcRwLockReadGuard, ArcRwLockUpgradeableGuard, ArcRwLockWriteGuard, RwLock, diff --git a/ostd/src/sync/rcu/mod.rs b/ostd/src/sync/rcu/mod.rs index 53e81691..54222725 100644 --- a/ostd/src/sync/rcu/mod.rs +++ b/ostd/src/sync/rcu/mod.rs @@ -4,6 +4,8 @@ use core::{ marker::PhantomData, + mem::ManuallyDrop, + ops::Deref, ptr::NonNull, sync::atomic::{ AtomicPtr, @@ -148,7 +150,11 @@ impl RcuInner

{ let old_raw_ptr = self.ptr.swap(new_ptr, AcqRel); if let Some(p) = NonNull::new(old_raw_ptr) { - // SAFETY: It was previously returned by `into_raw`. + // SAFETY: + // 1. The pointer was previously returned by `into_raw`. + // 2. The pointer is removed from the RCU slot so that no one will + // use it after the end of the current grace period. The removal + // is done atomically, so it will only be dropped once. unsafe { delay_drop::

(p) }; } } @@ -232,7 +238,11 @@ impl RcuReadGuardInner<'_, P> { } if let Some(p) = NonNull::new(self.obj_ptr) { - // SAFETY: It was previously returned by `into_raw`. + // SAFETY: + // 1. The pointer was previously returned by `into_raw`. + // 2. The pointer is removed from the RCU slot so that no one will + // use it after the end of the current grace period. The removal + // is done atomically, so it will only be dropped once. unsafe { delay_drop::

(p) }; } @@ -394,10 +404,19 @@ impl RcuOptionReadGuard<'_, P> { } } +/// Delays the dropping of a [`NonNullPtr`] after the RCU grace period. +/// +/// This is internally needed for implementing [`Rcu`] and [`RcuOption`] +/// because we cannot alias a [`Box`]. Restoring `P` and use [`RcuDrop`] for it +/// can lead to multiple [`Box`]es simultaneously pointing to the same +/// content. +/// /// # Safety /// -/// The pointer must be previously returned by `into_raw` and the pointer -/// must be only be dropped once. +/// The pointer must be previously returned by `into_raw`, will not be used +/// after the end of the current grace period, and will only be dropped once. +/// +/// [`Box`]: alloc::boxed::Box unsafe fn delay_drop(pointer: NonNull<

::Target>) { struct ForceSend(NonNull<

::Target>); // SAFETY: Sending a raw pointer to another task is safe as long as @@ -422,6 +441,47 @@ unsafe fn delay_drop(pointer: NonNull<

::T }); } +/// A wrapper to delay calling destructor of `T` after the RCU grace period. +/// +/// Upon dropping this structure, a callback will be registered to the global +/// RCU monitor and the destructor of `T` will be delayed until the callback. +/// +/// [`RcuDrop`] is guaranteed to have the same layout as `T`. You can also +/// access the inner value safely via [`RcuDrop`]. +#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[repr(transparent)] +pub struct RcuDrop { + value: ManuallyDrop, +} + +impl RcuDrop { + /// Creates a new [`RcuDrop`] instance that delays the dropping of `value`. + pub fn new(value: T) -> Self { + Self { + value: ManuallyDrop::new(value), + } + } +} + +impl Deref for RcuDrop { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.value + } +} + +impl Drop for RcuDrop { + fn drop(&mut self) { + // SAFETY: The `ManuallyDrop` will not be used after this point. + let taken = unsafe { ManuallyDrop::take(&mut self.value) }; + let rcu_monitor = RCU_MONITOR.get().unwrap(); + rcu_monitor.after_grace_period(|| { + drop(taken); + }); + } +} + /// Finishes the current grace period. /// /// This function is called when the current grace period on current CPU is