JavaScript is disabled, refresh for a better experience. ambee/giterated

ambee/giterated

Git repository hosting, collaboration, and discovery for the Fediverse.

More progress :)

Amber - ⁨1⁩ year ago

parent: tbd commit: ⁨92c3f32

⁨giterated-plugin/src/future.rs⁩ - ⁨5846⁩ bytes
Raw
1 use futures_util::future::BoxFuture;
2 use futures_util::FutureExt;
3 use std::{
4 cell::UnsafeCell,
5 future::Future,
6 marker::PhantomData,
7 task::{Context, RawWaker, RawWakerVTable, Waker},
8 };
9
10 use crate::{callback::RuntimeState, new_stack::PluginState, FFIBox};
11
12 /// Future type for futures within the Runtime.
13 ///
14 /// Allows for plugins to spawn futures on the runtime.
15 #[derive(Clone)]
16 #[repr(C)]
17 pub struct RuntimeFuture<Output> {
18 /// The plugin's poll function, provided with the poll state for the future.
19 poll_fn: unsafe extern "C" fn(*const RuntimeFuture<()>, PluginState) -> RuntimeFuturePoll,
20 /// The function to wake the future, should only be called when the future is ready to be polled.
21 wake_fn: Option<unsafe extern "C" fn(*const RuntimeFuture<()>, PluginState)>,
22
23 /// The inner value that the owning plugin can use to poll the future
24 poll_state: PluginState,
25 /// The waker that the plugin uses for notification of the future's completion
26 waker_state: Option<PluginState>,
27
28 /// Whether its safe for the plugin to poll this future, panic if we think
29 /// we're supposed to poll and this is not set to `true`.
30 can_poll: bool,
31
32 _output_marker: PhantomData<Output>,
33 }
34
35 unsafe impl<Output> Send for RuntimeFuture<Output> where Output: Send {}
36 unsafe impl<Output> Sync for RuntimeFuture<Output> where Output: Sync {}
37
38 #[repr(C)]
39 pub struct RuntimeWakerCallback {
40 callback: PluginState,
41 waker_func: unsafe extern "C" fn(PluginState),
42 }
43
44 #[repr(C)]
45 pub enum RuntimeFuturePoll {
46 Ready(FFIBox<()>),
47 Pending,
48 }
49
50 pub struct WakerState {
51 waker: Waker,
52 }
53
54 impl<Output> RuntimeFuture<Output> {
55 pub(crate) unsafe fn poll(&mut self) -> RuntimeFuturePoll {
56 todo!()
57 }
58 }
59
60 unsafe extern "C" fn wake(_waker: PluginState) {}
61
62 pub struct LocalRuntimeFuture<Output> {
63 inner: BoxFuture<'static, Output>,
64 runtime_future: UnsafeCell<RuntimeFuture<Output>>,
65 }
66
67 unsafe impl<Output> Send for LocalRuntimeFuture<Output> where Output: Send {}
68 unsafe impl<Output> Sync for LocalRuntimeFuture<Output> where Output: Sync {}
69
70 impl<Output> LocalRuntimeFuture<Output> {
71 pub fn finalize(self) {}
72
73 pub fn into_runtime(&self) -> RuntimeFuture<Output> {
74 todo!()
75 }
76 }
77
78 pub trait RuntimeFuturesExt {
79 fn spawn_future<Output, F: Future<Output = Output> + Send + Sync + 'static>(
80 &self,
81 future: F,
82 ) -> RuntimeFuture<Output>;
83 }
84
85 impl RuntimeFuturesExt for RuntimeState {
86 fn spawn_future<Output, F: Future<Output = Output> + Send + Sync + 'static>(
87 &self,
88 future: F,
89 ) -> RuntimeFuture<Output> {
90 let type_eraser = async move {
91 let result = future.await;
92
93 FFIBox::from_box(Box::new(result)).untyped()
94 };
95
96 let runtime_future = RuntimeFuture {
97 poll_fn: poll_local,
98 wake_fn: None,
99 poll_state: PluginState::from(type_eraser.boxed()),
100 waker_state: None,
101 can_poll: true,
102 _output_marker: PhantomData,
103 };
104
105 runtime_future
106 }
107 }
108
109 unsafe extern "C" fn poll_local(
110 _future: *const RuntimeFuture<()>,
111 mut future_state: PluginState,
112 ) -> RuntimeFuturePoll {
113 let mut future: Box<BoxFuture<'static, FFIBox<()>>> = future_state.transmute_owned();
114 let runtime_future = future.as_ref();
115
116 let raw_waker = RawWaker::new(
117 Box::into_raw(Box::new(runtime_future)) as *const (),
118 &RUNTIME_FUTURE_WAKER_VTABLE,
119 );
120
121 let waker = unsafe { Waker::from_raw(raw_waker) };
122
123 // SAFETY: Pretty sure this has to be static lol
124 let poll_result = future.poll_unpin(&mut Context::from_waker(&waker));
125
126 #[allow(unused_assignments)]
127 {
128 // This is meant to communicate with the compiler the lifecycle of the object
129 future_state = PluginState::from_raw(future);
130 }
131
132 match poll_result {
133 std::task::Poll::Ready(result) => RuntimeFuturePoll::Ready(result),
134 std::task::Poll::Pending => RuntimeFuturePoll::Pending,
135 }
136 }
137
138 pub static RUNTIME_FUTURE_WAKER_VTABLE: RawWakerVTable = RawWakerVTable::new(
139 runtime_waker_vtable::waker_clone,
140 runtime_waker_vtable::waker_wake,
141 runtime_waker_vtable::waker_wake_by_ref,
142 runtime_waker_vtable::waker_drop,
143 );
144
145 mod runtime_waker_vtable {
146 use std::task::RawWaker;
147
148 pub unsafe fn waker_clone(_data: *const ()) -> RawWaker {
149 todo!()
150 }
151
152 pub unsafe fn waker_wake(_data: *const ()) {
153 todo!()
154 }
155
156 pub unsafe fn waker_wake_by_ref(_data: *const ()) {
157 todo!()
158 }
159
160 pub unsafe fn waker_drop(_data: *const ()) {
161 // no-op
162 }
163 }
164
165 /// Allows for a remote future to be polled on the target.
166 ///
167 /// The target can be the host or a plugin, but the future should only be polled by one
168 /// source.
169 impl<Output: Unpin> Future for RuntimeFuture<Output> {
170 type Output = Output;
171
172 fn poll(
173 mut self: std::pin::Pin<&mut Self>,
174 cx: &mut Context<'_>,
175 ) -> std::task::Poll<Self::Output> {
176 let waker_state = WakerState {
177 waker: cx.waker().clone(),
178 };
179
180 let waker_state = PluginState::from(waker_state);
181
182 self.waker_state = Some(waker_state);
183 self.wake_fn = Some(wake_local);
184
185 match unsafe {
186 (self.poll_fn)(
187 &*self as *const RuntimeFuture<_> as *const RuntimeFuture<()>,
188 self.poll_state,
189 )
190 } {
191 RuntimeFuturePoll::Ready(result) => {
192 let result: Output = unsafe { *result.retype::<Output>().into_box() };
193
194 std::task::Poll::Ready(result)
195 }
196 RuntimeFuturePoll::Pending => std::task::Poll::Pending,
197 }
198 }
199 }
200
201 unsafe extern "C" fn wake_local(_future: *const RuntimeFuture<()>, _waker_state: PluginState) {
202 todo!()
203 }
204