#![allow(unsafe_code)]
use core::fmt;
use core::ops::Deref;
use kovan::{Atom, AtomGuard, CachePadded};
use super::Notify;
use super::internal::{Arc, AtomicU64, AtomicUsize, Ordering};
use self::error::{RecvError, SendError};
const CLOSED: u64 = 1;
const STEP: u64 = 2;
fn version_of(state: u64) -> u64 {
state & !CLOSED
}
pub mod error {
use core::fmt;
use std::error::Error;
#[derive(PartialEq, Eq, Clone, Copy)]
pub struct SendError<T>(pub T);
impl<T> fmt::Debug for SendError<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("SendError").finish_non_exhaustive()
}
}
impl<T> fmt::Display for SendError<T> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(fmt, "channel closed")
}
}
impl<T> Error for SendError<T> {}
#[derive(Debug, Clone)]
pub struct RecvError(pub(super) ());
impl fmt::Display for RecvError {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(fmt, "channel closed")
}
}
impl Error for RecvError {}
}
struct Shared<T: Send + Sync + 'static> {
value: Atom<T>,
state: CachePadded<AtomicU64>,
ref_count_rx: AtomicUsize,
ref_count_tx: AtomicUsize,
notify_rx: Notify,
notify_tx: Notify,
}
impl<T: Send + Sync + 'static> Shared<T> {
fn published(&self) {
self.state.fetch_add(STEP, Ordering::Release);
self.notify_rx.notify_waiters();
}
fn snapshot(&self) -> (AtomGuard<'_, T>, u64) {
let version = version_of(self.state.load(Ordering::Acquire));
(self.value.load(), version)
}
}
impl<T: Send + Sync + 'static + fmt::Debug> fmt::Debug for Shared<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let state = self.state.load(Ordering::Acquire);
f.debug_struct("Shared")
.field("value", &*self.value.load())
.field("version", &version_of(state))
.field("is_closed", &(state & CLOSED != 0))
.field("ref_count_rx", &self.ref_count_rx)
.finish()
}
}
pub fn channel<T: Send + Sync + 'static>(init: T) -> (Sender<T>, Receiver<T>) {
let shared = Arc::new(Shared {
value: Atom::new(init),
state: CachePadded::new(AtomicU64::new(0)),
ref_count_rx: AtomicUsize::new(1),
ref_count_tx: AtomicUsize::new(1),
notify_rx: Notify::new(),
notify_tx: Notify::new(),
});
let tx = Sender {
shared: Arc::clone(&shared),
};
let rx = Receiver { shared, version: 0 };
(tx, rx)
}
pub struct Sender<T: Send + Sync + 'static> {
shared: Arc<Shared<T>>,
}
pub struct Receiver<T: Send + Sync + 'static> {
shared: Arc<Shared<T>>,
version: u64,
}
pub struct Ref<'a, T: Send + Sync + 'static> {
guard: AtomGuard<'a, T>,
has_changed: bool,
}
unsafe impl<T: Send + Sync + 'static> Sync for Ref<'_, T> {}
impl<T: Send + Sync + 'static> Ref<'_, T> {
pub fn has_changed(&self) -> bool {
self.has_changed
}
}
impl<T: Send + Sync + 'static> Deref for Ref<'_, T> {
type Target = T;
fn deref(&self) -> &T {
&self.guard
}
}
impl<T: Send + Sync + 'static + fmt::Debug> fmt::Debug for Ref<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Ref")
.field("value", &**self)
.field("has_changed", &self.has_changed)
.finish()
}
}
impl<T: Send + Sync + 'static> Receiver<T> {
fn from_shared(version: u64, shared: Arc<Shared<T>>) -> Self {
shared.ref_count_rx.fetch_add(1, Ordering::Relaxed);
Self { shared, version }
}
pub fn borrow(&self) -> Ref<'_, T> {
let (guard, version) = self.shared.snapshot();
Ref {
guard,
has_changed: self.version != version,
}
}
pub fn borrow_and_update(&mut self) -> Ref<'_, T> {
let (guard, version) = self.shared.snapshot();
let has_changed = self.version != version;
self.version = version;
Ref { guard, has_changed }
}
pub fn has_changed(&self) -> Result<bool, RecvError> {
let state = self.shared.state.load(Ordering::Acquire);
if state & CLOSED != 0 {
return Err(RecvError(()));
}
Ok(self.version != version_of(state))
}
pub fn is_closed(&self) -> bool {
self.shared.state.load(Ordering::Acquire) & CLOSED != 0
}
pub fn mark_changed(&mut self) {
self.version = self.version.wrapping_sub(STEP);
}
pub fn mark_unchanged(&mut self) {
self.version = version_of(self.shared.state.load(Ordering::Acquire));
}
pub async fn changed(&mut self) -> Result<(), RecvError> {
changed_impl(&self.shared, &mut self.version).await
}
pub async fn wait_for(
&mut self,
mut f: impl FnMut(&T) -> bool,
) -> Result<Ref<'_, T>, RecvError> {
let mut closed = false;
loop {
{
let (guard, new_version) = self.shared.snapshot();
let has_changed = self.version != new_version;
self.version = new_version;
if (!closed || has_changed) && f(&guard) {
return Ok(Ref { guard, has_changed });
}
}
if closed {
return Err(RecvError(()));
}
closed = changed_impl(&self.shared, &mut self.version).await.is_err();
}
}
pub fn same_channel(&self, other: &Self) -> bool {
Arc::ptr_eq(&self.shared, &other.shared)
}
}
fn maybe_changed<T: Send + Sync + 'static>(
shared: &Shared<T>,
version: &mut u64,
) -> Option<Result<(), RecvError>> {
let state = shared.state.load(Ordering::Acquire);
let new_version = version_of(state);
if *version != new_version {
*version = new_version;
return Some(Ok(()));
}
if state & CLOSED != 0 {
return Some(Err(RecvError(())));
}
None
}
async fn changed_impl<T: Send + Sync + 'static>(
shared: &Shared<T>,
version: &mut u64,
) -> Result<(), RecvError> {
loop {
let notified = shared.notify_rx.notified();
if let Some(ret) = maybe_changed(shared, version) {
return ret;
}
notified.await;
}
}
impl<T: Send + Sync + 'static> Clone for Receiver<T> {
fn clone(&self) -> Self {
Self::from_shared(self.version, Arc::clone(&self.shared))
}
}
impl<T: Send + Sync + 'static> Drop for Receiver<T> {
fn drop(&mut self) {
if self.shared.ref_count_rx.fetch_sub(1, Ordering::Relaxed) == 1 {
self.shared.notify_tx.notify_waiters();
}
}
}
impl<T: Send + Sync + 'static + fmt::Debug> fmt::Debug for Receiver<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Receiver")
.field("shared", &*self.shared)
.field("version", &self.version)
.finish()
}
}
impl<T: Send + Sync + 'static> Sender<T> {
pub fn new(init: T) -> Self {
channel(init).0
}
pub fn send(&self, value: T) -> Result<(), SendError<T>> {
if self.receiver_count() == 0 {
return Err(SendError(value));
}
self.shared.value.store(value);
self.shared.published();
Ok(())
}
pub fn send_modify<F>(&self, mut modify: F)
where
F: FnMut(&mut T),
T: Clone,
{
self.send_if_modified(|value| {
modify(value);
true
});
}
pub fn send_if_modified<F>(&self, mut modify: F) -> bool
where
F: FnMut(&mut T) -> bool,
T: Clone,
{
loop {
let current = self.shared.value.load();
let mut next = T::clone(¤t);
if !modify(&mut next) {
return false;
}
if self.shared.value.compare_and_swap(¤t, next).is_ok() {
break;
}
}
self.shared.published();
true
}
pub fn send_replace(&self, value: T) -> T
where
T: Clone,
{
let old = self.shared.value.swap(value);
self.shared.published();
T::clone(&old)
}
pub fn borrow(&self) -> Ref<'_, T> {
Ref {
guard: self.shared.value.load(),
has_changed: false,
}
}
pub fn is_closed(&self) -> bool {
self.receiver_count() == 0
}
pub async fn closed(&self) {
while self.receiver_count() > 0 {
let notified = self.shared.notify_tx.notified();
if self.receiver_count() == 0 {
return;
}
notified.await;
}
}
pub fn subscribe(&self) -> Receiver<T> {
let version = version_of(self.shared.state.load(Ordering::Acquire));
Receiver::from_shared(version, Arc::clone(&self.shared))
}
pub fn receiver_count(&self) -> usize {
self.shared.ref_count_rx.load(Ordering::Relaxed)
}
pub fn sender_count(&self) -> usize {
self.shared.ref_count_tx.load(Ordering::Relaxed)
}
pub fn same_channel(&self, other: &Self) -> bool {
Arc::ptr_eq(&self.shared, &other.shared)
}
}
impl<T: Send + Sync + 'static> Clone for Sender<T> {
fn clone(&self) -> Self {
self.shared.ref_count_tx.fetch_add(1, Ordering::Relaxed);
Self {
shared: Arc::clone(&self.shared),
}
}
}
impl<T: Send + Sync + 'static + Default> Default for Sender<T> {
fn default() -> Self {
Self::new(T::default())
}
}
impl<T: Send + Sync + 'static> Drop for Sender<T> {
fn drop(&mut self) {
if self.shared.ref_count_tx.fetch_sub(1, Ordering::AcqRel) == 1 {
self.shared.state.fetch_or(CLOSED, Ordering::Release);
self.shared.notify_rx.notify_waiters();
}
}
}
impl<T: Send + Sync + 'static + fmt::Debug> fmt::Debug for Sender<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Sender")
.field("shared", &*self.shared)
.finish()
}
}
#[cfg(all(test, not(loom)))]
mod tests {
use super::*;
#[test]
fn the_closed_bit_leaves_the_version_alone() {
let (tx, rx) = channel(0u8);
tx.send(1).unwrap();
let before = version_of(tx.shared.state.load(Ordering::Acquire));
drop(tx);
assert!(rx.is_closed());
assert_eq!(version_of(rx.shared.state.load(Ordering::Acquire)), before);
}
#[test]
fn the_version_wraps_without_touching_the_closed_bit() {
let (tx, rx) = channel(0u8);
tx.shared.state.store(u64::MAX - 1, Ordering::SeqCst);
tx.send(1).unwrap();
let state = tx.shared.state.load(Ordering::SeqCst);
assert_eq!(state & CLOSED, 0, "a wrapped version is not a close");
assert_eq!(version_of(state), 0);
assert!(!rx.is_closed());
}
}