#![allow(unsafe_code)]
use core::cell::{Cell, UnsafeCell};
use core::future::Future;
use core::pin::Pin;
use core::ptr;
use core::task::{Context, Poll, Waker};
use super::internal::Withdraw;
thread_local! {
static HEAD: Cell<*const Header> = const { Cell::new(ptr::null()) };
}
#[repr(C)]
struct Header {
withdraw: unsafe fn(*const Header),
frozen: Cell<bool>,
polling: Cell<bool>,
linked: Cell<bool>,
waker: Cell<Option<Waker>>,
prev: Cell<*const Header>,
}
#[repr(C)]
struct Record<F> {
header: Header,
future: UnsafeCell<F>,
}
unsafe fn withdraw_of<F: Withdraw>(header: *const Header) {
let record = unsafe { &*header.cast::<Record<F>>() };
unsafe { Pin::new_unchecked(&mut *record.future.get()) }.withdraw();
}
pub(super) struct Frame<'a, F> {
record: &'a Record<F>,
}
impl<F: Future + Withdraw> Future for Frame<'_, F> {
type Output = F::Output;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<F::Output> {
let header = &self.record.header;
header.frozen.set(false);
struct Polling<'h>(&'h Header);
impl Drop for Polling<'_> {
fn drop(&mut self) {
self.0.polling.set(false);
}
}
header.polling.set(true);
let polling = Polling(header);
let polled = unsafe { Pin::new_unchecked(&mut *self.record.future.get()) }.poll(cx);
drop(polling);
if polled.is_pending() {
keep(header, cx.waker());
link(header);
}
polled
}
}
fn keep(header: &Header, waker: &Waker) {
match header.waker.take() {
Some(held) if held.will_wake(waker) => header.waker.set(Some(held)),
_ => header.waker.set(Some(waker.clone())),
}
}
fn link(header: &Header) {
if !header.linked.get() {
header.prev.set(HEAD.get());
HEAD.set(header);
header.linked.set(true);
}
}
struct Unlink<'a>(&'a Header);
impl Drop for Unlink<'_> {
fn drop(&mut self) {
if self.0.linked.get() {
debug_assert!(ptr::eq(HEAD.get(), self.0), "the frames end in order");
HEAD.set(self.0.prev.get());
self.0.linked.set(false);
}
}
}
pub(super) fn in_line<F, R>(future: F, run: impl FnOnce(Frame<'_, F>) -> R) -> R
where
F: Future + Withdraw,
{
let record = Record {
header: Header {
withdraw: withdraw_of::<F>,
frozen: Cell::new(false),
polling: Cell::new(false),
linked: Cell::new(false),
waker: Cell::new(None),
prev: Cell::new(ptr::null()),
},
future: UnsafeCell::new(future),
};
let _unlink = Unlink(&record.header);
run(Frame { record: &record })
}
#[inline(always)]
pub(super) fn any() -> bool {
!HEAD.get().is_null()
}
#[cold]
#[inline(never)]
pub(super) fn freeze() {
let mut node = HEAD.get();
while !node.is_null() {
let header = unsafe { &*node };
if !header.frozen.get() && !header.polling.get() {
header.frozen.set(true);
unsafe { (header.withdraw)(node) };
if let Some(waker) = header.waker.take() {
waker.wake_by_ref();
header.waker.set(Some(waker));
}
}
node = header.prev.get();
}
}
#[cfg(test)]
pub(crate) fn depth() -> usize {
let mut count = 0;
let mut node = HEAD.get();
while !node.is_null() {
count += 1;
node = unsafe { &*node }.prev.get();
}
count
}
#[cfg(all(test, not(loom)))]
mod tests {
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::time::Duration;
use super::*;
use crate::sync::{Mutex, RwLock, Semaphore, mpsc};
fn within<R: Send + 'static>(body: impl FnOnce() -> R + Send + 'static) -> R {
let (done, result) = std::sync::mpsc::channel();
std::thread::spawn(move || {
let _ = done.send(body());
});
result
.recv_timeout(Duration::from_secs(30))
.expect("a nested blocking wait never ended")
}
async fn until(started: &AtomicUsize, count: usize) {
while started.load(Ordering::SeqCst) < count {
crate::task::yield_now().await;
}
}
fn runtime() -> crate::Runtime {
crate::Runtime::builder()
.worker_threads(0)
.build()
.expect("a runtime")
}
#[test]
fn nested_mutex_waits_never_wait_on_each_other_with_the_lock_free() {
within(|| {
runtime().block_on(async {
let mutex = Arc::new(Mutex::new(0u32));
let held = Arc::clone(&mutex).try_lock_owned().expect("free");
let started = Arc::new(AtomicUsize::new(0));
let wait = |m: Arc<Mutex<u32>>| {
let started = Arc::clone(&started);
crate::spawn(async move {
started.fetch_add(1, Ordering::SeqCst);
*m.blocking_lock() += 1;
})
};
let outer = wait(Arc::clone(&mutex));
let inner = wait(Arc::clone(&mutex));
let gate = Arc::clone(&started);
let release = crate::spawn(async move {
until(&gate, 2).await;
drop(held);
});
release.await;
outer.await;
inner.await;
assert_eq!(*mutex.lock().await, 2);
});
});
}
#[test]
fn a_frozen_wait_that_was_granted_passes_the_grant_to_the_frame_on_top() {
within(|| {
runtime().block_on(async {
let (tx, mut rx) = mpsc::channel::<u32>(1);
tx.try_send(0).expect("room");
let started = Arc::new(AtomicUsize::new(0));
let outer = {
let (tx, started) = (tx.clone(), Arc::clone(&started));
crate::spawn(async move {
started.fetch_add(1, Ordering::SeqCst);
tx.blocking_send(10).expect("open");
})
};
let inner = crate::spawn(async move {
until(&started, 1).await;
assert_eq!(rx.try_recv(), Ok(0));
tx.blocking_send(20).expect("open");
let first = rx.recv().await.expect("a value");
let second = rx.recv().await.expect("a value");
(first, second)
});
assert_eq!(inner.await, (20, 10));
outer.await;
});
});
}
#[test]
fn nested_semaphore_waits_never_wait_on_each_other() {
within(|| {
runtime().block_on(async {
let sem = Arc::new(Semaphore::new(1));
let held = Arc::clone(&sem).try_acquire_owned().expect("free");
let started = Arc::new(AtomicUsize::new(0));
let wait = |s: Arc<Semaphore>| {
let started = Arc::clone(&started);
crate::spawn(async move {
started.fetch_add(1, Ordering::SeqCst);
drop(s.blocking_acquire().expect("open"));
})
};
let outer = wait(Arc::clone(&sem));
let inner = wait(Arc::clone(&sem));
let gate = Arc::clone(&started);
let release = crate::spawn(async move {
until(&gate, 2).await;
drop(held);
});
release.await;
outer.await;
inner.await;
assert_eq!(sem.available_permits(), 1);
});
});
}
#[test]
fn nested_write_waits_never_wait_on_each_other() {
within(|| {
runtime().block_on(async {
let lock = Arc::new(RwLock::new(0u32));
let held = Arc::clone(&lock).write_owned().await;
let started = Arc::new(AtomicUsize::new(0));
let wait = |l: Arc<RwLock<u32>>| {
let started = Arc::clone(&started);
crate::spawn(async move {
started.fetch_add(1, Ordering::SeqCst);
*l.blocking_write() += 1;
})
};
let outer = wait(Arc::clone(&lock));
let inner = wait(Arc::clone(&lock));
let gate = Arc::clone(&started);
let release = crate::spawn(async move {
until(&gate, 2).await;
drop(held);
});
release.await;
outer.await;
inner.await;
assert_eq!(*lock.read().await, 2);
});
});
}
#[test]
fn nested_blocking_sends_never_wait_on_each_other() {
within(|| {
runtime().block_on(async {
let (tx, mut rx) = mpsc::channel::<u32>(1);
tx.try_send(0).expect("room");
let started = Arc::new(AtomicUsize::new(0));
let send = |tx: mpsc::Sender<u32>, value| {
let started = Arc::clone(&started);
crate::spawn(async move {
started.fetch_add(1, Ordering::SeqCst);
tx.blocking_send(value).expect("open");
})
};
let outer = send(tx.clone(), 10);
let inner = send(tx, 20);
let gate = Arc::clone(&started);
let receiver = crate::spawn(async move {
until(&gate, 2).await;
let mut got = Vec::new();
for _ in 0..3 {
got.push(rx.recv().await.expect("a value"));
}
got
});
outer.await;
inner.await;
let mut got = receiver.await;
got.sort_unstable();
assert_eq!(got, [0, 10, 20]);
});
});
}
struct Panics(bool);
impl Future for Panics {
type Output = ();
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
if self.0 {
panic!("the frame's future panicked");
}
self.0 = true;
cx.waker().wake_by_ref();
Poll::Pending
}
}
impl Withdraw for Panics {
fn withdraw(self: Pin<&mut Self>) {}
}
#[test]
fn the_stack_is_popped_after_a_panic_in_the_frames_future() {
within(|| {
runtime().block_on(async {
crate::spawn(async {
let caught = std::panic::catch_unwind(|| {
crate::sync::internal::blocking_in_line(Panics(false));
});
assert!(caught.is_err());
assert_eq!(depth(), 0, "the record stayed on the stack");
})
.await;
});
});
}
}