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
// #![cfg_attr(not(feature = "std"), no_std)]
#![deny(clippy::undocumented_unsafe_blocks)]
//! Arc based Rcu implementation based on my implementation in [mthom/scryer-prolog#1980](https://github.com/mthom/scryer-prolog/pull/1980)
//!
//! ```text
//! Arc
//! Rcu
//! Arcu
//! ```
extern crate alloc;
pub mod epoch_counters;
use alloc::sync::Arc;
use epoch_counters::EpochCounterPool;
use crate::epoch_counters::EpochCounter;
pub mod atomic;
pub mod rwlock;
pub mod rcu_ref;
pub trait Rcu {
type Item;
type Pool: EpochCounterPool;
fn new(initial: impl Into<Arc<Self::Item>>, epoch_counter_pool: Self::Pool) -> Self;
/// Read the value of the Rcu for the current epoch
///
/// ## Blocking
/// The initial read on each thread may block while registering the epoch counter.
/// Further read on the same thread won't block even for different Rcu.
///
/// ## Procedure
///
/// 1. Register the Epoch Counter (only done once per thread, may block)
/// 2. atomically increment the epoch counter (by one from even to odd)
/// 3. atomically load the arc pointer
/// 4. atomically increment the arc strong count
/// 5. atomically increment the epoch counter (by one from odd back to even)
#[cfg(feature = "thread_local_counter")]
fn read(&self) -> rcu_ref::RcuRef<Self::Item, Self::Item>
where
Self: Rcu<Pool = epoch_counters::GlobalEpochCounterPool>,
{
let arc = crate::epoch_counters::with_thread_local_epoch_counter(|epoch_counter| {
// Safety:
// - we just registered the epoch counter
// - this is a thread local epoch counter that is only used here, so there can't be a concurrent use
unsafe { self.raw_read(epoch_counter) }
});
rcu_ref::RcuRef::<Self::Item, Self::Item>::new(arc)
}
/// Replace the Rcu's content with a new value
///
/// This does not synchronize writes and the last to update the active_value pointer wins.
///
/// all writes that do not win will be lost, though not leaked.
/// This will block until the old value can be reclaimed,
/// i.e. all threads witnessed to be in the read critical sections
/// have been witnessed to have left the critical section at least once
fn replace(&self, new_value: impl Into<Arc<Self::Item>>) -> Arc<Self::Item>;
/// Update the Rcu using the provided update function
/// Retries when the Rcu has been updated/replaced between reading the old value and writing the new value
/// Aborts when the update function returns None
#[cfg(feature = "thread_local_counter")]
fn try_update<F, R>(&self, mut update: F) -> Option<Arc<Self::Item>>
where
Self: Rcu<Pool = epoch_counters::GlobalEpochCounterPool>,
F: FnMut(&Self::Item) -> Option<R>,
R: Into<Arc<Self::Item>>,
{
// Safety:
// epoch_counter is thread local and as such can't be in use concurrently
// get_epoch_counters returns the list of all registered epoch counters
crate::epoch_counters::with_thread_local_epoch_counter(|epoch_counter| unsafe {
self.raw_try_update(move |old| update(old).map(Into::into), epoch_counter)
})
}
/// ## Safety
/// - The epoch counter must not be used concurrently
/// - The epoch counter must belong to the EpochCounterPool of this Rcu
unsafe fn raw_read(&self, epoch_counter: &EpochCounter) -> Arc<Self::Item>;
/// Update the Rcu using the provided update function
/// Retries when the Rcu has been updated/replaced between reading the old value and writing the new value
/// Aborts when the update function returns None
///
/// ## Safety
/// - The epoch counter must not be used concurrently
/// - The epoch counter must belong to the EpochCounterPool of this Rcu
unsafe fn raw_try_update(
&self,
update: impl FnMut(&Self::Item) -> Option<Arc<Self::Item>>,
epoch_counter: &EpochCounter,
) -> Option<Arc<Self::Item>>;
}