use event_listener::Event;
use crate::sync::loom_atomic::{Arc, AtomicU64, AtomicUsize, Ordering, RwLock, RwLockReadGuard};
#[derive(Debug, thiserror::Error)]
#[error("watch channel has no receivers")]
pub struct SendError<T>(pub T);
#[derive(Debug, thiserror::Error)]
#[error("watch channel sender dropped")]
pub struct RecvError;
struct Inner<T> {
value: RwLock<T>,
version: AtomicU64,
event: Event,
senders: AtomicUsize,
receivers: AtomicUsize,
}
pub struct Sender<T> {
inner: Arc<Inner<T>>,
}
impl<T> Sender<T> {
pub fn send(&self, value: T) -> Result<(), SendError<T>> {
if self.inner.receivers.load(Ordering::Acquire) == 0 {
return Err(SendError(value));
}
{
let mut guard = self
.inner
.value
.write()
.unwrap_or_else(|err| err.into_inner());
*guard = value;
}
self.inner.version.fetch_add(1, Ordering::AcqRel);
self.inner.event.notify(usize::MAX);
Ok(())
}
pub fn is_closed(&self) -> bool {
self.inner.receivers.load(Ordering::Acquire) == 0
}
pub fn subscribe(&self) -> Receiver<T> {
self.inner.receivers.fetch_add(1, Ordering::AcqRel);
Receiver {
inner: self.inner.clone(),
last_seen: self.inner.version.load(Ordering::Acquire),
}
}
pub fn borrow(&self) -> Ref<'_, T> {
Ref {
guard: self
.inner
.value
.read()
.unwrap_or_else(|err| err.into_inner()),
}
}
}
impl<T> Drop for Sender<T> {
fn drop(&mut self) {
if self.inner.senders.fetch_sub(1, Ordering::AcqRel) == 1 {
self.inner.event.notify(usize::MAX);
}
}
}
pub struct Receiver<T> {
inner: Arc<Inner<T>>,
last_seen: u64,
}
impl<T> Receiver<T> {
pub async fn changed(&mut self) -> Result<(), RecvError> {
loop {
let current = self.inner.version.load(Ordering::Acquire);
if current != self.last_seen {
return Ok(());
}
if self.inner.senders.load(Ordering::Acquire) == 0 {
return Err(RecvError);
}
let listener = self.inner.event.listen();
let current = self.inner.version.load(Ordering::Acquire);
if current != self.last_seen {
return Ok(());
}
if self.inner.senders.load(Ordering::Acquire) == 0 {
return Err(RecvError);
}
listener.await;
}
}
pub fn has_changed(&self) -> Result<bool, RecvError> {
let current = self.inner.version.load(Ordering::Acquire);
if current != self.last_seen {
return Ok(true);
}
if self.inner.senders.load(Ordering::Acquire) == 0 {
return Err(RecvError);
}
Ok(false)
}
pub fn borrow(&self) -> Ref<'_, T> {
Ref {
guard: self
.inner
.value
.read()
.unwrap_or_else(|err| err.into_inner()),
}
}
pub fn borrow_and_update(&mut self) -> Ref<'_, T> {
self.last_seen = self.inner.version.load(Ordering::Acquire);
self.borrow()
}
}
impl<T> Clone for Receiver<T> {
fn clone(&self) -> Self {
self.inner.receivers.fetch_add(1, Ordering::AcqRel);
Self {
inner: self.inner.clone(),
last_seen: self.last_seen,
}
}
}
impl<T> Drop for Receiver<T> {
fn drop(&mut self) {
self.inner.receivers.fetch_sub(1, Ordering::AcqRel);
}
}
pub struct Ref<'lifetime, T> {
guard: RwLockReadGuard<'lifetime, T>,
}
impl<T> std::ops::Deref for Ref<'_, T> {
type Target = T;
fn deref(&self) -> &T {
&self.guard
}
}
pub fn channel<T>(initial: T) -> (Sender<T>, Receiver<T>) {
let inner = Arc::new(Inner {
value: RwLock::new(initial),
version: AtomicU64::new(0),
event: Event::new(),
senders: AtomicUsize::new(1),
receivers: AtomicUsize::new(1),
});
(
Sender {
inner: inner.clone(),
},
Receiver {
inner,
last_seen: 0,
},
)
}
#[cfg(all(test, not(loom)))]
#[allow(clippy::unwrap_used, clippy::expect_used)]
mod tests {
use super::*;
use futures::executor::block_on;
#[test]
fn initial_value_is_visible_via_borrow() {
let (_sender, receiver) = channel(7_u32);
assert_eq!(*receiver.borrow(), 7);
}
#[test]
fn send_makes_changed_resolve_and_updates_value() {
let (sender, mut receiver) = channel(0_u32);
sender.send(42).expect("send");
block_on(async {
receiver.changed().await.expect("changed");
assert_eq!(*receiver.borrow_and_update(), 42);
});
}
#[test]
fn back_to_back_sends_collapse_to_latest() {
let (sender, mut receiver) = channel(0_u32);
sender.send(1).expect("send 1");
sender.send(2).expect("send 2");
sender.send(3).expect("send 3");
block_on(async {
receiver.changed().await.expect("changed");
assert_eq!(*receiver.borrow_and_update(), 3);
assert!(!receiver.has_changed().expect("has_changed"));
});
}
#[test]
fn dropping_sender_makes_changed_error() {
let (sender, mut receiver) = channel(0_u32);
drop(sender);
block_on(async {
let result = receiver.changed().await;
assert!(result.is_err());
});
}
#[test]
fn dropping_all_receivers_makes_send_error() {
let (sender, receiver) = channel(0_u32);
drop(receiver);
match sender.send(1) {
Err(SendError(value)) => assert_eq!(value, 1),
Ok(()) => panic!("send to receiver-less channel should error"),
}
}
#[test]
fn subscribe_starts_at_current_version_and_resolves_on_next_send() {
let (sender, _receiver) = channel(0_u32);
sender.send(1).expect("send 1");
let mut newcomer = sender.subscribe();
assert!(!newcomer.has_changed().expect("has_changed"));
sender.send(2).expect("send 2");
block_on(async {
newcomer.changed().await.expect("changed");
assert_eq!(*newcomer.borrow_and_update(), 2);
});
}
}
#[cfg(all(test, loom))]
#[allow(clippy::unwrap_used, clippy::expect_used)]
mod loom_tests {
use super::channel;
use loom::sync::Arc;
fn check(model: impl Fn() + Sync + Send + 'static) {
let mut builder = loom::model::Builder::new();
builder.preemption_bound = Some(3);
builder.check(model);
}
#[test]
fn send_then_changed_observes_new_value() {
check(|| {
let (sender, mut receiver) = channel(0_u32);
sender.send(42).expect("send");
loom::future::block_on(async {
receiver.changed().await.expect("changed");
});
assert_eq!(*receiver.borrow_and_update(), 42);
});
}
#[test]
fn changed_then_send_resolves() {
check(|| {
let (sender, mut receiver) = channel(0_u32);
let sender = Arc::new(sender);
let sender_producer = sender.clone();
let producer = loom::thread::spawn(move || {
sender_producer.send(1).expect("send");
});
loom::future::block_on(async {
receiver.changed().await.expect("changed");
});
producer.join().expect("producer thread panicked");
assert_eq!(*receiver.borrow_and_update(), 1);
});
}
#[test]
fn concurrent_send_and_changed() {
check(|| {
let (sender, mut receiver) = channel(0_u32);
let sender = Arc::new(sender);
let sender_producer = sender.clone();
let producer = loom::thread::spawn(move || {
sender_producer.send(99).expect("send");
});
let consumer = loom::thread::spawn(move || {
loom::future::block_on(async {
receiver.changed().await.expect("changed");
});
*receiver.borrow_and_update()
});
producer.join().expect("producer thread panicked");
let observed = consumer.join().expect("consumer thread panicked");
assert_eq!(observed, 99);
});
}
}