use std::fmt;
use std::future::poll_fn;
use std::ops::Deref;
use std::pin::Pin;
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::{Arc, Mutex, RwLock, RwLockReadGuard};
use std::task::{Context, Poll};
use crate::op::completion::{CompletionFuture, CompletionHandle};
use crate::sys::current::channel::runtime_waiter;
pub fn channel<T: Send + 'static>(initial: T) -> (Sender<T>, Receiver<T>) {
let shared = Arc::new(Shared {
value: RwLock::new(initial),
version: AtomicU64::new(0),
book: Arc::new(Mutex::new(Book {
sender_count: 1,
receiver_count: 1,
waiters: Vec::new(),
next_waiter_id: 1,
})),
});
(
Sender {
shared: Arc::clone(&shared),
},
Receiver {
shared,
version: 0,
wait: None,
},
)
}
pub struct Sender<T: Send + 'static> {
shared: Arc<Shared<T>>,
}
pub struct Receiver<T: Send + 'static> {
shared: Arc<Shared<T>>,
version: u64,
wait: Option<CompletionFuture<Result<u64, RecvError>>>,
}
pub struct Ref<'a, T: Send + 'static> {
guard: RwLockReadGuard<'a, T>,
}
struct Shared<T: Send + 'static> {
value: RwLock<T>,
version: AtomicU64,
book: Arc<Mutex<Book>>,
}
struct Book {
sender_count: usize,
receiver_count: usize,
waiters: Vec<WatchWaiter>,
next_waiter_id: usize,
}
struct WatchWaiter {
id: usize,
version: u64,
handle: CompletionHandle<Result<u64, RecvError>>,
}
#[derive(Debug, Eq, PartialEq)]
pub struct SendError<T>(pub T);
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct RecvError;
impl Book {
fn enqueue_waiter(
&mut self,
version: u64,
handle: CompletionHandle<Result<u64, RecvError>>,
) -> usize {
let id = self.next_waiter_id;
self.next_waiter_id = self.next_waiter_id.wrapping_add(1);
self.waiters.push(WatchWaiter {
id,
version,
handle,
});
id
}
fn remove_waiter(&mut self, waiter_id: usize) {
if let Some(index) = self
.waiters
.iter()
.position(|waiter| waiter.id == waiter_id)
{
self.waiters.swap_remove(index);
}
}
fn wake_changed(
&mut self,
current_version: u64,
) -> Vec<CompletionHandle<Result<u64, RecvError>>> {
let mut ready = Vec::new();
let mut index = 0;
while index < self.waiters.len() {
if self.waiters[index].version < current_version {
ready.push(self.waiters.swap_remove(index).handle);
} else {
index += 1;
}
}
ready
}
fn close_sender(&mut self) -> Vec<CompletionHandle<Result<u64, RecvError>>> {
self.sender_count = self
.sender_count
.checked_sub(1)
.expect("sender count underflow: more drops than creates");
if self.sender_count == 0 {
self.waiters.drain(..).map(|waiter| waiter.handle).collect()
} else {
Vec::new()
}
}
}
impl<T: Send + 'static> Shared<T> {
fn lock_book(&self) -> std::sync::MutexGuard<'_, Book> {
self.book
.lock()
.expect("watch state should not be poisoned")
}
fn write_value(&self, mutate: impl FnOnce(&mut T)) -> u64 {
let mut slot = self
.value
.write()
.expect("watch state should not be poisoned");
mutate(&mut slot);
self.version.fetch_add(1, Ordering::Release) + 1
}
}
impl<T: Send + 'static> Clone for Sender<T> {
fn clone(&self) -> Self {
self.shared.lock_book().sender_count += 1;
Self {
shared: Arc::clone(&self.shared),
}
}
}
impl<T: Send + 'static> Clone for Receiver<T> {
fn clone(&self) -> Self {
self.shared.lock_book().receiver_count += 1;
Self {
shared: Arc::clone(&self.shared),
version: self.version,
wait: None,
}
}
}
impl<T: Send + 'static> Sender<T> {
pub fn send(&self, value: T) -> Result<(), SendError<T>> {
let waiters = {
let mut book = self.shared.lock_book();
if book.receiver_count == 0 {
return Err(SendError(value));
}
let version = self.shared.write_value(|slot| *slot = value);
book.wake_changed(version)
};
self.complete_changed(waiters);
Ok(())
}
pub fn send_modify(&self, f: impl FnOnce(&mut T)) {
let waiters = {
let mut book = self.shared.lock_book();
let version = self.shared.write_value(f);
book.wake_changed(version)
};
self.complete_changed(waiters);
}
pub fn send_if_modified(&self, f: impl FnOnce(&mut T) -> bool) -> bool {
let waiters = {
let mut book = self.shared.lock_book();
let version = {
let mut slot = self
.shared
.value
.write()
.expect("watch state should not be poisoned");
if !f(&mut slot) {
return false;
}
self.shared.version.fetch_add(1, Ordering::Release) + 1
};
book.wake_changed(version)
};
self.complete_changed(waiters);
true
}
pub fn borrow(&self) -> Ref<'_, T> {
Ref {
guard: self
.shared
.value
.read()
.expect("watch state should not be poisoned"),
}
}
pub fn subscribe(&self) -> Receiver<T> {
let version = {
let mut book = self.shared.lock_book();
book.receiver_count += 1;
self.shared.version.load(Ordering::Acquire)
};
Receiver {
shared: Arc::clone(&self.shared),
version,
wait: None,
}
}
pub fn receiver_count(&self) -> usize {
self.shared.lock_book().receiver_count
}
fn complete_changed(&self, waiters: Vec<CompletionHandle<Result<u64, RecvError>>>) {
let version = self.shared.version.load(Ordering::Acquire);
for waiter in waiters {
waiter.complete(Ok(version));
}
}
}
impl<T: Send + 'static> Receiver<T> {
pub async fn changed(&mut self) -> Result<(), RecvError> {
poll_fn(|cx| self.poll_changed(cx)).await
}
pub fn borrow(&self) -> Ref<'_, T> {
Ref {
guard: self
.shared
.value
.read()
.expect("watch state should not be poisoned"),
}
}
pub fn borrow_and_update(&mut self) -> Ref<'_, T> {
let guard = self
.shared
.value
.read()
.expect("watch state should not be poisoned");
self.version = self.shared.version.load(Ordering::Acquire);
Ref { guard }
}
fn poll_changed(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), RecvError>> {
if let Some(future) = self.wait.as_mut() {
match Pin::new(future).poll(cx) {
Poll::Ready(result) => {
self.wait.take();
match result {
Ok(version) if version > self.version => {
self.version = version;
Poll::Ready(Ok(()))
}
Ok(_) => {
self.poll_changed(cx)
}
Err(_) => Poll::Ready(Err(RecvError)),
}
}
Poll::Pending => Poll::Pending,
}
} else {
let (future, handle) = runtime_waiter::<Result<u64, RecvError>>();
let immediate = {
let mut book = self.shared.lock_book();
let current = self.shared.version.load(Ordering::Acquire);
if current > self.version {
Some(Ok(current))
} else if book.sender_count == 0 {
Some(Err(RecvError))
} else {
let waiter_id = book.enqueue_waiter(self.version, handle.clone());
set_cancel_waiter(&handle, &self.shared.book, waiter_id);
None
}
};
if let Some(result) = immediate {
handle.complete(result);
}
self.wait = Some(future);
self.poll_changed(cx)
}
}
}
impl<'a, T: Send + 'static> Deref for Ref<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.guard
}
}
fn set_cancel_waiter(
handle: &CompletionHandle<Result<u64, RecvError>>,
book: &Arc<Mutex<Book>>,
waiter_id: usize,
) {
let cancel_book = Arc::clone(book);
let cancel_handle = handle.clone();
handle.set_cancel(move || {
let mut book = cancel_book
.lock()
.expect("watch state should not be poisoned");
book.remove_waiter(waiter_id);
drop(book);
cancel_handle.finish(None);
});
}
impl<T: Send + 'static> Drop for Sender<T> {
fn drop(&mut self) {
let waiters = {
let mut book = self.shared.lock_book();
book.close_sender()
};
for waiter in waiters {
waiter.complete(Err(RecvError));
}
}
}
impl<T: Send + 'static> Drop for Receiver<T> {
fn drop(&mut self) {
let mut book = self.shared.lock_book();
book.receiver_count = book
.receiver_count
.checked_sub(1)
.expect("receiver count underflow: more drops than creates");
}
}
impl<T> fmt::Display for SendError<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "channel closed")
}
}
impl<T: fmt::Debug> std::error::Error for SendError<T> {}
impl fmt::Display for RecvError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "channel closed")
}
}
impl std::error::Error for RecvError {}
#[cfg(test)]
mod tests {
use std::sync::{Arc, Mutex};
use crate::{queue_macrotask, run, spawn};
use super::{RecvError, channel};
#[test]
fn receiver_borrows_initial_value() {
let (_sender, receiver) = channel(5usize);
assert_eq!(*receiver.borrow(), 5);
}
#[test]
fn two_borrows_on_same_thread_do_not_deadlock() {
let (sender, receiver) = channel(7usize);
let a = receiver.borrow();
let b = sender.borrow();
let c = receiver.borrow();
assert_eq!((*a, *b, *c), (7, 7, 7));
}
#[test]
fn changed_fires_after_send() {
let observed = Arc::new(Mutex::new(None::<usize>));
let observed_for_task = Arc::clone(&observed);
queue_macrotask(move || {
let (sender, mut receiver) = channel(1usize);
spawn(async move {
sender.send(2).unwrap();
receiver.changed().await.unwrap();
*observed_for_task.lock().unwrap() = Some(*receiver.borrow());
});
});
run();
assert_eq!(*observed.lock().unwrap(), Some(2));
}
#[test]
fn rapid_sends_coalesce_to_latest_value() {
let observed = Arc::new(Mutex::new(None::<usize>));
let observed_for_task = Arc::clone(&observed);
queue_macrotask(move || {
let (sender, mut receiver) = channel(0usize);
spawn(async move {
sender.send(1).unwrap();
sender.send(2).unwrap();
sender.send(3).unwrap();
receiver.changed().await.unwrap();
*observed_for_task.lock().unwrap() = Some(*receiver.borrow_and_update());
});
});
run();
assert_eq!(*observed.lock().unwrap(), Some(3));
}
#[test]
fn changed_errors_after_all_senders_drop() {
let observed = Arc::new(Mutex::new(None::<RecvError>));
let observed_for_task = Arc::clone(&observed);
queue_macrotask(move || {
let (sender, mut receiver) = channel(0usize);
spawn(async move {
drop(sender);
*observed_for_task.lock().unwrap() = Some(receiver.changed().await.unwrap_err());
});
});
run();
assert_eq!(*observed.lock().unwrap(), Some(RecvError));
}
#[test]
fn send_modify_updates_value() {
let observed = Arc::new(Mutex::new(None::<usize>));
let observed_for_task = Arc::clone(&observed);
queue_macrotask(move || {
let (sender, mut receiver) = channel(1usize);
spawn(async move {
sender.send_modify(|value| *value += 41);
receiver.changed().await.unwrap();
*observed_for_task.lock().unwrap() = Some(*receiver.borrow());
});
});
run();
assert_eq!(*observed.lock().unwrap(), Some(42));
}
}