urcu/rcu/reference.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
use crate::rcu::callback::{RcuCallFn, RcuDeferFn};
use crate::rcu::RcuContext;
/// This trait defines a RCU reference that can be owned after a RCU grace period.
///
/// #### Safety
///
/// * The underlying reference must be cleaned up upon dropping (see below).
/// * There may be immutable borrows to the underlying reference.
/// * There must not be any mutable borrows to the underlying reference.
///
/// #### Dropping
///
/// An [`RcuRef`] should always cleanup when [`Drop::drop`] is executed by taking
/// ownership and dropping the underlying value.
///
/// * We cannot call [`RcuContext::rcu_synchronize`] since we can't be sure that
/// a RCU read lock is currently held or not[^mborrow].
///
/// Because an [`RcuRef`] can be sent to any thread, we cannot guarantee that a
/// thread executing [`Drop::drop`] is properly registered.
///
/// * We cannot call [`RcuContext::rcu_defer`] since we can't enforce that the
/// thread is registered with the RCU defer mecanisms[^mborrow].
/// * We cannot call [`RcuContext::rcu_call`] since we can't enforce that the
/// thread is registered with the RCU read mecanisms[^cborrow].
///
/// The only way to keep the safety guarantees of this crate is to use the custom
/// cleanup thread through [`RcuRef::safe_cleanup`]. It is similar to the built-in
/// [`RcuContext::rcu_call`], except it doesn't expect the calling thread to be
/// registered with RCU in any way.
///
/// The downside is that it is most likely worst than [`RcuContext::rcu_call`] in
/// every way. If it is a performance problem, the owner of an [`RcuRef`] can alway
/// use [`RcuRef::defer_cleanup`] and [`RcuRef::call_cleanup`] before [`Drop::drop`]
/// is called.
///
/// [^mborrow]: Unless your [`RcuRef`] has a mutable borrow of an [`RcuContext`].
/// [^cborrow]: Unless your [`RcuRef`] has an immutable borrow of an [`RcuContext`].
#[must_use]
pub unsafe trait RcuRef<C> {
/// The output type after taking ownership.
type Output;
/// Take ownership of the reference.
///
/// #### Safety
///
/// You must wait for the grace period before taking ownership.
unsafe fn take_ownership_unchecked(self) -> Self::Output;
/// Take ownership of the reference.
fn take_ownership(self, context: &mut C) -> Self::Output
where
Self: Sized,
C: RcuContext,
{
context.rcu_synchronize();
// SAFETY: RCU grace period has ended.
unsafe { self.take_ownership_unchecked() }
}
/// Configure a cleanup callback to be called after the grace period.
///
/// #### Note
///
/// The function might internally call [`RcuContext::rcu_synchronize`] and block.
///
/// The callback is guaranteed to be executed on the current thread.
fn defer_cleanup(self, context: &mut C)
where
Self: Sized,
C: RcuContext,
{
context.rcu_defer(RcuDeferFn::<_, C>::new(move || {
// SAFETY: The caller already executed a RCU syncronization.
unsafe {
self.take_ownership_unchecked();
}
}))
}
/// Configure a cleanup callback to be called after the grace period.
///
/// #### Note
///
/// The function will internally call [`RcuContext::rcu_read_lock`].
///
/// The reference must implement [`Send`] since the cleanup will be executed in an helper thread.
fn call_cleanup(self, context: &C)
where
Self: Sized + Send + 'static,
C: RcuContext + 'static,
{
context.rcu_call(RcuCallFn::new(move || {
// SAFETY: The caller already executed a RCU syncronization.
unsafe {
self.take_ownership_unchecked();
}
}));
}
fn safe_cleanup(self)
where
Self: Sized + Send + 'static,
C: RcuContext,
{
C::rcu_cleanup(Box::new(move |context| {
context.rcu_synchronize();
// SAFETY: An RCU syncronization barrier was called.
unsafe {
self.take_ownership_unchecked();
}
}));
}
}
/// #### Safety
///
/// It is the responsability of the underlying type to be safe.
unsafe impl<T, C> RcuRef<C> for Option<T>
where
T: RcuRef<C>,
{
type Output = Option<T::Output>;
unsafe fn take_ownership_unchecked(self) -> Self::Output {
self.map(|r| r.take_ownership_unchecked())
}
}
/// #### Safety
///
/// It is the responsability of the underlying type to be safe.
unsafe impl<T, C> RcuRef<C> for Vec<T>
where
T: RcuRef<C>,
{
type Output = Vec<T::Output>;
unsafe fn take_ownership_unchecked(self) -> Self::Output {
self.into_iter()
.map(|r| r.take_ownership_unchecked())
.collect()
}
}
macro_rules! impl_rcu_ref_for_tuple {
($($x:literal),*) => {
paste::paste!{
/// #### Safety
///
/// It is the responsability of the underlying types to be safe.
unsafe impl<$([<T $x>]),*, C> RcuRef<C> for ($([<T $x>]),*)
where
$([<T $x>]: RcuRef<C>),*,
{
type Output = ($([<T $x>]::Output),*,);
unsafe fn take_ownership_unchecked(self) -> Self::Output {
(
$(self.$x.take_ownership_unchecked()),*,
)
}
}
}
};
}
impl_rcu_ref_for_tuple!(0, 1);
impl_rcu_ref_for_tuple!(0, 1, 2);
impl_rcu_ref_for_tuple!(0, 1, 2, 3);
impl_rcu_ref_for_tuple!(0, 1, 2, 3, 4);
impl_rcu_ref_for_tuple!(0, 1, 2, 3, 4, 5);
impl_rcu_ref_for_tuple!(0, 1, 2, 3, 4, 5, 6);