use core::sync::atomic::{AtomicUsize, Ordering};

/// Simple spinloop-based fence guaranteeing that the execution resumes only after its condition is
/// met.
pub struct SpinFence {
    value: AtomicUsize,
}

// SpinFence impls
impl SpinFence {
    /// Constructs a new [SpinFence]
    pub const fn new() -> Self {
        Self {
            value: AtomicUsize::new(0),
        }
    }

    /// Resets a fence back to its original state
    pub fn reset(&self) {
        self.value.store(0, Ordering::Release);
    }

    /// "Signals" a fence, incrementing its internal counter by one
    pub fn signal(&self) {
        self.value.fetch_add(1, Ordering::SeqCst);
    }

    /// Waits until the fence is signalled at least the amount of times specified
    pub fn wait_all(&self, count: usize) {
        while self.value.load(Ordering::Acquire) < count {
            core::hint::spin_loop();
        }
    }

    /// Waits until the fence is signalled at least once
    pub fn wait_one(&self) {
        self.wait_all(1);
    }

    /// Returns `true` if the fence has been signalled at least the amount of times specified
    pub fn try_wait_all(&self, count: usize) -> bool {
        self.value.load(Ordering::Acquire) >= count
    }
}
