use std::future::Future;
use std::pin::Pin;
use std::sync::atomic::{AtomicUsize as StdAtomicUsize, Ordering as StdOrdering};
use std::task::{Context, Poll, Waker};
use loom::sync::{Arc, Mutex};
use loom::thread;
use super::super::{Runnable, Schedule, ScheduleInfo, spawn};
use super::{model, pop};
const NOWHERE: usize = 0;
const HANDLE: usize = 1;
const RUNNER: usize = 2;
struct Where {
queue: Arc<Mutex<Vec<Runnable>>>,
handle: thread::ThreadId,
freed: std::sync::Arc<StdAtomicUsize>,
}
impl Drop for Where {
fn drop(&mut self) {
let on = if thread::current().id() == self.handle {
HANDLE
} else {
RUNNER
};
self.freed.store(on, StdOrdering::SeqCst);
}
}
unsafe impl Schedule for Where {
fn schedule(&self, runnable: Runnable, _info: ScheduleInfo) {
self.queue.lock().unwrap().push(runnable);
}
}
struct Seven;
impl Future for Seven {
type Output = u8;
fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<u8> {
Poll::Ready(7)
}
}
fn completion_racing_a_polled_handle() {
let queue = Arc::new(Mutex::new(Vec::new()));
let freed = std::sync::Arc::new(StdAtomicUsize::new(NOWHERE));
let sched = Where {
queue: queue.clone(),
handle: thread::current().id(),
freed: freed.clone(),
};
let (runnable, mut task) = spawn(Seven, sched);
runnable.schedule();
let runner = {
let queue = queue.clone();
thread::spawn(move || {
while let Some(runnable) = pop(&queue) {
runnable.run();
}
})
};
let mut cx = Context::from_waker(Waker::noop());
let output = loop {
match Pin::new(&mut task).poll(&mut cx) {
Poll::Ready(output) => break output,
Poll::Pending => thread::yield_now(),
}
};
drop(task);
runner.join().unwrap();
assert_eq!(output, 7);
assert_eq!(
freed.load(StdOrdering::SeqCst),
HANDLE,
"the run freed a task whose handle held it as it completed"
);
}
#[test]
fn a_handle_that_sees_the_completion_frees_the_task_itself() {
model(
"a_handle_that_sees_the_completion_frees_the_task_itself",
10,
completion_racing_a_polled_handle,
);
}