use std::{
future::Future,
mem::forget,
pin::Pin,
sync::{Arc, Condvar, Mutex},
task::{Context, Poll, RawWaker, RawWakerVTable, Waker},
};
enum SignalState {
Empty,
Waiting,
Notified,
}
struct Signal {
state: Mutex<SignalState>,
cond: Condvar,
}
impl Signal {
fn new() -> Self {
Self {
state: Mutex::new(SignalState::Empty),
cond: Condvar::new(),
}
}
fn wait(&self) {
let mut state = self.state.lock().unwrap();
match *state {
SignalState::Notified => {
*state = SignalState::Empty;
return;
}
SignalState::Waiting => {
unreachable!("Multiple threads waiting on the same signal: Open a bug report!");
}
SignalState::Empty => {
*state = SignalState::Waiting;
while let SignalState::Waiting = *state {
state = self.cond.wait(state).unwrap();
}
}
}
}
fn notify(&self) {
let mut state = self.state.lock().unwrap();
match *state {
SignalState::Notified => {}
SignalState::Empty => *state = SignalState::Notified,
SignalState::Waiting => {
*state = SignalState::Empty;
self.cond.notify_one();
}
}
}
}
type SignalArc = Arc<Signal>;
static VTABLE: RawWakerVTable = unsafe {
RawWakerVTable::new(
|signal| {
let arc = SignalArc::from_raw(signal as *const _);
let waker = RawWaker::new(Arc::into_raw(arc.clone()) as *const _, &VTABLE);
forget(arc);
waker
},
|signal| {
let arc = SignalArc::from_raw(signal as *const _);
arc.notify();
drop(arc);
},
|signal| (&*(signal as *const Signal)).notify(),
|signal| drop(SignalArc::from_raw(signal as *const Signal)),
)
};
pub fn block_on<F: Future>(mut fut: F) -> F::Output {
let signal = Arc::new(Signal::new());
let waker = unsafe {
Waker::from_raw(RawWaker::new(
Arc::into_raw(signal.clone()) as *const _,
&VTABLE,
))
};
loop {
let fut = unsafe { Pin::new_unchecked(&mut fut) };
match fut.poll(&mut Context::from_waker(&waker)) {
Poll::Pending => signal.wait(),
Poll::Ready(item) => break item,
}
}
}