diff options
Diffstat (limited to 'src/epoch.rs')
-rw-r--r-- | src/epoch.rs | 55 |
1 files changed, 37 insertions, 18 deletions
diff --git a/src/epoch.rs b/src/epoch.rs index e7759d9..663508b 100644 --- a/src/epoch.rs +++ b/src/epoch.rs @@ -7,14 +7,15 @@ //! If an object became garbage in some epoch, then we can be sure that after two advancements no //! participant will hold a reference to it. That is the crux of safe memory reclamation. -use core::sync::atomic::{AtomicUsize, Ordering}; +use crate::primitive::sync::atomic::AtomicUsize; +use core::sync::atomic::Ordering; /// An epoch that can be marked as pinned or unpinned. /// /// Internally, the epoch is represented as an integer that wraps around at some unspecified point /// and a flag that represents whether it is pinned or unpinned. #[derive(Copy, Clone, Default, Debug, Eq, PartialEq)] -pub struct Epoch { +pub(crate) struct Epoch { /// The least significant bit is set if pinned. The rest of the bits hold the epoch. data: usize, } @@ -22,7 +23,7 @@ pub struct Epoch { impl Epoch { /// Returns the starting epoch in unpinned state. #[inline] - pub fn starting() -> Self { + pub(crate) fn starting() -> Self { Self::default() } @@ -30,7 +31,7 @@ impl Epoch { /// /// Internally, epochs are represented as numbers in the range `(isize::MIN / 2) .. (isize::MAX /// / 2)`, so the returned distance will be in the same interval. - pub fn wrapping_sub(self, rhs: Self) -> isize { + pub(crate) fn wrapping_sub(self, rhs: Self) -> isize { // The result is the same with `(self.data & !1).wrapping_sub(rhs.data & !1) as isize >> 1`, // because the possible difference of LSB in `(self.data & !1).wrapping_sub(rhs.data & !1)` // will be ignored in the shift operation. @@ -39,13 +40,13 @@ impl Epoch { /// Returns `true` if the epoch is marked as pinned. #[inline] - pub fn is_pinned(self) -> bool { + pub(crate) fn is_pinned(self) -> bool { (self.data & 1) == 1 } /// Returns the same epoch, but marked as pinned. #[inline] - pub fn pinned(self) -> Epoch { + pub(crate) fn pinned(self) -> Epoch { Epoch { data: self.data | 1, } @@ -53,7 +54,7 @@ impl Epoch { /// Returns the same epoch, but marked as unpinned. #[inline] - pub fn unpinned(self) -> Epoch { + pub(crate) fn unpinned(self) -> Epoch { Epoch { data: self.data & !1, } @@ -63,7 +64,7 @@ impl Epoch { /// /// The returned epoch will be marked as pinned only if the previous one was as well. #[inline] - pub fn successor(self) -> Epoch { + pub(crate) fn successor(self) -> Epoch { Epoch { data: self.data.wrapping_add(2), } @@ -72,7 +73,7 @@ impl Epoch { /// An atomic value that holds an `Epoch`. #[derive(Default, Debug)] -pub struct AtomicEpoch { +pub(crate) struct AtomicEpoch { /// Since `Epoch` is just a wrapper around `usize`, an `AtomicEpoch` is similarly represented /// using an `AtomicUsize`. data: AtomicUsize, @@ -81,14 +82,14 @@ pub struct AtomicEpoch { impl AtomicEpoch { /// Creates a new atomic epoch. #[inline] - pub fn new(epoch: Epoch) -> Self { + pub(crate) fn new(epoch: Epoch) -> Self { let data = AtomicUsize::new(epoch.data); AtomicEpoch { data } } /// Loads a value from the atomic epoch. #[inline] - pub fn load(&self, ord: Ordering) -> Epoch { + pub(crate) fn load(&self, ord: Ordering) -> Epoch { Epoch { data: self.data.load(ord), } @@ -96,19 +97,37 @@ impl AtomicEpoch { /// Stores a value into the atomic epoch. #[inline] - pub fn store(&self, epoch: Epoch, ord: Ordering) { + pub(crate) fn store(&self, epoch: Epoch, ord: Ordering) { self.data.store(epoch.data, ord); } /// Stores a value into the atomic epoch if the current value is the same as `current`. /// - /// The return value is always the previous value. If it is equal to `current`, then the value - /// is updated. + /// The return value is a result indicating whether the new value was written and containing + /// the previous value. On success this value is guaranteed to be equal to `current`. /// - /// The `Ordering` argument describes the memory ordering of this operation. + /// This method takes two `Ordering` arguments to describe the memory + /// ordering of this operation. `success` describes the required ordering for the + /// read-modify-write operation that takes place if the comparison with `current` succeeds. + /// `failure` describes the required ordering for the load operation that takes place when + /// the comparison fails. Using `Acquire` as success ordering makes the store part + /// of this operation `Relaxed`, and using `Release` makes the successful load + /// `Relaxed`. The failure ordering can only be `SeqCst`, `Acquire` or `Relaxed` + /// and must be equivalent to or weaker than the success ordering. #[inline] - pub fn compare_and_swap(&self, current: Epoch, new: Epoch, ord: Ordering) -> Epoch { - let data = self.data.compare_and_swap(current.data, new.data, ord); - Epoch { data } + pub(crate) fn compare_exchange( + &self, + current: Epoch, + new: Epoch, + success: Ordering, + failure: Ordering, + ) -> Result<Epoch, Epoch> { + match self + .data + .compare_exchange(current.data, new.data, success, failure) + { + Ok(data) => Ok(Epoch { data }), + Err(data) => Err(Epoch { data }), + } } } |