use core::cell::RefCell;
use core::future::Future;
use core::pin::Pin;
use core::task::{Context, Poll, Waker};
use critical_section::Mutex;
use crate::DEFAULT_WAITERS;
#[derive(Debug)]
pub(crate) struct Notify<const WAITERS: usize> {
inner: Mutex<RefCell<State<WAITERS>>>,
}
#[derive(Debug)]
struct State<const WAITERS: usize> {
generation: u32,
evictions: u32,
waiting: [Option<Waker>; WAITERS],
}
impl<const WAITERS: usize> Notify<WAITERS> {
pub(crate) const fn new() -> Self {
Self {
inner: Mutex::new(RefCell::new(State {
generation: 0,
evictions: 0,
waiting: [const { None }; WAITERS],
})),
}
}
pub(crate) fn generation(&self) -> u32 {
critical_section::with(|token| self.inner.borrow(token).borrow().generation)
}
pub(crate) fn evictions(&self) -> u32 {
critical_section::with(|token| self.inner.borrow(token).borrow().evictions)
}
pub(crate) fn bump(&self) {
let woken = critical_section::with(|token| {
let mut state = self.inner.borrow(token).borrow_mut();
state.generation = state.generation.wrapping_add(1);
core::mem::replace(&mut state.waiting, [const { None }; WAITERS])
});
for waker in woken.into_iter().flatten() {
waker.wake();
}
}
fn register(&self, waker: &Waker) {
let evicted = critical_section::with(|token| {
let mut state = self.inner.borrow(token).borrow_mut();
for slot in &mut state.waiting {
match slot {
Some(existing) if existing.will_wake(waker) => return None,
Some(_) => {}
None => {
*slot = Some(waker.clone());
return None;
}
}
}
state.evictions = state.evictions.saturating_add(1);
state.waiting[0].replace(waker.clone())
});
if let Some(evicted) = evicted {
evicted.wake();
}
}
}
pub struct Changes<T: Clone + 'static, const WAITERS: usize = DEFAULT_WAITERS> {
cell: &'static crate::ConfigCell<T, WAITERS>,
seen: u32,
}
impl<T: Clone + 'static, const WAITERS: usize> Changes<T, WAITERS> {
pub(crate) fn new(cell: &'static crate::ConfigCell<T, WAITERS>) -> Self {
Self {
seen: cell.notify().generation(),
cell,
}
}
pub fn changed(&mut self) -> impl Future<Output = T> + '_ {
Changed { changes: self }
}
#[must_use]
pub const fn seen(&self) -> u32 {
self.seen
}
}
impl<T: Clone + 'static, const WAITERS: usize> core::fmt::Debug for Changes<T, WAITERS> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Changes")
.field("seen", &self.seen)
.finish_non_exhaustive()
}
}
struct Changed<'a, T: Clone + 'static, const WAITERS: usize> {
changes: &'a mut Changes<T, WAITERS>,
}
impl<T: Clone + 'static, const WAITERS: usize> Future for Changed<'_, T, WAITERS> {
type Output = T;
fn poll(self: Pin<&mut Self>, context: &mut Context<'_>) -> Poll<T> {
let changes = &mut self.get_mut().changes;
if let Some(value) = take(changes) {
return Poll::Ready(value);
}
changes.cell.notify().register(context.waker());
match take(changes) {
Some(value) => Poll::Ready(value),
None => Poll::Pending,
}
}
}
fn take<T: Clone + 'static, const WAITERS: usize>(changes: &mut Changes<T, WAITERS>) -> Option<T> {
let current = changes.cell.notify().generation();
if current == changes.seen {
return None;
}
changes.seen = current;
changes.cell.get()
}