use std::sync::atomic::{AtomicU8, Ordering};
#[derive(Debug)]
pub struct QuickBool {
state: AtomicU8,
}
impl QuickBool {
const UNSET: u8 = 0;
const FALSE: u8 = 0xFF; const TRUE: u8 = 1;
pub const fn new() -> Self {
Self {
state: AtomicU8::new(Self::UNSET),
}
}
pub fn get_or_set<F>(&self, f: F) -> bool
where
F: FnOnce() -> bool,
{
let current = self.state.load(Ordering::Relaxed);
match current {
Self::TRUE => true,
Self::FALSE => false,
Self::UNSET => {
let computed_value = f();
let target_state = if computed_value { Self::TRUE } else { Self::FALSE };
match self.state.compare_exchange(
Self::UNSET,
target_state,
Ordering::AcqRel,
Ordering::Acquire,
) {
Ok(_) => computed_value,
Err(actual) => {
actual == Self::TRUE
}
}
}
_ => unreachable!("Invalid state value"),
}
}
#[inline]
pub fn get(&self) -> Option<bool> {
match self.state.load(Ordering::Relaxed) {
Self::TRUE => Some(true),
Self::FALSE => Some(false),
Self::UNSET => None,
_ => unreachable!("Invalid state value"),
}
}
#[inline]
pub fn is_set(&self) -> bool {
self.state.load(Ordering::Relaxed) != Self::UNSET
}
pub fn reset(&self) {
self.state.store(Self::UNSET, Ordering::Release);
}
}
impl Default for QuickBool {
fn default() -> Self {
Self::new()
}
}
impl Clone for QuickBool {
fn clone(&self) -> Self {
Self {
state: AtomicU8::new(self.state.load(Ordering::Relaxed)),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::Arc;
use std::thread;
#[test]
fn test_new_quick_bool() {
let qb = QuickBool::new();
assert_eq!(qb.get(), None);
assert!(!qb.is_set());
}
#[test]
fn test_get_or_set_once() {
let qb = QuickBool::new();
let value = qb.get_or_set(|| true);
assert!(value);
assert_eq!(qb.get(), Some(true));
assert!(qb.is_set());
}
#[test]
fn test_get_or_set_multiple_calls() {
let qb = QuickBool::new();
let mut call_count = 0;
let value1 = qb.get_or_set(|| {
call_count += 1;
false
});
let value2 = qb.get_or_set(|| {
call_count += 1;
panic!("This should not execute");
});
assert!(!value1);
assert!(!value2);
assert_eq!(call_count, 1);
assert_eq!(qb.get(), Some(false));
}
#[test]
fn test_reset() {
let qb = QuickBool::new();
qb.get_or_set(|| true);
assert!(qb.is_set());
qb.reset();
assert!(!qb.is_set());
assert_eq!(qb.get(), None);
}
#[test]
fn test_concurrent_access() {
let qb = Arc::new(QuickBool::new());
let mut handles = vec![];
for _ in 0..10 {
let qb_clone = Arc::clone(&qb);
let handle = thread::spawn(move || {
qb_clone.get_or_set(|| {
thread::sleep(std::time::Duration::from_millis(1));
true
})
});
handles.push(handle);
}
let results: Vec<bool> = handles.into_iter()
.map(|h| h.join().unwrap())
.collect();
assert!(results.iter().all(|&x| x));
assert_eq!(qb.get(), Some(true));
}
#[test]
fn test_clone() {
let qb1 = QuickBool::new();
qb1.get_or_set(|| true);
let qb2 = qb1.clone();
assert_eq!(qb2.get(), Some(true));
assert!(qb2.is_set());
}
}