extern crate alloc;
use alloc::boxed::Box;
use alloc::sync::{Arc, Weak};
use alloc::vec::Vec;
use core::cell::UnsafeCell;
use core::sync::atomic::{AtomicU64, Ordering};
use spin::Mutex;
use crate::ring::{Padded, RingIndex};
struct EventRing<T> {
slots: Box<[UnsafeCell<T>]>,
index: RingIndex,
cursor: Padded<AtomicU64>,
trackers: Mutex<Vec<Weak<Padded<AtomicU64>>>>,
}
unsafe impl<T: Send + Sync> Send for EventRing<T> {}
unsafe impl<T: Send + Sync> Sync for EventRing<T> {}
impl<T> EventRing<T> {
fn slowest(&self) -> Option<u64> {
let mut trackers = self.trackers.lock();
let mut min = u64::MAX;
let mut any = false;
trackers.retain(|weak| match weak.upgrade() {
Some(t) => {
min = min.min(t.0.load(Ordering::Acquire));
any = true;
true
}
None => false,
});
any.then_some(min)
}
}
pub struct EventPublisher<T> {
ring: Arc<EventRing<T>>,
seq: u64,
cached_slowest: u64,
}
unsafe impl<T: Send + Sync> Send for EventPublisher<T> {}
impl<T> EventPublisher<T> {
fn has_room(&mut self) -> bool {
let capacity = self.ring.index.capacity;
if self.seq >= self.cached_slowest + capacity {
match self.ring.slowest() {
Some(slowest) => {
self.cached_slowest = slowest;
if self.seq >= slowest + capacity {
return false;
}
}
None => return true,
}
}
true
}
pub fn publish(&mut self, f: impl FnOnce(&mut T)) {
while !self.has_room() {
core::hint::spin_loop();
}
self.write(f);
}
pub fn try_publish(&mut self, f: impl FnOnce(&mut T)) -> bool {
if !self.has_room() {
return false;
}
self.write(f);
true
}
fn write(&mut self, f: impl FnOnce(&mut T)) {
let idx = self.ring.index.slot(self.seq);
f(unsafe { &mut *self.ring.slots[idx].get() });
self.ring.cursor.0.store(self.seq, Ordering::Release);
self.seq += 1;
}
pub fn published(&self) -> u64 {
self.seq
}
pub fn capacity(&self) -> u64 {
self.ring.index.capacity
}
}
pub struct EventSubscribable<T> {
ring: Arc<EventRing<T>>,
}
unsafe impl<T: Send + Sync> Send for EventSubscribable<T> {}
unsafe impl<T: Send + Sync> Sync for EventSubscribable<T> {}
impl<T> Clone for EventSubscribable<T> {
fn clone(&self) -> Self {
EventSubscribable {
ring: self.ring.clone(),
}
}
}
impl<T> EventSubscribable<T> {
pub fn subscribe(&self) -> EventSubscriber<T> {
let mut trackers = self.ring.trackers.lock();
let head = self.ring.cursor.0.load(Ordering::Acquire);
let start = if head == u64::MAX { 0 } else { head + 1 };
let tracker = Arc::new(Padded(AtomicU64::new(start)));
trackers.push(Arc::downgrade(&tracker));
drop(trackers);
EventSubscriber {
ring: self.ring.clone(),
cursor: start,
tracker,
}
}
}
pub struct EventSubscriber<T> {
ring: Arc<EventRing<T>>,
cursor: u64,
tracker: Arc<Padded<AtomicU64>>,
}
unsafe impl<T: Send + Sync> Send for EventSubscriber<T> {}
impl<T> EventSubscriber<T> {
pub fn process<R>(&mut self, f: impl FnOnce(&T) -> R) -> Option<R> {
let head = self.ring.cursor.0.load(Ordering::Acquire);
if head == u64::MAX || self.cursor > head {
return None;
}
let idx = self.ring.index.slot(self.cursor);
let out = f(unsafe { &*self.ring.slots[idx].get() });
self.cursor += 1;
self.tracker.0.store(self.cursor, Ordering::Release);
Some(out)
}
pub fn cursor(&self) -> u64 {
self.cursor
}
pub fn pending(&self) -> u64 {
let head = self.ring.cursor.0.load(Ordering::Acquire);
if head == u64::MAX || self.cursor > head {
0
} else {
head - self.cursor + 1
}
}
}
pub fn event_channel<T: Send + Sync + 'static>(
capacity: usize,
mut factory: impl FnMut() -> T,
) -> (EventPublisher<T>, EventSubscribable<T>) {
let index = RingIndex::new(capacity);
let slots: Vec<UnsafeCell<T>> = (0..capacity).map(|_| UnsafeCell::new(factory())).collect();
let ring = Arc::new(EventRing {
slots: slots.into_boxed_slice(),
index,
cursor: Padded(AtomicU64::new(u64::MAX)),
trackers: Mutex::new(Vec::new()),
});
(
EventPublisher {
ring: ring.clone(),
seq: 0,
cached_slowest: 0,
},
EventSubscribable { ring },
)
}