use std::future::Future;
use std::pin::Pin;
use std::sync::{Arc, Mutex};
use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};
type BoxedFuture = Pin<Box<dyn Future<Output = ()> + Send + 'static>>;
static ASYNC_TASKS: Mutex<Vec<BoxedFuture>> = Mutex::new(Vec::new());
pub fn spawn<F>(future: F)
where
F: Future<Output = ()> + Send + 'static,
{
let mut tasks = ASYNC_TASKS.lock().unwrap();
tasks.push(Box::pin(future));
}
fn dummy_raw_waker() -> RawWaker {
fn no_op(_: *const ()) {}
fn clone(_: *const ()) -> RawWaker {
dummy_raw_waker()
}
static VTABLE: RawWakerVTable = RawWakerVTable::new(clone, no_op, no_op, no_op);
RawWaker::new(std::ptr::null(), &VTABLE)
}
#[cfg(not(test))]
pub fn block_on<F: Future>(future: F) -> F::Output {
use objc::{class, msg_send, sel, sel_impl};
let waker = unsafe { Waker::from_raw(dummy_raw_waker()) };
let mut cx = Context::from_waker(&waker);
let mut future = std::pin::pin!(future);
loop {
match future.as_mut().poll(&mut cx) {
Poll::Ready(val) => return val,
Poll::Pending => {
let run_loop = class!(NSRunLoop);
unsafe {
let mode = class!(NSDefaultRunLoopMode);
let date_cls = class!(NSDate);
let date: *mut objc::runtime::Object = msg_send![date_cls, distantPast];
let _: () = msg_send![run_loop, runMode: mode beforeDate: date];
}
std::thread::yield_now();
}
}
}
}
#[cfg(test)]
pub fn block_on<F: Future>(future: F) -> F::Output {
let waker = unsafe { Waker::from_raw(dummy_raw_waker()) };
let mut cx = Context::from_waker(&waker);
let mut future = std::pin::pin!(future);
loop {
match future.as_mut().poll(&mut cx) {
Poll::Ready(val) => return val,
Poll::Pending => std::thread::yield_now(),
}
}
}
pub fn on_click_async<F, Fut>(f: F) -> usize
where
F: Fn() -> Fut + Send + Sync + 'static,
Fut: Future<Output = ()> + Send + 'static,
{
let id = crate::event::next_id();
let f = Arc::new(f);
crate::event::register(id, move || {
let f = f.clone();
std::thread::spawn(move || {
block_on(f());
});
});
id
}
#[cfg(test)]
mod tests {
#[test]
fn block_on_completes_ready_future() {
let v = super::block_on(async { 41 + 1 });
assert_eq!(v, 42);
}
#[test]
fn block_on_completes_pending_then_ready() {
use std::future::poll_fn;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
let n = Arc::new(AtomicUsize::new(0));
let n2 = n.clone();
let out = super::block_on(async move {
poll_fn(move |cx| {
let c = n2.fetch_add(1, Ordering::SeqCst);
if c == 0 {
cx.waker().wake_by_ref();
std::task::Poll::Pending
} else {
std::task::Poll::Ready(99)
}
})
.await
});
assert_eq!(out, 99);
}
}