use futures_util::future::BoxFuture; use futures_util::FutureExt; use giterated_abi::{ state::State, value_ex::FfiValueUntyped, FfiFuture, FfiValue, FfiValueMut, FfiValueRef, RuntimeFuturePoll, }; use std::{ cell::UnsafeCell, future::Future, mem::transmute, ops::Deref, task::{Context, RawWaker, RawWakerVTable, Waker}, }; #[repr(C)] pub struct RuntimeWakerCallback { callback: FfiValueUntyped, waker_func: unsafe extern "C" fn(FfiValueUntyped), } pub struct WakerState { waker: Waker, } unsafe extern "C" fn wake(_waker: FfiValueUntyped) {} 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) -> FfiFuture { todo!() } } pub trait RuntimeFuturesExt { fn spawn_future + Send + Sync + 'static>( &self, future: F, ) -> RuntimeFuture; } impl RuntimeFuturesExt for State { 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 = unsafe { FfiFuture::from_raw(poll_local, type_eraser.boxed()) }; // RuntimeFuture(runtime_future) todo!() } } unsafe extern "C" fn poll_local( _future: FfiValueMut>, future_state: FfiValueMut<()>, ) -> RuntimeFuturePoll { let mut future_state: FfiValueMut>> = transmute(future_state); let runtime_future = future_state.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_state.poll_unpin(&mut Context::from_waker(&waker)); 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 } } #[repr(transparent)] pub struct RuntimeFuture(FfiFuture); /// 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(), }; unsafe { self.0.write_waker(wake_local, waker_state) }; match unsafe { self.0.poll() } { RuntimeFuturePoll::Ready(result) => { let result: FfiValue = unsafe { transmute(result) }; std::task::Poll::Ready(result.take()) } RuntimeFuturePoll::Pending => std::task::Poll::Pending, } } } /// The function used to wake a local future over FFI unsafe extern "C" fn wake_local(future: FfiValueMut>) { if let Some(waker) = &future.waker_state { let waker: FfiValueRef = unsafe { transmute(waker.deref()) }; waker.waker.wake_by_ref(); } }