use std::{
fmt,
marker::PhantomData,
ops::Deref,
sync::{
Arc,
atomic::{AtomicI64, Ordering},
},
};
use wbase::thread::current_thread_id;
use crate::{Error, LightEpoch, MAX_USER_WORDS, Result};
pub struct Participant {
epoch: Arc<LightEpoch>,
entry_idx: usize,
}
impl Participant {
pub(crate) fn new(epoch: Arc<LightEpoch>, entry_idx: usize) -> Self {
Self { epoch, entry_idx }
}
#[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()
}
}