use core::cell::RefCell;
use core::future::Future;
use core::pin::Pin;
use core::task::{Context, Poll, Waker};
use critical_section::Mutex;
pub const WAITERS: usize = 4;
#[derive(Debug)]
pub(crate) struct Notify {
inner: Mutex<RefCell<State>>,
}
#[derive(Debug)]
struct State {
generation: u32,
waiting: [Option<Waker>; WAITERS],
}
impl Notify {
pub(crate) const fn new() -> Self {
Self {
inner: Mutex::new(RefCell::new(State {
generation: 0,
waiting: [const { None }; WAITERS],
})),
}
}
pub(crate) fn generation(&self) -> u32 {
critical_section::with(|token| self.inner.borrow(token).borrow().generation)
}
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::take(&mut state.waiting)
});
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.waiting[0].replace(waker.clone())
});
if let Some(evicted) = evicted {
evicted.wake();
}
}
}
pub struct Changes<T: Clone + 'static> {
cell: &'static crate::ConfigCell<T>,
seen: u32,
}
impl<T: Clone + 'static> Changes<T> {
pub(crate) fn new(cell: &'static crate::ConfigCell<T>) -> 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> core::fmt::Debug for Changes<T> {
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> {
changes: &'a mut Changes<T>,
}
impl<T: Clone + 'static> Future for Changed<'_, T> {
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>(changes: &mut Changes<T>) -> Option<T> {
let current = changes.cell.notify().generation();
if current == changes.seen {
return None;
}
changes.seen = current;
changes.cell.get()
}