use std::sync::atomic::{AtomicU64, Ordering};
use std::thread::yield_now;
const SENTINEL_VALUE: u64 = 1 << 60;
pub struct AtomicProtectingSpinlock {
tracker: AtomicU64,
}
impl AtomicProtectingSpinlock {
pub fn new() -> Self {
Self {
tracker: AtomicU64::new(0),
}
}
pub fn lock_exclusive(&self) -> APSExclusiveGuard<'_> {
loop {
let current_value = self.tracker.load(Ordering::Relaxed);
if current_value == 0 {
let swap_result = self.tracker.compare_exchange(
0,
SENTINEL_VALUE,
Ordering::Acquire,
Ordering::Acquire,
);
if swap_result.is_ok() {
return APSExclusiveGuard { parent: self };
}
}
yield_now()
}
}
pub fn lock_inclusive(&self) -> Option<APSInclusiveGuard<'_>> {
let old_value = self.tracker.fetch_add(1, Ordering::Acquire);
if old_value < SENTINEL_VALUE {
Some(APSInclusiveGuard { parent: self })
} else {
None
}
}
}
impl Default for AtomicProtectingSpinlock {
fn default() -> Self {
Self::new()
}
}
pub struct APSExclusiveGuard<'a> {
parent: &'a AtomicProtectingSpinlock,
}
impl<'a> Drop for APSExclusiveGuard<'a> {
fn drop(&mut self) {
self.parent.tracker.store(0, Ordering::Release);
}
}
pub struct APSInclusiveGuard<'a> {
parent: &'a AtomicProtectingSpinlock,
}
impl<'a> Drop for APSInclusiveGuard<'a> {
fn drop(&mut self) {
self.parent.tracker.fetch_sub(1, Ordering::Release);
}
}