-
Notifications
You must be signed in to change notification settings - Fork 234
/
mod.rs
149 lines (136 loc) · 5.9 KB
/
mod.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::{future::Future, sync::Arc};
mod future;
mod scheduler;
use future::*;
use scheduler::*;
#[cfg(test)]
mod tests;
use crate::{derive_ffi_traits, Handle, HandleAlloc, LiftArgsError, LowerReturn, RustCallStatus};
/// Result code for [rust_future_poll]. This is passed to the continuation function.
#[repr(i8)]
#[derive(Debug, PartialEq, Eq)]
pub enum RustFuturePoll {
/// The future is ready and is waiting for [rust_future_complete] to be called
Ready = 0,
/// The future might be ready and [rust_future_poll] should be called again
MaybeReady = 1,
}
/// Foreign callback that's passed to [rust_future_poll]
///
/// The Rust side of things calls this when the foreign side should call [rust_future_poll] again
/// to continue progress on the future.
pub type RustFutureContinuationCallback = extern "C" fn(callback_data: u64, RustFuturePoll);
// === Public FFI API ===
/// Create a new [Handle] for a Rust future
///
/// For each exported async function, UniFFI will create a scaffolding function that uses this to
/// create the [Handle] to pass to the foreign code.
// Need to allow let_and_return, or clippy complains when the `ffi-trace` feature is disabled.
#[allow(clippy::let_and_return)]
pub fn rust_future_new<F, T, UT>(future: F, tag: UT) -> Handle
where
// F is the future type returned by the exported async function. It needs to be Send + `static
// since it will move between threads for an indeterminate amount of time as the foreign
// executor calls polls it and the Rust executor wakes it. It does not need to by `Sync`,
// since we synchronize all access to the values.
F: Future<Output = Result<T, LiftArgsError>> + Send + 'static,
// T is the output of the Future. It needs to implement [LowerReturn]. Also it must be Send +
// 'static for the same reason as F.
T: LowerReturn<UT> + Send + 'static,
// The UniFfiTag ZST. The Send + 'static bound is to keep rustc happy.
UT: Send + 'static,
// Needed to allocate a handle
dyn RustFutureFfi<T::ReturnType>: HandleAlloc<UT>,
{
let handle = <dyn RustFutureFfi<T::ReturnType> as HandleAlloc<UT>>::new_handle(
RustFuture::new(future, tag) as Arc<dyn RustFutureFfi<T::ReturnType>>,
);
trace!("rust_future_new: {handle:?}");
handle
}
/// Poll a Rust future
///
/// When the future is ready to progress the continuation will be called with the `data` value and
/// a [RustFuturePoll] value. For each [rust_future_poll] call the continuation will be called
/// exactly once.
///
/// # Safety
///
/// The [Handle] must not previously have been passed to [rust_future_free]
pub unsafe fn rust_future_poll<ReturnType, UT>(
handle: Handle,
callback: RustFutureContinuationCallback,
data: u64,
) where
dyn RustFutureFfi<ReturnType>: HandleAlloc<UT>,
{
trace!("rust_future_poll: {handle:?}");
<dyn RustFutureFfi<ReturnType> as HandleAlloc<UT>>::get_arc(handle).ffi_poll(callback, data)
}
/// Cancel a Rust future
///
/// Any current and future continuations will be immediately called with RustFuturePoll::Ready.
///
/// This is needed for languages like Swift, which continuation to wait for the continuation to be
/// called when tasks are cancelled.
///
/// # Safety
///
/// The [Handle] must not previously have been passed to [rust_future_free]
pub unsafe fn rust_future_cancel<ReturnType, UT>(handle: Handle)
where
dyn RustFutureFfi<ReturnType>: HandleAlloc<UT>,
{
trace!("rust_future_cancel: {handle:?}");
<dyn RustFutureFfi<ReturnType> as HandleAlloc<UT>>::get_arc(handle).ffi_cancel()
}
/// Complete a Rust future
///
/// Note: the actually extern "C" scaffolding functions can't be generic, so we generate one for
/// each supported FFI type.
///
/// # Safety
///
/// - The [Handle] must not previously have been passed to [rust_future_free]
/// - The `T` param must correctly correspond to the [rust_future_new] call. It must
/// be `<Output as LowerReturn<UT>>::ReturnType`
pub unsafe fn rust_future_complete<ReturnType, UT>(
handle: Handle,
out_status: &mut RustCallStatus,
) -> ReturnType
where
dyn RustFutureFfi<ReturnType>: HandleAlloc<UT>,
{
trace!("rust_future_complete: {handle:?}");
<dyn RustFutureFfi<ReturnType> as HandleAlloc<UT>>::get_arc(handle).ffi_complete(out_status)
}
/// Free a Rust future, dropping the strong reference and releasing all references held by the
/// future.
///
/// # Safety
///
/// The [Handle] must not previously have been passed to [rust_future_free]
pub unsafe fn rust_future_free<ReturnType, UT>(handle: Handle)
where
dyn RustFutureFfi<ReturnType>: HandleAlloc<UT>,
{
trace!("rust_future_free: {handle:?}");
<dyn RustFutureFfi<ReturnType> as HandleAlloc<UT>>::consume_handle(handle).ffi_free()
}
// Derive HandleAlloc for dyn RustFutureFfi<T> for all FFI return types
derive_ffi_traits!(impl<UT> HandleAlloc<UT> for dyn RustFutureFfi<u8>);
derive_ffi_traits!(impl<UT> HandleAlloc<UT> for dyn RustFutureFfi<i8>);
derive_ffi_traits!(impl<UT> HandleAlloc<UT> for dyn RustFutureFfi<u16>);
derive_ffi_traits!(impl<UT> HandleAlloc<UT> for dyn RustFutureFfi<i16>);
derive_ffi_traits!(impl<UT> HandleAlloc<UT> for dyn RustFutureFfi<u32>);
derive_ffi_traits!(impl<UT> HandleAlloc<UT> for dyn RustFutureFfi<i32>);
derive_ffi_traits!(impl<UT> HandleAlloc<UT> for dyn RustFutureFfi<u64>);
derive_ffi_traits!(impl<UT> HandleAlloc<UT> for dyn RustFutureFfi<i64>);
derive_ffi_traits!(impl<UT> HandleAlloc<UT> for dyn RustFutureFfi<f32>);
derive_ffi_traits!(impl<UT> HandleAlloc<UT> for dyn RustFutureFfi<f64>);
derive_ffi_traits!(impl<UT> HandleAlloc<UT> for dyn RustFutureFfi<*const std::ffi::c_void>);
derive_ffi_traits!(impl<UT> HandleAlloc<UT> for dyn RustFutureFfi<crate::RustBuffer>);
derive_ffi_traits!(impl<UT> HandleAlloc<UT> for dyn RustFutureFfi<()>);