use futures_util::future::BoxFuture; use futures_util::FutureExt; use std::{ cell::UnsafeCell, future::Future, marker::PhantomData, task::{Context, RawWaker, RawWakerVTable, Waker}, }; use crate::{callback::RuntimeState, new_stack::PluginState, FFIBox}; /// Future type for futures within the Runtime. /// /// Allows for plugins to spawn futures on the runtime. #[derive(Clone)] #[repr(C)] pub struct RuntimeFuture { /// The plugin's poll function, provided with the poll state for the future. poll_fn: unsafe extern "C" fn(*const RuntimeFuture<()>, PluginState) -> RuntimeFuturePoll, /// The function to wake the future, should only be called when the future is ready to be polled. wake_fn: Option, PluginState)>, /// The inner value that the owning plugin can use to poll the future poll_state: PluginState, /// The waker that the plugin uses for notification of the future's completion waker_state: Option, /// Whether its safe for the plugin to poll this future, panic if we think /// we're supposed to poll and this is not set to `true`. can_poll: bool, _output_marker: PhantomData, } unsafe impl Send for RuntimeFuture where Output: Send {} unsafe impl Sync for RuntimeFuture where Output: Sync {} #[repr(C)] pub struct RuntimeWakerCallback { callback: PluginState, waker_func: unsafe extern "C" fn(PluginState), } #[repr(C)] pub enum RuntimeFuturePoll { Ready(FFIBox<()>), Pending, } pub struct WakerState { waker: Waker, } impl RuntimeFuture { pub(crate) unsafe fn poll(&mut self) -> RuntimeFuturePoll { todo!() } } unsafe extern "C" fn wake(_waker: PluginState) {} pub struct LocalRuntimeFuture { inner: BoxFuture<'static, Output>, runtime_future: UnsafeCell>, } unsafe impl Send for LocalRuntimeFuture where Output: Send {} unsafe impl Sync for LocalRuntimeFuture where Output: Sync {} impl LocalRuntimeFuture { pub fn finalize(self) {} pub fn into_runtime(&self) -> RuntimeFuture { todo!() } } pub trait RuntimeFuturesExt { fn spawn_future + Send + Sync + 'static>( &self, future: F, ) -> RuntimeFuture; } impl RuntimeFuturesExt for RuntimeState { fn spawn_future + Send + Sync + 'static>( &self, future: F, ) -> RuntimeFuture { let type_eraser = async move { let result = future.await; FFIBox::from_box(Box::new(result)).untyped() }; let runtime_future = RuntimeFuture { poll_fn: poll_local, wake_fn: None, poll_state: PluginState::from(type_eraser.boxed()), waker_state: None, can_poll: true, _output_marker: PhantomData, }; runtime_future } } unsafe extern "C" fn poll_local( _future: *const RuntimeFuture<()>, mut future_state: PluginState, ) -> RuntimeFuturePoll { let mut future: Box>> = future_state.transmute_owned(); let runtime_future = future.as_ref(); let raw_waker = RawWaker::new( Box::into_raw(Box::new(runtime_future)) as *const (), &RUNTIME_FUTURE_WAKER_VTABLE, ); let waker = unsafe { Waker::from_raw(raw_waker) }; // SAFETY: Pretty sure this has to be static lol let poll_result = future.poll_unpin(&mut Context::from_waker(&waker)); #[allow(unused_assignments)] { // This is meant to communicate with the compiler the lifecycle of the object future_state = PluginState::from_raw(future); } match poll_result { std::task::Poll::Ready(result) => RuntimeFuturePoll::Ready(result), std::task::Poll::Pending => RuntimeFuturePoll::Pending, } } pub static RUNTIME_FUTURE_WAKER_VTABLE: RawWakerVTable = RawWakerVTable::new( runtime_waker_vtable::waker_clone, runtime_waker_vtable::waker_wake, runtime_waker_vtable::waker_wake_by_ref, runtime_waker_vtable::waker_drop, ); mod runtime_waker_vtable { use std::task::RawWaker; pub unsafe fn waker_clone(_data: *const ()) -> RawWaker { todo!() } pub unsafe fn waker_wake(_data: *const ()) { todo!() } pub unsafe fn waker_wake_by_ref(_data: *const ()) { todo!() } pub unsafe fn waker_drop(_data: *const ()) { // no-op } } /// Allows for a remote future to be polled on the target. /// /// The target can be the host or a plugin, but the future should only be polled by one /// source. impl Future for RuntimeFuture { type Output = Output; fn poll( mut self: std::pin::Pin<&mut Self>, cx: &mut Context<'_>, ) -> std::task::Poll { let waker_state = WakerState { waker: cx.waker().clone(), }; let waker_state = PluginState::from(waker_state); self.waker_state = Some(waker_state); self.wake_fn = Some(wake_local); match unsafe { (self.poll_fn)( &*self as *const RuntimeFuture<_> as *const RuntimeFuture<()>, self.poll_state, ) } { RuntimeFuturePoll::Ready(result) => { let result: Output = unsafe { *result.retype::().into_box() }; std::task::Poll::Ready(result) } RuntimeFuturePoll::Pending => std::task::Poll::Pending, } } } unsafe extern "C" fn wake_local(_future: *const RuntimeFuture<()>, _waker_state: PluginState) { todo!() }