use std::cell::RefCell;
use std::collections::VecDeque;
use std::rc::{Rc, Weak};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::{Arc, Mutex, Weak as ArcWeak};
use crate::reactive::runtime::{self, CurrentOwnerGuard, OwnerData};
type RemoteWaker = Arc<dyn Fn() + Send + Sync>;
const QUEUE_POISONED: &str = "a channel queue lock is poisoned, although it \
is never held while user code runs";
const WAKER_POISONED: &str = "a channel waker lock is poisoned, although it \
is never held while user code runs";
pub fn channel<T: Send + 'static>(handler: impl FnMut(T) + 'static) -> UiSender<T> {
let owner = runtime::running_owner();
let book = owner.as_ref().and_then(book_of);
let Some((owner, book)) = owner.zip(book) else {
debug_assert!(
false,
"channel() ran where no application owner is current, so no \
frame will ever run its handler and the sender it returns \
delivers nothing. Create channels inside an application, for \
example in a build closure"
);
return UiSender {
shared: Arc::new(Shared {
queue: Mutex::new(Queue::Closed),
waker: Mutex::new(None),
senders: AtomicUsize::new(1),
}),
};
};
let shared = Arc::new(Shared {
queue: Mutex::new(Queue::Open(VecDeque::new())),
waker: Mutex::new(book.borrow().waker.clone()),
senders: AtomicUsize::new(1),
});
let mut handler = Box::new(handler) as Box<dyn FnMut(T)>;
let draining = Arc::clone(&shared);
let run: Drain = Box::new(move |bound: usize| {
for _ in 0..bound {
let value = {
let mut queue = draining.queue.lock().expect(QUEUE_POISONED);
match &mut *queue {
Queue::Open(values) => values.pop_front(),
Queue::Closed => None,
}
};
let Some(value) = value else {
break;
};
handler(value);
}
});
let id = {
let mut book = book.borrow_mut();
let id = book.next_id;
book.next_id += 1;
book.receivers.push(Rc::new(Receiver {
id,
owner: Rc::downgrade(&owner),
run: RefCell::new(Some(run)),
channel: Arc::clone(&shared) as Arc<dyn ErasedChannel>,
}));
id
};
let weak_book = Rc::downgrade(&book);
let closing: ArcWeak<dyn ErasedChannel> = Arc::downgrade(&shared) as ArcWeak<dyn ErasedChannel>;
owner.state.borrow_mut().cleanups.push(Box::new(move || {
if let Some(closing) = closing.upgrade() {
closing.close();
}
if let Some(book) = weak_book.upgrade() {
let removed = {
let mut book = book.borrow_mut();
let at = book.receivers.iter().position(|other| other.id == id);
at.map(|at| book.receivers.remove(at))
};
drop(removed);
}
}));
UiSender { shared }
}
#[must_use = "a channel delivers only through its sender, so a dropped \
sender delivers nothing"]
pub struct UiSender<T> {
shared: Arc<Shared<T>>,
}
impl<T> Clone for UiSender<T> {
fn clone(&self) -> Self {
self.shared.senders.fetch_add(1, Ordering::Relaxed);
UiSender {
shared: Arc::clone(&self.shared),
}
}
}
impl<T> Drop for UiSender<T> {
fn drop(&mut self) {
self.shared.senders.fetch_sub(1, Ordering::Release);
}
}
impl<T> std::fmt::Debug for UiSender<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.pad("UiSender")
}
}
impl<T: Send> UiSender<T> {
pub fn send(&self, value: T) -> Result<(), Closed<T>> {
let first = {
let mut queue = self.shared.queue.lock().expect(QUEUE_POISONED);
match &mut *queue {
Queue::Closed => return Err(Closed { value }),
Queue::Open(values) => {
let first = values.is_empty();
values.push_back(value);
first
}
}
};
if first {
self.shared.wake();
}
Ok(())
}
}
#[derive(Debug)]
pub struct Closed<T> {
pub value: T,
}
enum Queue<T> {
Open(VecDeque<T>),
Closed,
}
struct Shared<T> {
queue: Mutex<Queue<T>>,
waker: Mutex<Option<RemoteWaker>>,
senders: AtomicUsize,
}
trait ErasedChannel {
fn pending(&self) -> usize;
fn close(&self);
fn set_waker(&self, waker: RemoteWaker);
fn wake(&self);
fn finished(&self) -> bool;
}
impl<T> ErasedChannel for Shared<T> {
fn pending(&self) -> usize {
let queue = self.queue.lock().expect(QUEUE_POISONED);
match &*queue {
Queue::Open(values) => values.len(),
Queue::Closed => 0,
}
}
fn close(&self) {
*self.queue.lock().expect(QUEUE_POISONED) = Queue::Closed;
}
fn set_waker(&self, waker: RemoteWaker) {
*self.waker.lock().expect(WAKER_POISONED) = Some(waker);
}
fn wake(&self) {
let waker = self.waker.lock().expect(WAKER_POISONED).clone();
if let Some(waker) = waker {
waker();
}
}
fn finished(&self) -> bool {
self.senders.load(Ordering::Acquire) == 0 && self.pending() == 0
}
}
struct Receiver {
id: u64,
owner: Weak<OwnerData>,
run: RefCell<Option<Drain>>,
channel: Arc<dyn ErasedChannel>,
}
type Drain = Box<dyn FnMut(usize)>;
#[derive(Default)]
pub(crate) struct ChannelBook {
waker: Option<RemoteWaker>,
next_id: u64,
receivers: Vec<Rc<Receiver>>,
}
pub(crate) fn drain_all(book: &Rc<RefCell<ChannelBook>>) {
let mut reached: Option<u64> = None;
loop {
let receiver = {
let book = book.borrow();
let at = match reached {
Some(id) => book.receivers.partition_point(|other| other.id <= id),
None => 0,
};
match book.receivers.get(at) {
Some(receiver) => Rc::clone(receiver),
None => break,
}
};
reached = Some(receiver.id);
let Some(owner) = receiver
.owner
.upgrade()
.filter(|owner| !owner.cleaned.get())
else {
continue;
};
let bound = receiver.channel.pending();
if bound == 0 {
continue;
}
let Ok(mut slot) = receiver.run.try_borrow_mut() else {
continue;
};
let Some(mut run) = slot.take() else {
continue;
};
drop(slot);
{
let _guard = CurrentOwnerGuard::enter(&owner);
let closes_on_unwind = CloseOnUnwind(&*receiver.channel);
run(bound);
std::mem::forget(closes_on_unwind);
}
*receiver.run.borrow_mut() = Some(run);
if receiver.channel.pending() > 0 {
receiver.channel.wake();
}
}
loop {
let finished = {
let mut book = book.borrow_mut();
let at = book
.receivers
.iter()
.position(|receiver| receiver.channel.finished());
at.map(|at| book.receivers.remove(at))
};
let Some(finished) = finished else {
break;
};
drop(finished);
}
}
struct CloseOnUnwind<'a>(&'a dyn ErasedChannel);
impl Drop for CloseOnUnwind<'_> {
fn drop(&mut self) {
self.0.close();
}
}
pub(crate) fn has_pending(book: &Rc<RefCell<ChannelBook>>) -> bool {
book.borrow()
.receivers
.iter()
.any(|receiver| receiver.channel.pending() > 0)
}
pub(crate) fn install_waker(book: &Rc<RefCell<ChannelBook>>, waker: RemoteWaker) {
let mut borrowed = book.borrow_mut();
for receiver in &borrowed.receivers {
receiver.channel.set_waker(Arc::clone(&waker));
}
borrowed.waker = Some(waker);
}
fn book_of(owner: &Rc<OwnerData>) -> Option<Rc<RefCell<ChannelBook>>> {
let mut current = Rc::clone(owner);
loop {
let book = current
.channel_book
.borrow()
.as_ref()
.and_then(Weak::upgrade);
if book.is_some() {
return book;
}
let parent = current.parent.as_ref()?.upgrade()?;
current = parent;
}
}
#[cfg(test)]
#[path = "channel_tests.rs"]
mod tests;