#![no_std]
#![doc(
html_logo_url = "https://ardaku.github.io/mm/logo.svg",
html_favicon_url = "https://ardaku.github.io/mm/icon.svg",
html_root_url = "https://docs.rs/whisk"
)]
#![warn(
anonymous_parameters,
missing_copy_implementations,
missing_debug_implementations,
missing_docs,
nonstandard_style,
rust_2018_idioms,
single_use_lifetimes,
trivial_casts,
trivial_numeric_casts,
unreachable_pub,
unused_extern_crates,
unused_qualifications,
variant_size_differences
)]
#![deny(unsafe_code)]
extern crate alloc;
use alloc::{
sync::{self, Arc},
vec::Vec,
};
use core::{
cell::{Cell, UnsafeCell},
future::Future,
pin::Pin,
sync::atomic::{
self, AtomicBool,
Ordering::{Acquire, Relaxed, Release},
},
task::{
Context,
Poll::{self, Pending, Ready},
Waker,
},
};
#[allow(unsafe_code)]
mod spin {
use super::*;
#[derive(Default)]
pub(super) struct Spin<T: Default> {
flag: AtomicBool,
data: UnsafeCell<T>,
}
impl<T: Default> Spin<T> {
#[inline(always)]
pub(super) fn with<O>(&self, then: impl FnOnce(&mut T) -> O) -> O {
while self
.flag
.compare_exchange_weak(false, true, Relaxed, Relaxed)
.is_err()
{
core::hint::spin_loop();
}
atomic::fence(Acquire);
let output = then(unsafe { &mut *self.data.get() });
self.flag.store(false, Release);
output
}
}
unsafe impl<T: Default + Send> Send for Spin<T> {}
unsafe impl<T: Default + Send> Sync for Spin<T> {}
}
#[derive(Default)]
#[repr(C)]
struct Wake {
wake: Option<Waker>,
chan: usize,
list: Vec<(usize, Waker)>,
}
impl Wake {
#[inline(always)]
fn register(&mut self, chan: usize, waker: Waker) {
if let Some(wake) = self.wake.take() {
if self.chan == chan {
(self.chan, self.wake) = (chan, Some(waker));
} else {
self.list.extend([(self.chan, wake), (chan, waker)]);
}
} else if self.list.is_empty() {
(self.chan, self.wake) = (chan, Some(waker));
} else if let Some(wake) = self.list.iter_mut().find(|w| w.0 == chan) {
wake.1 = waker;
} else {
self.list.push((chan, waker));
}
}
#[inline(always)]
fn wake(&mut self) {
if let Some(waker) = self.wake.take() {
waker.wake();
return;
}
for waker in self.list.drain(..) {
waker.1.wake();
}
}
}
struct Locked<T: Send> {
recv: Wake,
send: Wake,
data: Option<T>,
}
impl<T: Send> Default for Locked<T> {
#[inline]
fn default() -> Self {
let data = None;
let send = Wake::default();
let recv = Wake::default();
Self { data, send, recv }
}
}
#[derive(Default)]
struct Shared<T: Send> {
spin: spin::Spin<Locked<T>>,
}
pub struct Channel<T: Send>(Arc<Shared<T>>);
impl<T: Send> core::fmt::Debug for Channel<T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Channel")
.field("strong_count", &Arc::strong_count(&self.0))
.finish()
}
}
impl<T: Send> Clone for Channel<T> {
#[inline]
fn clone(&self) -> Self {
Self(Arc::clone(&self.0))
}
}
impl<T: Send> Default for Channel<T> {
#[inline]
fn default() -> Self {
Self::new()
}
}
impl<T: Send> Channel<T> {
#[inline]
pub fn new() -> Self {
let spin = spin::Spin::default();
Self(Arc::new(Shared { spin }))
}
#[inline]
pub fn downgrade(&self) -> Weak<T> {
Weak(Arc::downgrade(&self.0))
}
#[inline(always)]
pub async fn send(&self, message: T) {
Message((*self).clone(), Cell::new(Some(message))).await
}
#[inline(always)]
pub async fn recv(&self) -> T {
self.await
}
#[inline(always)]
fn poll_internal(&self, cx: &mut Context<'_>) -> Poll<T> {
let waker = cx.waker();
let uid = Arc::as_ptr(&self.0) as usize;
self.0.spin.with(|shared| {
if let Some(output) = shared.data.take() {
shared.send.wake();
Ready(output)
} else {
shared.recv.register(uid, waker.clone());
Pending
}
})
}
}
impl<T: Send> Future for Channel<T> {
type Output = T;
#[inline(always)]
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
self.poll_internal(cx)
}
}
impl<T: Send> Future for &Channel<T> {
type Output = T;
#[inline(always)]
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
self.poll_internal(cx)
}
}
#[cfg(feature = "pasts")]
impl<T: Send> pasts::Notifier for Channel<T> {
type Event = T;
#[inline(always)]
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<T> {
self.poll_internal(cx)
}
}
#[cfg(feature = "pasts")]
impl<T: Send> pasts::Notifier for &Channel<T> {
type Event = T;
#[inline(always)]
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<T> {
self.poll_internal(cx)
}
}
#[cfg(feature = "futures-core")]
impl<T: Send> futures_core::Stream for Channel<Option<T>> {
type Item = T;
#[inline(always)]
fn poll_next(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Option<Self::Item>> {
self.poll_internal(cx)
}
}
#[cfg(feature = "futures-core")]
impl<T: Send> futures_core::Stream for &Channel<Option<T>> {
type Item = T;
#[inline(always)]
fn poll_next(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Option<Self::Item>> {
self.poll_internal(cx)
}
}
pub struct Weak<T: Send>(sync::Weak<Shared<T>>);
impl<T: Send> core::fmt::Debug for Weak<T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Weak")
.field("strong_count", &sync::Weak::strong_count(&self.0))
.finish()
}
}
impl<T: Send> Default for Weak<T> {
#[inline]
fn default() -> Self {
Self::new()
}
}
impl<T: Send> Weak<T> {
#[inline]
pub fn new() -> Self {
Self(sync::Weak::new())
}
#[inline]
pub fn upgrade(&self) -> Option<Channel<T>> {
Some(Channel(self.0.upgrade()?))
}
#[inline(always)]
pub async fn try_send(&self, message: T) -> Result<(), ()> {
if let Some(channel) = self.0.upgrade() {
Message(Channel(channel), Cell::new(Some(message))).await;
Ok(())
} else {
Err(())
}
}
#[inline(always)]
pub async fn try_recv(&self) -> Result<T, ()> {
if let Some(channel) = self.0.upgrade() {
Ok(Channel(channel).await)
} else {
Err(())
}
}
}
struct Message<T: Send>(Channel<T>, Cell<Option<T>>);
#[allow(unsafe_code)]
impl<T: Send> Message<T> {
#[inline(always)]
fn pin_get(self: Pin<&Self>) -> Pin<&Cell<Option<T>>> {
unsafe { self.map_unchecked(|s| &s.1) }
}
}
impl<T: Send> Future for Message<T> {
type Output = ();
#[inline]
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = Pin::new(&self).get_ref();
let waker = cx.waker();
let uid = Arc::as_ptr(&this.0 .0) as usize;
this.0 .0.spin.with(|shared| {
if shared.data.is_none() {
shared.data = this.as_ref().pin_get().take();
shared.recv.wake();
Ready(())
} else {
shared.send.register(uid, waker.clone());
Pending
}
})
}
}