#![no_std]
use core::cell::UnsafeCell;
use core::mem::MaybeUninit;
use core::sync::atomic;
struct BufferState(atomic::AtomicU8);
pub struct Buffer<T> {
ping: UnsafeCell<T>,
pong: UnsafeCell<T>,
state: BufferState,
}
pub struct Ref<'a, T> {
ptr: &'a T,
state: &'a BufferState,
}
pub struct RefMut<'a, T> {
ptr: &'a mut T,
state: &'a BufferState,
}
impl BufferState {
const LOCK_READ: u8 = 0b0000_0001;
const LOCK_WRITE: u8 = 0b0000_0010;
const MODE_IS_FLIPPED: u8 = 0b0000_0100;
const WANT_MODE_CHANGE: u8 = 0b0000_1000;
const NEW_DATA_READY: u8 = 0b0001_0000;
const fn new() -> Self {
Self(atomic::AtomicU8::new(0))
}
fn lock(&self, condition: fn(u8) -> bool, action: fn(u8) -> u8) -> Option<bool> {
let mut new_flags = None::<u8>;
let _ = self.0.fetch_update(
atomic::Ordering::Acquire,
atomic::Ordering::Relaxed,
|flags| {
if condition(flags) {
new_flags = Some(action(flags));
}
new_flags
},
);
new_flags.map(|f| f & Self::MODE_IS_FLIPPED != 0)
}
fn lock_read(&self, allow_repeated: bool) -> Option<bool> {
self.lock(
if allow_repeated {
|flags| flags & Self::LOCK_READ == 0
} else {
|flags| flags & (Self::LOCK_READ | Self::NEW_DATA_READY) == Self::NEW_DATA_READY
},
|flags| (flags | Self::LOCK_READ) & !Self::NEW_DATA_READY,
)
}
fn lock_write(&self, allow_repeated: bool) -> Option<bool> {
self.lock(
if allow_repeated {
|flags| flags & Self::LOCK_WRITE == 0
} else {
|flags| flags & (Self::LOCK_WRITE | Self::NEW_DATA_READY) == 0
},
|flags| flags | Self::LOCK_WRITE,
)
}
fn release(&self, action: fn(u8) -> u8) {
let _ = self.0.fetch_update(
atomic::Ordering::Release,
atomic::Ordering::Relaxed,
|flags| Some(action(flags)),
); }
fn release_read(&self) {
self.release(|mut flags| {
flags &= !Self::LOCK_READ;
if flags & (Self::LOCK_WRITE | Self::WANT_MODE_CHANGE) == Self::WANT_MODE_CHANGE {
flags &= !Self::WANT_MODE_CHANGE;
flags ^= Self::MODE_IS_FLIPPED;
}
flags
})
}
fn release_write(&self) {
self.release(|mut flags| {
flags &= !Self::LOCK_WRITE;
flags |= Self::NEW_DATA_READY;
if flags & Self::LOCK_READ == 0 {
flags &= !Self::WANT_MODE_CHANGE;
flags ^= Self::MODE_IS_FLIPPED;
} else {
flags |= Self::WANT_MODE_CHANGE;
}
flags
})
}
fn release_and_lock(&self, action: fn(u8) -> u8) -> bool {
let mut new_flags = 0u8;
let _ = self.0.fetch_update(
atomic::Ordering::AcqRel,
atomic::Ordering::Relaxed,
|flags| {
new_flags = action(flags);
Some(new_flags)
},
); new_flags & Self::MODE_IS_FLIPPED != 0
}
fn release_and_lock_read(&self) -> bool {
self.release_and_lock(|mut flags| {
flags |= Self::LOCK_READ;
flags &= !Self::NEW_DATA_READY;
if flags & (Self::LOCK_WRITE | Self::WANT_MODE_CHANGE) == Self::WANT_MODE_CHANGE {
flags &= !Self::WANT_MODE_CHANGE;
flags ^= Self::MODE_IS_FLIPPED;
}
flags
})
}
fn release_and_lock_write(&self) -> bool {
self.release_and_lock(|mut flags| {
if flags & Self::LOCK_WRITE != 0 {
flags |= Self::NEW_DATA_READY;
if flags & Self::LOCK_READ == 0 {
flags &= !Self::WANT_MODE_CHANGE;
flags ^= Self::MODE_IS_FLIPPED;
} else {
flags |= Self::WANT_MODE_CHANGE;
}
} else {
flags |= Self::LOCK_WRITE;
}
flags
})
}
}
impl<'a, T> Ref<'a, T> {
fn new(buf: &'a Buffer<T>, allow_repeated: bool) -> Option<Self> {
let mode = buf.state.lock_read(allow_repeated)?;
Some(Ref {
ptr: unsafe { &*buf.get_pointer(mode, true) },
state: &buf.state,
})
}
}
impl<'a, T> RefMut<'a, T> {
fn new(buf: &'a Buffer<T>, allow_repeated: bool) -> Option<Self> {
let mode = buf.state.lock_write(allow_repeated)?;
Some(RefMut {
ptr: unsafe { &mut *buf.get_pointer(mode, false) },
state: &buf.state,
})
}
}
impl<'a, T> Drop for Ref<'a, T> {
fn drop(&mut self) {
self.state.release_read();
}
}
impl<'a, T> Drop for RefMut<'a, T> {
fn drop(&mut self) {
self.state.release_write();
}
}
impl<'a, T> core::ops::Deref for Ref<'a, T> {
type Target = T;
fn deref(&self) -> &T {
self.ptr
}
}
impl<'a, T> core::ops::Deref for RefMut<'a, T> {
type Target = T;
fn deref(&self) -> &T {
self.ptr
}
}
impl<'a, T> core::ops::DerefMut for RefMut<'a, T> {
fn deref_mut(&mut self) -> &mut T {
self.ptr
}
}
impl<T: Copy> Buffer<T> {
pub const fn new(value: T) -> Self {
Buffer {
ping: UnsafeCell::new(value),
pong: UnsafeCell::new(value),
state: BufferState::new(),
}
}
}
impl<T: Default> Buffer<T> {
pub fn default() -> Self {
Buffer {
ping: UnsafeCell::default(),
pong: UnsafeCell::default(),
state: BufferState::new(),
}
}
}
impl<T> Buffer<MaybeUninit<T>> {
pub const fn uninit() -> Self {
Buffer {
ping: UnsafeCell::new(MaybeUninit::uninit()),
pong: UnsafeCell::new(MaybeUninit::uninit()),
state: BufferState::new(),
}
}
}
impl<T> Buffer<T> {
const fn get_pointer(&self, state: bool, read: bool) -> *mut T {
(if state ^ read { &self.ping } else { &self.pong }).get()
}
pub fn read(&self) -> Option<Ref<T>> {
Ref::new(&self, true)
}
pub fn read_once(&self) -> Option<Ref<T>> {
Ref::new(&self, false)
}
pub fn write(&self) -> Option<RefMut<T>> {
RefMut::new(&self, true)
}
pub fn write_no_discard(&self) -> Option<RefMut<T>> {
RefMut::new(&self, false)
}
pub unsafe fn release_read(&self) {
self.state.release_read();
}
pub unsafe fn release_write(&self) {
self.state.release_write();
}
pub unsafe fn read_unchecked(&self) -> &T {
&*self.get_pointer(self.state.release_and_lock_read(), true)
}
pub unsafe fn write_unchecked(&self) -> &mut T {
&mut *self.get_pointer(self.state.release_and_lock_write(), false)
}
}
unsafe impl<T: Send> Send for Buffer<T> {}
unsafe impl<T: Sync> Sync for Buffer<T> {}