#![no_std]
#![warn(
elided_lifetimes_in_paths,
explicit_outlives_requirements,
missing_debug_implementations,
missing_docs,
semicolon_in_expressions_from_macros,
single_use_lifetimes,
trivial_casts,
trivial_numeric_casts,
unreachable_pub,
unsafe_op_in_unsafe_fn,
unused_qualifications,
)]
use core::cell::Cell;
use core::ptr::NonNull;
use scopeguard::ScopeGuard;
use lilos::exec::Notify;
#[derive(Default)]
pub struct Handoff<T> {
state: Cell<State<T>>,
ping: Notify,
}
impl<T> Handoff<T> {
pub const fn new() -> Self {
Self {
state: Cell::new(State::Idle),
ping: Notify::new(),
}
}
pub fn split(&mut self) -> (Pusher<'_, T>, Popper<'_, T>) {
(Pusher(self), Popper(self))
}
}
impl<T> Drop for Handoff<T> {
fn drop(&mut self) {
debug_assert!(matches!(self.state.get(), State::Idle));
}
}
impl<T> core::fmt::Debug for Handoff<T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Handoff")
.field("state", &self.state)
.field("ping", &self.ping)
.finish()
}
}
#[derive(Default)]
enum State<T> {
#[default]
Idle,
PushWait(NonNull<Option<T>>),
PopWait(NonNull<Option<T>>),
}
impl<T> core::fmt::Debug for State<T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::Idle => f.write_str("Idle"),
Self::PushWait(p) => f.debug_tuple("PushWait").field(p).finish(),
Self::PopWait(p) => f.debug_tuple("PopWait").field(p).finish(),
}
}
}
impl<T> Copy for State<T> {}
impl<T> Clone for State<T> {
fn clone(&self) -> Self {
*self }
}
pub struct Pusher<'a, T>(&'a Handoff<T>);
impl<T> Pusher<'_, T> {
pub fn try_push(&mut self, value: T) -> Result<(), T> {
match self.0.state.get() {
State::PopWait(dest_ptr) => {
unsafe {
dest_ptr.as_ptr().write(Some(value));
}
self.0.state.set(State::Idle);
self.0.ping.notify();
Ok(())
},
#[cfg(debug_assertions)]
State::PushWait(_) => panic!(),
_ => Err(value),
}
}
pub async fn push(&mut self, value: T) {
let mut guard = scopeguard::guard(Some(value), |v| {
if v.is_some() {
debug_assert!(matches!(self.0.state.get(), State::PushWait(_)));
self.0.state.set(State::Idle);
}
});
loop {
if guard.is_some() {
match self.0.state.get() {
State::Idle => {
self.0.state.set(State::PushWait(
NonNull::from(&mut *guard)
));
self.0.ping.until_next().await;
continue;
}
State::PushWait(_) => {
self.0.ping.until_next().await;
continue;
}
State::PopWait(dest_ptr) => {
unsafe {
dest_ptr.as_ptr().write(ScopeGuard::into_inner(guard));
}
self.0.state.set(State::Idle);
self.0.ping.notify();
return;
},
}
} else {
debug_assert!(matches!(self.0.state.get(), State::Idle | State::PopWait(_)));
break;
}
}
}
}
impl<T> core::fmt::Debug for Pusher<'_, T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_tuple("Pusher").field(&self.0).finish()
}
}
pub struct Popper<'a, T>(&'a Handoff<T>);
impl<T> Popper<'_, T> {
pub fn try_pop(&mut self) -> Option<T> {
match self.0.state.get() {
State::PushWait(src_ptr) => {
let value = unsafe { &mut *src_ptr.as_ptr() }.take();
self.0.state.set(State::Idle);
self.0.ping.notify();
value
},
#[cfg(debug_assertions)]
State::PopWait(_) => panic!(),
_ => None,
}
}
pub async fn pop(&mut self) -> T {
let mut guard = scopeguard::guard(None, |v| {
if v.is_none() {
debug_assert!(matches!(self.0.state.get(), State::PopWait(_)));
self.0.state.set(State::Idle);
}
});
loop {
if guard.is_some() {
debug_assert!(matches!(self.0.state.get(), State::Idle | State::PushWait(_)));
return ScopeGuard::into_inner(guard).unwrap();
} else {
match self.0.state.get() {
State::Idle => {
self.0.state.set(State::PopWait(
NonNull::from(&mut *guard)
));
self.0.ping.until_next().await;
continue;
}
State::PopWait(_) => {
self.0.ping.until_next().await;
continue;
},
State::PushWait(src_ptr) => {
core::mem::swap(
unsafe { &mut *src_ptr.as_ptr() },
&mut *guard,
);
self.0.state.set(State::Idle);
self.0.ping.notify();
return ScopeGuard::into_inner(guard).unwrap();
},
}
}
}
}
}
impl<T> core::fmt::Debug for Popper<'_, T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_tuple("Popper").field(&self.0).finish()
}
}