use crate::application::submit_to_main_thread;
use crate::sys;
use std::cell::Cell;
use std::collections::HashMap;
use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::task::{Context, RawWaker, RawWakerVTable};
static NEXT_TASK_ID: AtomicUsize = AtomicUsize::new(1);
struct Inner {
task_id: usize,
}
impl Inner {
fn new(task_id: usize) -> Self {
Inner { task_id }
}
}
struct Waker {
inner: Arc<Inner>,
}
const WAKER_VTABLE: RawWakerVTable = RawWakerVTable::new(
|data| {
let w = unsafe { Arc::from_raw(data as *const Waker) };
let w2 = w.clone();
_ = Arc::into_raw(w); RawWaker::new(Arc::into_raw(w2) as *const (), &WAKER_VTABLE)
},
|data| {
let w = unsafe { Arc::from_raw(data as *const Waker) };
wake_task(w.inner.task_id);
},
|data| {
let w = unsafe { Arc::from_raw(data as *const Waker) };
wake_task(w.inner.task_id);
std::mem::forget(w);
},
|data| {
let w = unsafe { Arc::from_raw(data as *const Waker) };
drop(w);
},
);
impl Waker {
fn into_waker(self) -> std::task::Waker {
let arc_waker = Arc::into_raw(Arc::new(self));
unsafe { std::task::Waker::from_raw(RawWaker::new(arc_waker as *const (), &WAKER_VTABLE)) }
}
}
struct Task {
context: logwise::context::Context,
our_task_id: usize,
future: Pin<Box<dyn Future<Output = ()> + 'static>>,
wake_inner: Arc<Inner>,
}
fn wake_task(task_id: usize) {
crate::application::submit_to_main_thread("wake_task".to_string(), move || {
let mut pollable = POLLABLE.take();
pollable.push(task_id);
POLLABLE.replace(pollable);
main_executor_iter();
});
}
thread_local! {
static RUNNING: Cell<Option<HashMap<usize, Task>>> = const { Cell::new(None) };
static POLLABLE: Cell<Vec<usize>> = const { Cell::new(Vec::new()) };
}
pub async fn on_main_thread_async<R: Send + 'static, F: Future<Output = R> + Send + 'static>(
debug_label: String,
future: F,
) -> R {
let (sender, fut) = r#continue::continuation();
crate::application::submit_to_main_thread(debug_label.clone(), || {
already_on_main_thread_submit(debug_label, async move {
let r = future.await;
sender.send(r);
})
});
fut.await
}
pub fn already_on_main_thread_submit<F: Future<Output = ()> + 'static>(
debug_label: String,
future: F,
) {
assert!(sys::is_main_thread());
let task_id = NEXT_TASK_ID.fetch_add(1, Ordering::Relaxed);
let wake_inner = Arc::new(Inner::new(task_id));
let parent_context = logwise::context::Context::current();
let new_context = logwise::context::Context::new_task(
Some(parent_context),
debug_label.clone(),
logwise::Level::DebugInternal,
logwise::log_enabled!(logwise::Level::DebugInternal),
);
logwise::debuginternal_sync!(
"Creating task {id} {label}",
id = logwise::privacy::IPromiseItsNotPrivate(new_context.task_id()),
label = logwise::privacy::LogIt(debug_label)
);
let task = Task {
our_task_id: task_id,
context: new_context,
future: Box::pin(future),
wake_inner,
};
let mut pollable = POLLABLE.take();
pollable.push(task_id);
POLLABLE.replace(pollable);
let mut running = RUNNING.take().unwrap_or_default();
running.insert(task_id, task);
RUNNING.replace(Some(running));
main_executor_iter();
}
fn main_executor_iter() {
let begin_iter = crate::application::time::Instant::now();
let mut swap_pollable = POLLABLE.take();
let poll = swap_pollable.pop();
POLLABLE.replace(swap_pollable);
match poll {
None => {
}
Some(task) => {
let mut running = RUNNING.take().unwrap_or_default();
let mut task = running.remove(&task).unwrap();
let task_id = task.context.task_id();
RUNNING.replace(Some(running));
let waker = Waker {
inner: task.wake_inner.clone(),
};
let into_waker = waker.into_waker();
let parent = logwise::context::Context::current();
task.context.clone().set_current();
let mut context = Context::from_waker(&into_waker);
let poll_result = task.future.as_mut().poll(&mut context);
parent.set_current();
match poll_result {
std::task::Poll::Ready(()) => {
}
std::task::Poll::Pending => {
let mut running = RUNNING.take().unwrap_or_default();
running.insert(task.our_task_id, task);
RUNNING.replace(Some(running));
}
}
submit_to_main_thread("main_executor_iter".to_string(), main_executor_iter);
if begin_iter.elapsed() > crate::application::time::Duration::from_millis(10) {
logwise::warn_sync!(
"main_executor_iter {task} took too long: {duration}",
task = logwise::privacy::IPromiseItsNotPrivate(task_id),
duration = logwise::privacy::IPromiseItsNotPrivate(begin_iter.elapsed())
);
}
}
}
}