use std::convert::Infallible;
use std::thread;
use super::{
FastCasDecision,
FastCasError,
FastCasPolicy,
FastCasState,
FastCasSuccess,
};
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub struct FastCas {
policy: FastCasPolicy,
}
impl FastCas {
#[inline]
pub const fn once() -> Self {
Self {
policy: FastCasPolicy::once(),
}
}
#[inline]
pub const fn spin(max_attempts: u32) -> Self {
Self {
policy: FastCasPolicy::spin(max_attempts),
}
}
#[inline]
pub const fn spin_yield(spin_attempts: u32, max_attempts: u32) -> Self {
Self {
policy: FastCasPolicy::spin_yield(spin_attempts, max_attempts),
}
}
#[inline]
pub const fn with_policy(policy: FastCasPolicy) -> Self {
Self { policy }
}
#[inline]
pub const fn policy(&self) -> FastCasPolicy {
self.policy
}
#[inline(always)]
pub fn execute<R, E, F>(
&self,
state: &FastCasState,
operation: F,
) -> Result<FastCasSuccess<R>, FastCasError<E>>
where
F: Fn(usize) -> FastCasDecision<R, E>,
{
let max_attempts = self.policy.max_attempts();
let mut attempts = 1;
loop {
let current = state.load();
match operation(current) {
FastCasDecision::Update { next, output } => {
match state.compare_set(current, next) {
Ok(()) => {
return Ok(FastCasSuccess::updated(current, next, output, attempts));
}
Err(actual) if attempts >= max_attempts => {
return Err(FastCasError::Conflict {
current: actual,
attempts,
});
}
Err(_) => {}
}
}
FastCasDecision::Finish { output } => {
return Ok(FastCasSuccess::finished(current, output, attempts));
}
FastCasDecision::Abort { error } => {
return Err(FastCasError::Abort {
current,
error,
attempts,
});
}
}
attempts += 1;
if self.policy.should_yield_before(attempts) {
thread::yield_now();
}
}
}
#[inline(always)]
pub fn update_by<R, E, F>(
&self,
state: &FastCasState,
operation: F,
) -> Result<FastCasSuccess<R>, FastCasError<E>>
where
F: Fn(usize) -> Result<(usize, R), E>,
{
self.execute(state, |current| match operation(current) {
Ok((next, output)) => FastCasDecision::update(next, output),
Err(error) => FastCasDecision::abort(error),
})
}
#[inline(always)]
pub fn compare_update(
&self,
state: &FastCasState,
expected: usize,
next: usize,
) -> Result<FastCasSuccess<()>, FastCasError<Infallible>> {
self.compare_update_with(state, expected, next, |_, _| ())
}
#[inline(always)]
pub fn compare_update_with<R, F>(
&self,
state: &FastCasState,
expected: usize,
next: usize,
output: F,
) -> Result<FastCasSuccess<R>, FastCasError<Infallible>>
where
F: Fn(usize, usize) -> R,
{
let attempts = 1;
match state.compare_set(expected, next) {
Ok(()) => Ok(FastCasSuccess::updated(
expected,
next,
output(expected, next),
attempts,
)),
Err(current) => Err(FastCasError::Conflict { current, attempts }),
}
}
}
impl Default for FastCas {
#[inline]
fn default() -> Self {
Self::spin(16)
}
}