use std::fmt;
use std::sync::atomic::Ordering;
use bitflags::Flags;
mod atomic_bits;
#[cfg(test)]
mod tests;
pub use atomic_bits::AtomicBits;
pub struct AtomicFlags<F>
where
F: Flags,
F::Bits: AtomicBits,
F: Copy,
{
storage: <F::Bits as AtomicBits>::Storage,
}
impl<F> Default for AtomicFlags<F>
where
F: Flags,
F::Bits: AtomicBits,
F: Copy,
F: Default,
{
fn default() -> Self {
Self::new(F::default())
}
}
impl<F> fmt::Debug for AtomicFlags<F>
where
F: Flags,
F::Bits: AtomicBits,
F: Copy,
F: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.load(Ordering::SeqCst).fmt(f)
}
}
impl<F> AtomicFlags<F>
where
F: Flags,
F::Bits: AtomicBits,
F: Copy,
{
#[inline]
pub fn new(flags: F) -> Self {
Self::from_storage(F::Bits::new(flags.bits()))
}
pub const fn from_storage(storage: <F::Bits as AtomicBits>::Storage) -> Self {
Self { storage }
}
#[inline]
pub fn store(&self, flags: F, ordering: Ordering) {
F::Bits::store(&self.storage, flags.bits(), ordering);
}
#[inline]
pub fn load(&self, ordering: Ordering) -> F {
F::from_bits_retain(F::Bits::load(&self.storage, ordering))
}
#[inline]
pub fn fetch_and(&self, flags: F, ordering: Ordering) -> F {
F::from_bits_retain(F::Bits::fetch_and(&self.storage, flags.bits(), ordering))
}
#[inline]
pub fn fetch_or(&self, flags: F, ordering: Ordering) -> F {
F::from_bits_retain(F::Bits::fetch_or(&self.storage, flags.bits(), ordering))
}
#[inline]
pub fn fetch_xor(&self, flags: F, ordering: Ordering) -> F {
F::from_bits_retain(F::Bits::fetch_xor(&self.storage, flags.bits(), ordering))
}
#[inline]
pub fn toggle(&self, flags: F, ordering: Ordering) -> F {
self.fetch_xor(flags, ordering)
}
#[inline]
pub fn fetch_nand(&self, flags: F, ordering: Ordering) -> F {
F::from_bits_retain(F::Bits::fetch_nand(&self.storage, flags.bits(), ordering))
}
#[inline]
pub fn swap(&self, flags: F, ordering: Ordering) -> F {
F::from_bits_retain(F::Bits::swap(&self.storage, flags.bits(), ordering))
}
#[inline]
pub fn compare_exchange(
&self,
current: F,
new: F,
success: Ordering,
failure: Ordering,
) -> Result<F, F> {
F::Bits::compare_exchange(&self.storage, current.bits(), new.bits(), success, failure)
.map(F::from_bits_retain)
.map_err(F::from_bits_retain)
}
#[inline]
pub fn compare_exchange_weak(
&self,
current: F,
new: F,
success: Ordering,
failure: Ordering,
) -> Result<F, F> {
F::Bits::compare_exchange_weak(&self.storage, current.bits(), new.bits(), success, failure)
.map(F::from_bits_retain)
.map_err(F::from_bits_retain)
}
#[inline]
pub fn try_update<Op>(
&self,
set_order: Ordering,
fetch_order: Ordering,
mut f: Op,
) -> Result<F, F>
where
Op: FnMut(F) -> Option<F>,
{
let mut previous = self.load(fetch_order);
loop {
let new = match f(previous) {
Some(new) => new,
None => return Err(previous),
};
match self.compare_exchange_weak(previous, new, set_order, fetch_order) {
Ok(previous) => return Ok(previous),
Err(actual) => previous = actual,
}
}
}
#[inline]
pub fn take(&self, flags: F, ordering: Ordering) -> F {
let complement = Self::retained_complement(flags);
let previous = self.fetch_and(complement, ordering);
previous.intersection(flags)
}
#[inline]
pub fn peek(&self, flags: F, ordering: Ordering) -> F {
self.load(ordering).intersection(flags)
}
#[inline]
pub fn clear(&self, ordering: Ordering) {
self.store(F::empty(), ordering);
}
#[inline]
pub fn into_inner(self) -> F {
F::from_bits_retain(F::Bits::into_inner(self.storage))
}
#[inline]
pub fn set(&self, flags: F, ordering: Ordering) -> F {
self.fetch_or(flags, ordering)
}
#[inline]
pub fn unset(&self, flags: F, ordering: Ordering) -> F {
let complement = Self::retained_complement(flags);
self.fetch_and(complement, ordering)
}
#[inline]
fn retained_complement(flags: F) -> F {
F::from_bits_retain(!flags.bits())
}
}