#![no_std]
use core::{
cell::UnsafeCell,
sync::atomic::{AtomicU64, Ordering::SeqCst},
};
use atomic_memcpy::{atomic_load, atomic_store};
cfg_select! {
feature = "alloc" => {
mod _alloc;
pub use _alloc::*;
}
_ => {}
}
cfg_select! {
feature = "std" => {
mod _std;
pub use _std::*;
}
_ => {}
}
pub struct UnsafeSeqLock<T> {
version: AtomicU64,
value: UnsafeCell<T>,
}
impl<T> UnsafeSeqLock<T> {
pub const fn new(value: T) -> Self {
Self {
version: AtomicU64::new(0),
value: UnsafeCell::new(value),
}
}
pub unsafe fn read(&self) -> T {
loop {
if let Some(v) = unsafe { self.try_read() } {
break v;
}
}
}
pub unsafe fn read_version(&self) -> (u64, T) {
loop {
if let Some((v0, v)) = unsafe { self.try_read_version() } {
break (v0, v);
}
}
}
pub unsafe fn try_read(&self) -> Option<T> {
unsafe { self.try_read_version() }.map(|(_, v)| v)
}
pub unsafe fn try_read_version(&self) -> Option<(u64, T)> {
let Self { version, value } = self;
let v0 = version.load(SeqCst);
match v0.is_multiple_of(2) {
true => {
let mb = unsafe { atomic_load(value.get(), SeqCst) };
let v1 = version.load(SeqCst);
match v0 == v1 {
true => Some((v0, unsafe { mb.assume_init() })),
false => None,
}
}
false => None,
}
}
pub unsafe fn write(&self, value: T) {
unsafe { self.write_version(value) };
}
pub unsafe fn write_version(&self, value: T) -> u64 {
let Self { version, value: v } = self;
version.fetch_add(1, SeqCst);
unsafe { atomic_store(v.get(), value, SeqCst) }
version.fetch_add(1, SeqCst)
}
}
unsafe impl<T: Send> Send for UnsafeSeqLock<T> {}
unsafe impl<T: Sync> Sync for UnsafeSeqLock<T> {}