use std::{
array::from_fn,
cell::{Cell, RefCell, UnsafeCell},
fmt,
hint::spin_loop,
iter::repeat_with,
marker::PhantomData,
mem::{align_of, offset_of, size_of},
ops::Deref,
ptr,
sync::{
Arc, Weak,
atomic::{AtomicI64, AtomicU32, AtomicU64, Ordering, fence},
},
thread::{sleep, yield_now},
time::Duration,
};
use log::{debug, trace};
use crate::{EpochEntry, Error, MAX_USER_WORDS, Result};
pub const DRAIN_LIST_SIZE: usize = 16;
const SPIN_BEFORE_YIELD: usize = 32;
const YIELD_BEFORE_SLEEP: usize = 1024;
const BACKOFF_SLEEP_MICROS: u64 = 50;
#[inline]
fn backoff(round: usize) {
if round < SPIN_BEFORE_YIELD {
spin_loop();
} else if round < YIELD_BEFORE_SLEEP {
yield_now();
} else {
sleep(Duration::from_micros(BACKOFF_SLEEP_MICROS));
}
}
static NEXT_THREAD_ID: AtomicU64 = AtomicU64::new(1);
#[inline]
fn mix_thread_id(tid: u64) -> usize {
let mut z = tid.wrapping_add(0x9E37_79B9_7F4A_7C15);
z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
(z ^ (z >> 31)) as usize
}
const MAX_LOCAL_ENTRIES: usize = 4;
const MAX_OVERFLOW_STATES: usize = 16;
#[derive(Clone)]
struct LocalEntryState {
instance_id: u64,
active_entry: usize,
cached_slot: usize,
entries: Option<Weak<[EpochEntry]>>,
}
struct LocalEpochEntries {
count: usize,
inline: [LocalEntryState; MAX_LOCAL_ENTRIES],
overflow: Vec<LocalEntryState>,
}
impl Drop for LocalEpochEntries {
fn drop(&mut self) {
let mut need_fence = false;
for state in self.inline[..self.count]
.iter_mut()
.chain(self.overflow.iter_mut())
{
if state.active_entry != 0 {
if let Some(weak) = &state.entries
&& let Some(entries) = weak.upgrade()
&& state.active_entry <= entries.len()
{
let entry = &entries[state.active_entry - 1];
entry.reset();
need_fence = true;
}
state.active_entry = 0;
}
}
if need_fence {
fence(Ordering::SeqCst);
}
}
}
impl LocalEpochEntries {
const fn new() -> Self {
Self {
count: 0,
inline: [const {
LocalEntryState {
instance_id: 0,
active_entry: 0,
cached_slot: 0,
entries: None,
}
}; MAX_LOCAL_ENTRIES],
overflow: Vec::new(),
}
}
#[inline]
fn find(&self, instance_id: u64) -> Option<(usize, usize)> {
self.inline[..self.count]
.iter()
.chain(self.overflow.iter())
.find(|s| s.instance_id == instance_id)
.map(|s| (s.active_entry, s.cached_slot))
}
#[inline]
fn find_mut(&mut self, instance_id: u64) -> Option<&mut LocalEntryState> {
self.inline[..self.count]
.iter_mut()
.chain(self.overflow.iter_mut())
.find(|s| s.instance_id == instance_id)
}
fn set_active<F>(&mut self, instance_id: u64, entry: usize, get_weak: F)
where
F: FnOnce() -> Weak<[EpochEntry]>,
{
if let Some(item) = self.find_mut(instance_id) {
item.active_entry = entry;
if entry != 0 {
item.cached_slot = entry;
if item.entries.as_ref().is_none_or(|e| e.strong_count() == 0) {
item.entries = Some(get_weak());
}
}
return;
}
let new_state = LocalEntryState {
instance_id,
active_entry: entry,
cached_slot: entry,
entries: (entry != 0).then(get_weak),
};
if self.count < MAX_LOCAL_ENTRIES {
self.inline[self.count] = new_state;
self.count += 1;
} else {
if self.overflow.len() >= MAX_OVERFLOW_STATES {
self.overflow.retain(|s| {
s.active_entry != 0 || s.entries.as_ref().is_some_and(|w| w.strong_count() > 0)
});
}
self.overflow.push(new_state);
}
}
}
thread_local! {
static THREAD_ID: u64 = NEXT_THREAD_ID.fetch_add(1, Ordering::Relaxed);
static THREAD_LOCAL_ENTRIES: RefCell<LocalEpochEntries> = const { RefCell::new(LocalEpochEntries::new()) };
static FAST_ENTRY: Cell<FastEntry> = const { Cell::new(FastEntry::EMPTY) };
static FAST_PARTICIPANT: Cell<FastEntry> = const { Cell::new(FastEntry::EMPTY) };
}
#[derive(Clone, Copy)]
struct FastEntry {
instance_id: u64,
slot: usize,
ptr: *const EpochEntry,
}
impl FastEntry {
const EMPTY: Self = Self {
instance_id: 0,
slot: 0,
ptr: ptr::null(),
};
}
#[inline]
fn note_participant_slot(instance_id: u64, idx: usize, entry: &EpochEntry) {
FAST_PARTICIPANT.set(FastEntry {
instance_id,
slot: idx + 1,
ptr: ptr::from_ref(entry),
});
}
#[inline]
pub fn current_thread_id() -> u64 {
THREAD_ID.with(|&id| id)
}
#[inline]
fn get_thread_entry_and_cached(instance_id: u64) -> (usize, usize) {
THREAD_LOCAL_ENTRIES.with(|cell| cell.borrow().find(instance_id).unwrap_or((0, 0)))
}
#[inline]
fn get_thread_entry(instance_id: u64) -> usize {
THREAD_LOCAL_ENTRIES.with(|cell| {
cell
.borrow()
.find(instance_id)
.map_or(0, |(active, _)| active)
})
}
#[inline]
fn set_thread_entry<F>(instance_id: u64, entry: usize, get_weak: F)
where
F: FnOnce() -> Weak<[EpochEntry]>,
{
let resolved = THREAD_LOCAL_ENTRIES.with(|cell| {
let mut entries = cell.borrow_mut();
entries.set_active(instance_id, entry, get_weak);
if entry != 0 {
entries
.find_mut(instance_id)
.and_then(|s| s.entries.as_ref())
.and_then(Weak::upgrade)
.filter(|e| entry <= e.len())
.map(|e| ptr::from_ref(&e[entry - 1]))
} else {
None
}
});
match resolved {
Some(p) => FAST_ENTRY.set(FastEntry {
instance_id,
slot: entry,
ptr: p,
}),
None => FAST_ENTRY.set(FastEntry::EMPTY),
}
}
#[inline]
fn clear_thread_entry(instance_id: u64) {
THREAD_LOCAL_ENTRIES.with(|cell| {
let mut entries = cell.borrow_mut();
if let Some(item) = entries.find_mut(instance_id) {
item.active_entry = 0;
}
});
if FAST_ENTRY.get().instance_id == instance_id {
FAST_ENTRY.set(FastEntry::EMPTY);
}
}
const DRAIN_ENTRY_FREE: u64 = u64::MAX;
const DRAIN_ENTRY_CLAIMING: u64 = u64::MAX - 1;
#[repr(align(64))]
struct DrainEntry {
epoch: AtomicU64,
action: UnsafeCell<Option<Box<dyn FnOnce() + Send + 'static>>>,
}
unsafe impl Send for DrainEntry {}
unsafe impl Sync for DrainEntry {}
const _: () = assert!(size_of::<DrainEntry>() == 64);
const _: () = assert!(align_of::<DrainEntry>() == 64);
impl DrainEntry {
const fn new() -> Self {
Self {
epoch: AtomicU64::new(DRAIN_ENTRY_FREE),
action: UnsafeCell::new(None),
}
}
}
static NEXT_INSTANCE_ID: AtomicU64 = AtomicU64::new(1);
#[repr(C, align(64))]
pub struct LightEpoch {
pub id: u64,
pub current_epoch: AtomicU64,
_pad0: [u8; 48],
pub safe_to_reclaim_epoch: AtomicU64,
_pad1: [u8; 56],
pub drain_count: AtomicU32,
pub user_word_mask: AtomicU32,
_pad2: [u8; 56],
pub entries: Arc<[EpochEntry]>,
drain_list: Box<[DrainEntry; DRAIN_LIST_SIZE]>,
}
impl LightEpoch {
pub const DEFAULT_MAX_THREADS: usize = 128;
pub fn new(max_threads: usize) -> Self {
let max_threads = max_threads.max(1);
let entries: Arc<[EpochEntry]> = repeat_with(EpochEntry::new).take(max_threads).collect();
Self {
id: NEXT_INSTANCE_ID.fetch_add(1, Ordering::Relaxed),
current_epoch: AtomicU64::new(1),
_pad0: [0; 48],
safe_to_reclaim_epoch: AtomicU64::new(0),
_pad1: [0; 56],
drain_count: AtomicU32::new(0),
user_word_mask: AtomicU32::new(0),
_pad2: [0; 56],
entries,
drain_list: Box::new(from_fn(|_| DrainEntry::new())),
}
}
pub fn register(self: &Arc<Self>) -> Result<Participant> {
for (idx, entry) in self.entries.iter().enumerate() {
if entry.try_reserve() {
trace!("成功注册参与者,分配条目索引: {idx}");
note_participant_slot(self.id, idx, entry);
return Ok(Participant {
epoch: Arc::clone(self),
entry_idx: idx,
});
}
}
Err(Error::ExceededMaxThreads(self.entries.len()))
}
#[inline]
fn active_idx(&self) -> Option<usize> {
let entry = get_thread_entry(self.id);
(entry != 0 && entry <= self.entries.len()).then(|| entry - 1)
}
#[inline]
fn tls_protected_entry(&self, tid: u64) -> Option<&EpochEntry> {
let fe = FAST_ENTRY.get();
if fe.instance_id == self.id && fe.slot != 0 {
let entry = unsafe { &*fe.ptr };
if entry.thread_id() == tid && entry.is_protected() {
return Some(entry);
}
}
let idx = self.active_idx()?;
let entry = unsafe {
self.entries.get_unchecked(idx)
};
(entry.thread_id() == tid && entry.is_protected()).then_some(entry)
}
#[inline]
fn drain_if_pending(&self) {
if self.drain_count.load(Ordering::Acquire) > 0 {
self.drain();
}
}
#[inline]
fn help_drain_if_pending(&self) {
if self.drain_count.load(Ordering::Acquire) > 0 {
self.help_drain();
}
}
#[inline]
fn claim_entry(&self, idx: usize, tid: u64) -> bool {
let entry = unsafe {
self.entries.get_unchecked(idx)
};
if !entry.try_claim(tid, &self.current_epoch) {
return false;
}
set_thread_entry(self.id, idx + 1, || Arc::downgrade(&self.entries));
self.help_drain_if_pending();
true
}
pub fn resume(&self) {
let tid = current_thread_id();
if let Some(entry) = self.tls_protected_entry(tid) {
entry.inc_reentrant();
self.drain_if_pending();
return;
}
let len = self.entries.len();
let cached_slot = get_thread_entry_and_cached(self.id).1;
if let Some(idx) = cached_slot.checked_sub(1).filter(|&idx| idx < len)
&& self.claim_entry(idx, tid)
{
return;
}
let start = mix_thread_id(tid) % len;
let mut spins = 0usize;
loop {
for offset in 0..len {
let sum = start + offset;
if self.claim_entry(if sum < len { sum } else { sum - len }, tid) {
return;
}
}
backoff(spins);
spins = spins.wrapping_add(1);
}
}
pub fn suspend(&self) {
let Some(entry) = self.tls_protected_entry(current_thread_id()) else {
return;
};
if !entry.exit() {
return;
}
clear_thread_entry(self.id);
self.after_release();
}
#[inline]
fn after_release(&self) {
if self.drain_count.load(Ordering::Acquire) > 0 {
self.suspend_drain();
}
}
pub fn try_suspend(&self) -> bool {
if self.this_instance_protected() {
self.suspend();
true
} else {
false
}
}
pub fn resume_if_not_protected(&self) -> bool {
if self.this_instance_protected() {
false
} else {
self.resume();
true
}
}
pub fn this_instance_protected(&self) -> bool {
self.tls_protected_entry(current_thread_id()).is_some()
}
pub fn thread_protected(&self) -> bool {
self.thread_protected_entry().is_some()
}
fn thread_protected_entry(&self) -> Option<&EpochEntry> {
let tid = current_thread_id();
if let Some(entry) = self.tls_protected_entry(tid) {
return Some(entry);
}
let fp = FAST_PARTICIPANT.get();
if fp.instance_id == self.id && fp.slot != 0 {
let entry = unsafe { &*fp.ptr };
if entry.thread_id() == tid && entry.is_protected() {
return Some(entry);
}
}
self
.entries
.iter()
.find(|entry| entry.is_protected() && entry.thread_id() == tid)
}
pub fn suspend_resume(&self) {
self.suspend();
self.resume();
}
pub fn protect_and_drain(&self) {
let Some(idx) = self.active_idx() else {
debug_assert!(false, "试图刷新未受保护的纪元");
return;
};
let entry = unsafe { self.entries.get_unchecked(idx) };
debug_assert!(
entry.thread_id() == current_thread_id() && entry.is_protected(),
"试图刷新未受保护的纪元"
);
let current = self.current_epoch();
entry.refresh_epoch(current);
self.drain_if_pending();
}
pub fn protected_scope(&self) -> ProtectedScope<'_> {
ProtectedScope::new(self)
}
pub fn bump_current_epoch(&self) -> u64 {
let new_epoch = self.current_epoch.fetch_add(1, Ordering::AcqRel) + 1;
trace!("递增全局纪元至: {new_epoch}");
if self.drain_count.load(Ordering::Acquire) > 0 {
self.drain();
} else {
self.compute_safe_to_reclaim_epoch();
}
new_epoch
}
#[inline]
pub fn bump_epoch(&self) -> u64 {
self.bump_current_epoch()
}
#[inline]
fn help_drain(&self) {
if let Some(entry) = self.thread_protected_entry() {
let current = self.current_epoch.load(Ordering::Acquire);
entry.refresh_epoch(current);
}
self.drain();
}
pub fn bump_current_epoch_action<F>(&self, on_drain: F)
where
F: FnOnce() + Send + 'static,
{
let prior_epoch = self.bump_current_epoch() - 1;
let mut action_opt = Some(Box::new(on_drain) as Box<dyn FnOnce() + Send + 'static>);
'outer: loop {
for entry in self.drain_list.iter() {
let curr_epoch = entry.epoch.load(Ordering::Acquire);
if (curr_epoch == DRAIN_ENTRY_FREE
|| curr_epoch <= self.safe_to_reclaim_epoch.load(Ordering::Acquire))
&& entry
.epoch
.compare_exchange(
curr_epoch,
DRAIN_ENTRY_CLAIMING,
Ordering::AcqRel,
Ordering::Acquire,
)
.is_ok()
{
let new_action = action_opt.take();
let prev_action = unsafe {
let ptr = entry.action.get();
let prev = (*ptr).take();
*ptr = new_action;
prev
};
if curr_epoch == DRAIN_ENTRY_FREE {
self.drain_count.fetch_add(1, Ordering::AcqRel);
}
entry.epoch.store(prior_epoch, Ordering::Release);
if let Some(act) = prev_action {
act();
}
break 'outer;
}
}
self.help_drain();
yield_now();
}
self.help_drain();
}
#[inline]
pub fn current_epoch(&self) -> u64 {
self.current_epoch.load(Ordering::Acquire)
}
#[inline]
pub fn safe_to_reclaim_epoch(&self) -> u64 {
self.safe_to_reclaim_epoch.load(Ordering::Acquire)
}
pub fn compute_safe_to_reclaim_epoch(&self) -> u64 {
let curr = self.current_epoch.load(Ordering::Acquire);
let mut oldest = curr;
for entry in self.entries.iter() {
let epoch = entry.protected_epoch();
if epoch != 0 && epoch < oldest {
oldest = epoch;
if oldest == 1 {
break;
}
}
}
let safe = oldest.saturating_sub(1);
let prev = self.safe_to_reclaim_epoch.load(Ordering::Relaxed);
if safe > prev {
self.safe_to_reclaim_epoch.fetch_max(safe, Ordering::AcqRel);
}
prev.max(safe)
}
#[inline]
fn try_claim_ready_slot(entry: &DrainEntry, safe_epoch: u64) -> bool {
let trigger_epoch = entry.epoch.load(Ordering::Acquire);
trigger_epoch <= safe_epoch
&& entry
.epoch
.compare_exchange(
trigger_epoch,
DRAIN_ENTRY_CLAIMING,
Ordering::AcqRel,
Ordering::Acquire,
)
.is_ok()
}
pub fn drain(&self) {
let safe_epoch = self.compute_safe_to_reclaim_epoch();
for entry in self.drain_list.iter() {
if Self::try_claim_ready_slot(entry, safe_epoch) {
let action = unsafe { (*entry.action.get()).take() };
entry.epoch.store(DRAIN_ENTRY_FREE, Ordering::Release);
self.drain_count.fetch_sub(1, Ordering::AcqRel);
if let Some(act) = action {
act();
}
if self.drain_count.load(Ordering::Acquire) == 0 {
break;
}
}
}
}
fn suspend_drain(&self) {
while self.drain_count.load(Ordering::Acquire) > 0 {
fence(Ordering::SeqCst);
if self.entries.iter().any(EpochEntry::is_protected) {
return;
}
self.drain();
yield_now();
}
}
#[inline]
pub fn is_safe_to_reclaim(&self, target_epoch: u64) -> bool {
if target_epoch <= self.safe_to_reclaim_epoch.load(Ordering::Acquire) {
return true;
}
target_epoch <= self.compute_safe_to_reclaim_epoch()
}
pub fn bump_and_wait(&self, target_epoch: u64) {
debug!("开始 bump_and_wait 等待纪元 {target_epoch} 完全 drain");
debug_assert!(
!self.entries.iter().any(|e| e.is_protected()
&& e.thread_id() == current_thread_id()
&& e.protected_epoch() <= target_epoch),
"bump_and_wait 活锁:调用线程以 ≤ {target_epoch} 的纪元受保护,须先 suspend/refresh"
);
while self.current_epoch.load(Ordering::Acquire) <= target_epoch {
self.bump_epoch();
}
let mut spins = 0usize;
while !self.is_safe_to_reclaim(target_epoch) {
self.drain_if_pending();
backoff(spins);
spins = spins.wrapping_add(1);
}
self.drain_if_pending();
debug!("纪元 {target_epoch} drain 完成");
}
#[inline]
pub fn has_pending_drain(&self) -> bool {
self.drain_count.load(Ordering::Acquire) > 0
}
#[inline]
pub fn test_hook_this_thread_entry(&self) -> usize {
get_thread_entry(self.id)
}
#[inline]
pub fn test_hook_this_thread_announced_epoch(&self) -> u64 {
self
.active_idx()
.map(|idx| unsafe { self.entries.get_unchecked(idx).protected_epoch() })
.unwrap_or(0)
}
#[inline]
pub fn test_hook_announced_epoch_at(&self, entry: usize) -> u64 {
if entry == 0 || entry > self.entries.len() {
0
} else {
unsafe { self.entries.get_unchecked(entry - 1).protected_epoch() }
}
}
#[inline]
pub fn test_hook_thread_id_at(&self, entry: usize) -> u64 {
if entry == 0 || entry > self.entries.len() {
0
} else {
unsafe { self.entries.get_unchecked(entry - 1).thread_id() }
}
}
#[inline]
pub fn test_hook_drain_list_capacity(&self) -> usize {
DRAIN_LIST_SIZE
}
#[inline]
pub fn entry_count(&self) -> usize {
self.entries.len()
}
#[inline]
pub fn test_hook_max_user_words(&self) -> usize {
MAX_USER_WORDS
}
pub fn allocate_user_word(&self, initial_value: i64) -> Result<usize> {
loop {
let mask = self.user_word_mask.load(Ordering::Acquire);
let idx = (!mask).trailing_zeros() as usize;
if idx >= MAX_USER_WORDS {
return Err(Error::ExceededMaxUserWords(MAX_USER_WORDS));
}
let new_mask = mask | (1 << idx);
if self
.user_word_mask
.compare_exchange_weak(mask, new_mask, Ordering::AcqRel, Ordering::Acquire)
.is_err()
{
continue;
}
for entry in self.entries.iter() {
unsafe { entry.set_user_word_unchecked(idx, initial_value) };
}
return Ok(idx);
}
}
pub fn release_user_word(&self, word_index: usize) -> Result<()> {
if word_index >= MAX_USER_WORDS {
return Err(Error::InvalidUserWordIndex(word_index));
}
loop {
let mask = self.user_word_mask.load(Ordering::Acquire);
let new_mask = mask & !(1 << word_index);
if self
.user_word_mask
.compare_exchange_weak(mask, new_mask, Ordering::AcqRel, Ordering::Acquire)
.is_ok()
{
return Ok(());
}
}
}
#[inline]
pub fn this_thread_user_word_atomic(&self, word_index: usize) -> Result<&AtomicI64> {
if word_index >= MAX_USER_WORDS {
return Err(Error::InvalidUserWordIndex(word_index));
}
let idx = self.active_idx().ok_or(Error::NotProtected)?;
unsafe {
Ok(
self
.entries
.get_unchecked(idx)
.user_word_atomic_unchecked(word_index),
)
}
}
#[inline]
pub fn this_thread_user_word(&self, word_index: usize) -> Result<i64> {
Ok(
self
.this_thread_user_word_atomic(word_index)?
.load(Ordering::Acquire),
)
}
#[inline]
pub fn set_this_thread_user_word(&self, word_index: usize, val: i64) -> Result<()> {
self
.this_thread_user_word_atomic(word_index)?
.store(val, Ordering::Release);
Ok(())
}
pub fn get_min_user_word(&self, word_index: usize) -> Result<i64> {
if word_index >= MAX_USER_WORDS {
return Err(Error::InvalidUserWordIndex(word_index));
}
Ok(self.entries.iter().fold(i64::MAX, |min, e| {
unsafe { e.user_word_unchecked(word_index) }.min(min)
}))
}
}
impl Default for LightEpoch {
fn default() -> Self {
Self::new(Self::DEFAULT_MAX_THREADS)
}
}
const _: () = {
assert!(size_of::<LightEpoch>().is_multiple_of(64));
assert!(align_of::<LightEpoch>() == 64);
assert!(offset_of!(LightEpoch, current_epoch) == 8);
assert!(offset_of!(LightEpoch, safe_to_reclaim_epoch) == 64);
assert!(offset_of!(LightEpoch, drain_count) == 128);
assert!(offset_of!(LightEpoch, user_word_mask) == 132);
};
impl fmt::Debug for LightEpoch {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("LightEpoch")
.field("id", &self.id)
.field("current_epoch", &self.current_epoch.load(Ordering::Relaxed))
.field(
"safe_to_reclaim_epoch",
&self.safe_to_reclaim_epoch.load(Ordering::Relaxed),
)
.field("drain_count", &self.drain_count.load(Ordering::Relaxed))
.field("max_threads", &self.entries.len())
.finish()
}
}
pub struct Participant {
epoch: Arc<LightEpoch>,
entry_idx: usize,
}
impl Participant {
#[inline]
pub fn enter(&self) -> EpochGuard<'_> {
let tid = current_thread_id();
let entry = unsafe { self.epoch.entries.get_unchecked(self.entry_idx) };
let protected_epoch = entry.enter_with_tid(&self.epoch.current_epoch, tid);
self.epoch.drain_if_pending();
EpochGuard {
participant: self,
protected_epoch,
}
}
#[inline]
pub fn refresh(&self) {
let entry = unsafe { self.epoch.entries.get_unchecked(self.entry_idx) };
if entry.is_protected() {
let current = self.epoch.current_epoch();
entry.refresh_epoch(current);
self.epoch.drain_if_pending();
}
}
#[inline]
pub fn exit(&self) {
let entry = unsafe { self.epoch.entries.get_unchecked(self.entry_idx) };
if entry.exit() {
self.epoch.after_release();
}
}
#[inline]
pub fn entry_idx(&self) -> usize {
self.entry_idx
}
#[inline]
pub fn is_protected(&self) -> bool {
unsafe {
self
.epoch
.entries
.get_unchecked(self.entry_idx)
.is_protected()
}
}
#[inline]
pub fn reentrant_count(&self) -> u32 {
unsafe {
self
.epoch
.entries
.get_unchecked(self.entry_idx)
.reentrant_count()
}
}
#[inline]
pub fn protected_epoch(&self) -> u64 {
unsafe {
self
.epoch
.entries
.get_unchecked(self.entry_idx)
.protected_epoch()
}
}
#[inline]
fn user_word_ref(&self, word_index: usize) -> Result<&AtomicI64> {
if word_index >= MAX_USER_WORDS {
return Err(Error::InvalidUserWordIndex(word_index));
}
unsafe {
Ok(
self
.epoch
.entries
.get_unchecked(self.entry_idx)
.user_word_atomic_unchecked(word_index),
)
}
}
#[inline]
pub fn user_word(&self, word_index: usize) -> Result<i64> {
Ok(self.user_word_ref(word_index)?.load(Ordering::Acquire))
}
#[inline]
pub fn set_user_word(&self, word_index: usize, val: i64) -> Result<()> {
self
.user_word_ref(word_index)?
.store(val, Ordering::Release);
Ok(())
}
#[inline]
pub fn user_word_atomic(&self, word_index: usize) -> Result<&AtomicI64> {
self.user_word_ref(word_index)
}
}
impl Drop for Participant {
fn drop(&mut self) {
unsafe {
self
.epoch
.entries
.get_unchecked(self.entry_idx)
.release_reserve()
};
self.epoch.after_release();
}
}
impl fmt::Debug for Participant {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Participant")
.field("entry_idx", &self.entry_idx)
.field("is_protected", &self.is_protected())
.field("protected_epoch", &self.protected_epoch())
.field("reentrant_count", &self.reentrant_count())
.finish()
}
}
pub struct EpochGuard<'a> {
participant: &'a Participant,
protected_epoch: u64,
}
impl EpochGuard<'_> {
#[inline]
pub fn protected_epoch(&self) -> u64 {
self.protected_epoch
}
}
impl Drop for EpochGuard<'_> {
#[inline]
fn drop(&mut self) {
self.participant.exit();
}
}
impl Deref for EpochGuard<'_> {
type Target = Participant;
#[inline]
fn deref(&self) -> &Self::Target {
self.participant
}
}
impl fmt::Debug for EpochGuard<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("EpochGuard")
.field("entry_idx", &self.participant.entry_idx)
.field("protected_epoch", &self.protected_epoch)
.finish()
}
}
pub struct ProtectedScope<'a> {
epoch: &'a LightEpoch,
_marker: PhantomData<*const ()>,
}
impl<'a> ProtectedScope<'a> {
pub fn new(epoch: &'a LightEpoch) -> Self {
epoch.resume();
Self {
epoch,
_marker: PhantomData,
}
}
}
impl Drop for ProtectedScope<'_> {
#[inline]
fn drop(&mut self) {
self.epoch.suspend();
}
}
impl Deref for ProtectedScope<'_> {
type Target = LightEpoch;
#[inline]
fn deref(&self) -> &Self::Target {
self.epoch
}
}
impl fmt::Debug for ProtectedScope<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ProtectedScope")
.field("epoch_id", &self.epoch.id)
.field("current_epoch", &self.epoch.current_epoch())
.finish()
}
}