medea_jason/platform/dart/executor/
mod.rs

1//! Executor of [`Future`]s for the Dart environment.
2
3mod task;
4
5use std::{
6    ptr,
7    sync::{Arc, atomic, atomic::AtomicI64},
8};
9
10use dart_sys::{
11    _Dart_CObject__bindgen_ty_1, Dart_CObject,
12    Dart_CObject_Type_Dart_CObject_kInt64, Dart_Port,
13};
14
15pub use self::task::Task;
16use crate::{api::propagate_panic, platform::utils::dart_api};
17
18/// Runs a Rust [`Future`] on the current thread.
19pub fn spawn(fut: impl Future<Output = ()> + 'static) {
20    Task::spawn(Box::pin(fut));
21}
22
23/// Atomic variant of a [`Dart_Port`].
24type AtomicDartPort = AtomicI64;
25
26/// [`Dart_Port`] used to send [`Task`]'s poll commands so Dart will poll Rust
27/// [`Future`]s.
28///
29/// Must be initialized with the [`rust_executor_init()`] function during FFI
30/// initialization.
31static WAKE_PORT: AtomicDartPort = AtomicI64::new(0);
32
33/// Initializes Dart-driven async [`Task`] executor.
34///
35/// On a Dart side you should continuously read channel to get [`Task`]s
36/// addresses for polling.
37///
38/// # Safety
39///
40/// Must ONLY be called by Dart during FFI initialization.
41#[unsafe(no_mangle)]
42pub unsafe extern "C" fn rust_executor_init(wake_port: Dart_Port) {
43    WAKE_PORT.store(wake_port, atomic::Ordering::Release);
44}
45
46/// Polls the provided [`Task`].
47///
48/// # Safety
49///
50/// Valid [`Task`] pointer must be provided.
51///
52/// # Panics
53///
54/// If called not on the same thread where the [`Task`] was originally created.
55#[unsafe(no_mangle)]
56pub unsafe extern "C" fn rust_executor_poll_task(task: ptr::NonNull<Task>) {
57    propagate_panic(move || unsafe { Arc::from_raw(task.as_ptr()).poll() });
58}
59
60/// Commands an external Dart executor to poll the provided [`Task`].
61///
62/// Sends command that contains the provided [`Task`] to the configured
63/// [`WAKE_PORT`]. When received, Dart must poll it by calling the
64/// [`rust_executor_poll_task()`] function.
65///
66/// # Panics
67///
68/// If Dart-driven async [`Task`] executor is not initialized.
69fn task_wake(task: Arc<Task>) {
70    let wake_port = WAKE_PORT.load(atomic::Ordering::Acquire);
71    assert!(wake_port > 0, "`WAKE_PORT` address must be initialized");
72    let task = Arc::into_raw(task);
73
74    let mut task_addr = Dart_CObject {
75        type_: Dart_CObject_Type_Dart_CObject_kInt64,
76        value: _Dart_CObject__bindgen_ty_1 { as_int64: task as i64 },
77    };
78
79    let enqueued =
80        unsafe { dart_api::post_c_object(wake_port, &raw mut task_addr) };
81    if !enqueued {
82        log::warn!("Could not send message to Dart's native port");
83        unsafe {
84            drop(Arc::from_raw(task));
85        }
86    }
87}