use core::{
marker::PhantomData,
sync::atomic::{AtomicBool, Ordering},
};
pub trait Flip {
#[must_use]
fn flipped(&self) -> Self;
fn flip(&mut self)
where
Self: Sized,
{
*self = self.flipped();
}
}
impl Flip for bool {
fn flipped(&self) -> Self {
!self
}
}
#[derive(Debug, thiserror::Error)]
pub enum Error<T> {
Phantom(PhantomData<T>),
#[cfg(feature = "std")]
StdLockError(#[from] std::sync::TryLockError<T>),
#[cfg(feature = "parking_lot")]
LockError,
}
#[allow(clippy::module_name_repetitions)]
pub trait FlipImmut<'a, T: Flip + core::fmt::Debug>
where
Self: Sized,
Self::Error: core::fmt::Debug,
{
type Error;
fn try_flip(&'a self) -> Result<(), Self::Error>;
fn flip(&'a self) {
self.try_flip().unwrap();
}
fn try_flipped(&'a self) -> Result<T, Self::Error>;
fn flipped(&'a self) -> T {
self.try_flipped().unwrap()
}
}
impl<'a> FlipImmut<'a, bool> for AtomicBool {
type Error = Error<bool>;
fn try_flip(&'a self) -> Result<(), Self::Error> {
let val = self.load(Ordering::Relaxed);
self.store(!val, Ordering::Relaxed);
Ok(())
}
fn try_flipped(&'a self) -> Result<bool, Self::Error> {
let val = self.load(Ordering::Relaxed);
Ok(!val)
}
}
#[cfg(feature = "std")]
mod _std {
use std::sync::{Mutex, MutexGuard};
use super::{Error, Flip, FlipImmut};
impl<'a, T: Flip + core::fmt::Debug + 'a> FlipImmut<'a, T> for Mutex<T> {
type Error = Error<MutexGuard<'a, T>>;
fn try_flip(&'a self) -> Result<(), Self::Error> {
self.try_lock()?.flip();
Ok(())
}
fn try_flipped(&'a self) -> Result<T, Self::Error> {
Ok(self.try_lock()?.flipped())
}
}
#[cfg(feature = "parking_lot")]
impl<'a, T: Flip + core::fmt::Debug + 'a> FlipImmut<'a, T> for parking_lot::Mutex<T> {
type Error = Error<MutexGuard<'a, T>>;
fn flip(&'a self) {
self.lock().flip();
}
fn try_flip(&'a self) -> Result<(), Self::Error> {
match self.try_lock() {
Some(mut v) => {
v.flip();
Ok(())
}
None => Err(Error::LockError),
}
}
fn try_flipped(&'a self) -> Result<T, Self::Error> {
match self.try_lock() {
Some(v) => Ok(v.flipped()),
None => Err(Error::LockError),
}
}
}
}
#[cfg(test)]
mod tests {
use core::sync::atomic::AtomicBool;
use std::sync::Mutex;
use super::*;
#[test]
fn test_flip_bool() {
let mut bool = true;
assert!(bool);
bool.flip();
assert!(!bool);
assert!(bool.flipped());
}
static BOOL: Mutex<bool> = Mutex::new(true);
#[test]
fn test_flip_mutex() {
assert!(*BOOL.lock().unwrap());
BOOL.flip();
assert!(!*BOOL.lock().unwrap());
assert!(BOOL.flipped());
}
#[cfg(feature = "parking_lot")]
static PARKING_LOT: parking_lot::Mutex<bool> = parking_lot::Mutex::new(true);
#[cfg(feature = "parking_lot")]
#[test]
fn test_flip_parking_mutex() {
assert!(*PARKING_LOT.lock());
PARKING_LOT.flip();
assert!(!*PARKING_LOT.lock());
assert!(PARKING_LOT.flipped());
}
static ATOMIC: AtomicBool = AtomicBool::new(true);
impl Flip for AtomicBool {
fn flipped(&self) -> Self {
let mut bool = self.load(std::sync::atomic::Ordering::Relaxed);
bool.flip();
Self::new(bool)
}
}
#[test]
fn test_flip_atomic() {
let bool = ATOMIC.load(std::sync::atomic::Ordering::Relaxed);
assert!(bool);
ATOMIC.flip();
}
}