diff --git a/Cargo.lock b/Cargo.lock index e54c6c9a8..59ddc2363 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -102,7 +102,7 @@ dependencies = [ "pin-project-lite 0.2.6", "rand 0.8.3", "regex", - "serde 1.0.125", + "serde 1.0.126", "serde_json", "serde_urlencoded 0.7.0", "sha-1", @@ -129,7 +129,7 @@ dependencies = [ "futures-core", "http", "log", - "serde 1.0.125", + "serde 1.0.126", "serde_json", "serde_urlencoded 0.7.0", "slab", @@ -157,7 +157,7 @@ dependencies = [ "http", "log", "regex", - "serde 1.0.125", + "serde 1.0.126", ] [[package]] @@ -253,7 +253,7 @@ dependencies = [ "once_cell", "pin-project 1.0.7", "regex", - "serde 1.0.125", + "serde 1.0.126", "serde_json", "serde_urlencoded 0.7.0", "smallvec", @@ -467,7 +467,7 @@ dependencies = [ "percent-encoding", "pin-project-lite 0.2.6", "rand 0.8.3", - "serde 1.0.125", + "serde 1.0.126", "serde_json", "serde_urlencoded 0.7.0", ] @@ -655,7 +655,7 @@ dependencies = [ "lazy_static", "nom", "rust-ini", - "serde 1.0.125", + "serde 1.0.126", "serde-hjson", "serde_json", "toml", @@ -1060,7 +1060,7 @@ dependencies = [ "hyper 0.14.7", "hyper-tls", "mime", - "serde 1.0.125", + "serde 1.0.126", "serde_json", "tokio 1.5.0", "url", @@ -1316,7 +1316,7 @@ dependencies = [ "heck", "peg", "quote 1.0.9", - "serde 1.0.125", + "serde 1.0.126", "serde_json", "syn 1.0.72", "textwrap 0.12.1", @@ -1516,7 +1516,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac34a56cfd4acddb469cc7fff187ed5ac36f498ba085caf8bbc725e3ff474058" dependencies = [ "humantime", - "serde 1.0.125", + "serde 1.0.126", ] [[package]] @@ -1833,7 +1833,7 @@ dependencies = [ "reqwest", "rust-argon2", "rust-crypto", - "serde 1.0.125", + "serde 1.0.126", "serde_json", "serde_yaml", "serial_test", @@ -1863,7 +1863,7 @@ dependencies = [ "async-trait", "derive_more", "medea-macro 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.125", + "serde 1.0.126", "serde_json", "serde_with", ] @@ -1877,7 +1877,7 @@ dependencies = [ "async-trait", "derive_more", "medea-macro 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.125", + "serde 1.0.126", "serde_json", "serde_with", ] @@ -1896,7 +1896,7 @@ dependencies = [ "humantime-serde", "medea-control-api-proto", "protobuf", - "serde 1.0.125", + "serde 1.0.126", "serde_json", "slog", "slog-async", @@ -1958,7 +1958,7 @@ dependencies = [ "medea-reactive 0.1.1", "mockall", "predicates-tree", - "serde 1.0.125", + "serde 1.0.126", "serde_json", "tracerr", "url", @@ -2742,7 +2742,7 @@ dependencies = [ "native-tls", "percent-encoding", "pin-project-lite 0.2.6", - "serde 1.0.125", + "serde 1.0.126", "serde_json", "serde_urlencoded 0.7.0", "tokio 1.5.0", @@ -2894,9 +2894,9 @@ checksum = "9dad3f759919b92c3068c696c15c3d17238234498bbdcc80f2c469606f948ac8" [[package]] name = "serde" -version = "1.0.125" +version = "1.0.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171" +checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03" dependencies = [ "serde_derive", ] @@ -2915,9 +2915,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.125" +version = "1.0.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b093b7a2bb58203b5da3056c05b4ec1fed827dcfdb37347a8841695263b3d06d" +checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43" dependencies = [ "proc-macro2 1.0.26", "quote 1.0.9", @@ -2932,7 +2932,7 @@ checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79" dependencies = [ "itoa", "ryu", - "serde 1.0.125", + "serde 1.0.126", ] [[package]] @@ -2943,7 +2943,7 @@ checksum = "9ec5d77e2d4c73717816afac02670d5c4f534ea95ed430442cad02e7a6e32c97" dependencies = [ "dtoa", "itoa", - "serde 1.0.125", + "serde 1.0.126", "url", ] @@ -2956,7 +2956,7 @@ dependencies = [ "form_urlencoded", "itoa", "ryu", - "serde 1.0.125", + "serde 1.0.126", ] [[package]] @@ -2966,7 +2966,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33d14cb8c1b03d86e97ecbb3128d3e2f81fd8f02805680537b8d9ccb7dd8960b" dependencies = [ "rustversion", - "serde 1.0.125", + "serde 1.0.126", "serde_with_macros", ] @@ -2990,7 +2990,7 @@ checksum = "15654ed4ab61726bf918a39cb8d98a2e2995b002387807fa6ba58fdf7f59bb23" dependencies = [ "dtoa", "linked-hash-map", - "serde 1.0.125", + "serde 1.0.126", "yaml-rust", ] @@ -3100,7 +3100,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ddc0d2aff1f8f325ef660d9a0eb6e6dcd20b30b3f581a5897f58bf42d061c37a" dependencies = [ "chrono", - "serde 1.0.125", + "serde 1.0.126", "serde_json", "slog", ] @@ -3215,7 +3215,7 @@ checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef" dependencies = [ "proc-macro2 1.0.26", "quote 1.0.9", - "serde 1.0.125", + "serde 1.0.126", "serde_derive", "syn 1.0.72", ] @@ -3229,7 +3229,7 @@ dependencies = [ "base-x", "proc-macro2 1.0.26", "quote 1.0.9", - "serde 1.0.125", + "serde 1.0.126", "serde_derive", "serde_json", "sha1", @@ -3558,7 +3558,7 @@ version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" dependencies = [ - "serde 1.0.125", + "serde 1.0.126", ] [[package]] @@ -3848,7 +3848,7 @@ dependencies = [ "mime_guess", "pin-project 0.4.28", "scoped-tls", - "serde 1.0.125", + "serde 1.0.126", "serde_json", "serde_urlencoded 0.6.1", "tokio 0.2.25", @@ -3871,7 +3871,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d54ee1d4ed486f78874278e63e4069fc1ab9f6a18ca492076ffb90c5eb2997fd" dependencies = [ "cfg-if 1.0.0", - "serde 1.0.125", + "serde 1.0.126", "serde_json", "wasm-bindgen-macro", ] @@ -3988,7 +3988,7 @@ dependencies = [ "cookie 0.12.0", "http", "log", - "serde 1.0.125", + "serde 1.0.126", "serde_derive", "serde_json", "time 0.1.43", diff --git a/jason/flutter/lib/jason.dart b/jason/flutter/lib/jason.dart index ba74fe0b1..ac436b6cd 100644 --- a/jason/flutter/lib/jason.dart +++ b/jason/flutter/lib/jason.dart @@ -5,6 +5,7 @@ import 'dart:io'; import 'media_manager.dart'; import 'room_handle.dart'; +import 'util/executor.dart'; import 'util/move_semantic.dart'; import 'util/nullable_pointer.dart'; import 'util/callback.dart' as callback; @@ -27,6 +28,12 @@ typedef _free_Dart = void Function(Pointer); final DynamicLibrary dl = _dl_load(); +/// [Executor] that drives Rust futures. +/// +/// Instantiated in the [_dl_load()] function, and must not be touched ever +/// after that. +var executor; + final _new = dl.lookupFunction<_new_C, _new_Dart>('Jason__new'); final _media_manager = dl.lookupFunction<_mediaManager_C, _mediaManager_Dart>( @@ -64,6 +71,8 @@ DynamicLibrary _dl_load() { callback.registerFunctions(dl); completer.registerFunctions(dl); + executor = Executor(dl); + return dl; } diff --git a/jason/flutter/lib/reconnect_handle.dart b/jason/flutter/lib/reconnect_handle.dart index 55f5374b4..82e333664 100644 --- a/jason/flutter/lib/reconnect_handle.dart +++ b/jason/flutter/lib/reconnect_handle.dart @@ -48,19 +48,19 @@ class ReconnectHandle { /// Tries to reconnect a `Room` in a loop with a growing backoff delay. /// /// The first attempt to reconnect is guaranteed to happen not earlier than - /// `starting_delay_ms`. + /// [starting_delay_ms]. /// /// Also, it guarantees that delay between reconnection attempts won't be - /// greater than `max_delay_ms`. + /// greater than [max_delay_ms]. /// /// After each reconnection attempt, delay between reconnections will be - /// multiplied by the given `multiplier` until it reaches `max_delay_ms`. + /// multiplied by the given [multiplier] until it reaches [max_delay_ms]. /// /// If the `Room` is already reconnecting then new reconnection attempt won't /// be performed. Instead, it will wait for the first reconnection attempt /// result and use it here. /// - /// If `multiplier` is negative number then `multiplier` will be considered as + /// If [multiplier] is negative number then [multiplier] will be considered as /// `0.0`. Future reconnectWithBackoff( int startingDelayMs, double multiplier, int maxDelay) async { diff --git a/jason/flutter/lib/room_handle.dart b/jason/flutter/lib/room_handle.dart index 654d5bca2..2055392c5 100644 --- a/jason/flutter/lib/room_handle.dart +++ b/jason/flutter/lib/room_handle.dart @@ -143,7 +143,7 @@ class RoomHandle { RoomHandle(this.ptr); /// Connects to a media server and joins the `Room` with the provided - /// authorization `token`. + /// authorization [token]. /// /// Authorization token has a fixed format: /// `{{ Host URL }}/{{ Room ID }}/{{ Member ID }}?token={{ Auth Token }}` @@ -157,23 +157,23 @@ class RoomHandle { } } - /// Updates this `Room`'s `MediaStreamSettings`. This affects all the - /// `PeerConnection`s in this `Room`. If `MediaStreamSettings` are configured + /// Updates this `Room`'s [MediaStreamSettings]. This affects all the + /// `PeerConnection`s in this `Room`. If [MediaStreamSettings] are configured /// for some `Room`, then this `Room` can only send media tracks that - /// correspond to these settings. `MediaStreamSettings` update will change + /// correspond to these settings. [MediaStreamSettings] update will change /// media tracks in all sending peers, so that might cause a new /// [getUserMedia()][1] request to happen. /// /// Media obtaining/injection errors are additionally fired to - /// `on_failed_local_media` callback. + /// [RoomHandle.onFailedLocalMedia()] callback. /// - /// If `stop_first` set to `true` then affected local `Tracks` will be - /// dropped before new `MediaStreamSettings` are applied. This is usually + /// If [stop_first] set to `true` then affected local [LocalMediaTrack]s will + /// be dropped before new [MediaStreamSettings] are applied. This is usually /// required when changing video source device due to hardware limitations, /// e.g. having an active track sourced from device `A` may hinder /// [getUserMedia()][1] requests to device `B`. /// - /// `rollback_on_fail` option configures `MediaStreamSettings` update request + /// [rollback_on_fail] option configures [MediaStreamSettings] update request /// to automatically rollback to previous settings if new settings cannot be /// applied. /// diff --git a/jason/flutter/lib/util/executor.dart b/jason/flutter/lib/util/executor.dart new file mode 100644 index 000000000..f3718de68 --- /dev/null +++ b/jason/flutter/lib/util/executor.dart @@ -0,0 +1,59 @@ +import 'dart:ffi'; +import 'dart:isolate'; + +typedef _executorInit_C = Void Function(Int64); +typedef _executorInit_Dart = void Function(int); + +typedef _executorPollTask_C = Uint8 Function(Pointer); +typedef _executorPollTask_Dart = int Function(Pointer); + +typedef _executorDropTask_C = Void Function(Pointer); +typedef _executorDropTask_Dart = void Function(Pointer); + +/// Executor used to drive Rust futures. +/// +/// It must be instantiated before calling any `async` Rust functions. +class Executor { + /// Pointer to a Rust function used to initialize Rust side of this + /// [Executor]. + final _executorInit_Dart _loopInit; + + /// Pointer to a Rust function used to poll Rust futures. + final _executorPollTask_Dart _taskPoll; + + /// Pointer to a Rust function used to drop Rust futures on completion. + final _executorDropTask_Dart _taskDrop; + + /// [ReceivePort] used to receive commands for polling Rust futures. + late ReceivePort _wakePort; + + /// Creates a new [Executor]. + /// + /// Initializes Rust part of the [Executor], creates a [ReceivePort] that + /// accepts commands to poll Rust futures. + Executor(DynamicLibrary dylib) + : _loopInit = dylib + .lookup>('rust_executor_init') + .asFunction(), + _taskPoll = dylib + .lookup>( + 'rust_executor_poll_task') + .asFunction(), + _taskDrop = dylib + .lookup>( + 'rust_executor_drop_task') + .asFunction() { + _wakePort = ReceivePort()..listen(_pollTask); + _loopInit(_wakePort.sendPort.nativePort); + } + + /// Polls a Rust future basing on the provided [message]. Drops that future if + /// it's completed. + void _pollTask(dynamic message) { + final task = Pointer.fromAddress(message); + + if (_taskPoll(task) == 0) { + _taskDrop(task); + } + } +} diff --git a/jason/src/api/dart/utils/mod.rs b/jason/src/api/dart/utils/mod.rs index 29faf0bf6..0d3b60a3c 100644 --- a/jason/src/api/dart/utils/mod.rs +++ b/jason/src/api/dart/utils/mod.rs @@ -4,24 +4,17 @@ mod string; use std::future::Future; use dart_sys::Dart_Handle; -use futures::FutureExt as _; -use crate::{api::DartValue, platform::utils::Completer}; +use crate::{ + api::DartValue, + platform::{spawn, utils::Completer}, +}; pub use self::{ arrays::PtrArray, string::{c_str_into_string, string_into_c_str}, }; -/// Spawns the provided [`Future`] on the Dart event loop. -pub fn spawn(fut: F) -where - F: Future + 'static, -{ - // TODO: Implement executor. - fut.now_or_never().unwrap(); -} - /// Extension trait for a [`Future`] allowing to convert Rust [`Future`]s to /// Dart `Future`s. pub trait IntoDartFuture { diff --git a/jason/src/platform/dart/api_dl/trampoline.c b/jason/src/platform/dart/api_dl/trampoline.c index 242c068c1..bee1bafb1 100644 --- a/jason/src/platform/dart/api_dl/trampoline.c +++ b/jason/src/platform/dart/api_dl/trampoline.c @@ -24,3 +24,7 @@ Dart_Handle Dart_HandleFromPersistent_DL_Trampolined( void Dart_DeletePersistentHandle_DL_Trampolined(Dart_PersistentHandle handle) { Dart_DeletePersistentHandle_DL(handle); } + +bool Dart_PostCObject_DL_Trampolined(Dart_Port port_id, Dart_CObject* message) { + return Dart_PostCObject_DL(port_id, message); +} diff --git a/jason/src/platform/dart/executor/mod.rs b/jason/src/platform/dart/executor/mod.rs new file mode 100644 index 000000000..2c17c5340 --- /dev/null +++ b/jason/src/platform/dart/executor/mod.rs @@ -0,0 +1,99 @@ +//! Executor of [`Future`]s for the Dart environment. + +mod task; + +use std::{future::Future, rc::Rc}; + +use dart_sys::{Dart_CObject, Dart_CObjectValue, Dart_CObject_Type, Dart_Port}; + +use crate::platform::dart::utils::dart_api::Dart_PostCObject_DL_Trampolined; + +use self::task::Task; + +/// Runs a Rust [`Future`] on the current thread. +pub fn spawn(future: impl Future + 'static) { + let task = Task::new(Box::pin(future)); + + // Task is leaked and will be freed by Dart calling the + // `rust_executor_drop_task()` function. + task_wake(Rc::into_raw(task)); +} + +/// A [`Dart_Port`] used to send [`Task`]'s poll commands so Dart will poll Rust +/// [`Future`]s. +/// +/// Must be initialized with the [`rust_executor_init()`] function during FFI +/// initialization. +static mut WAKE_PORT: Option = None; + +/// Initializes Dart-driven async [`Task`] executor. +/// +/// On a Dart side you should continuously read channel to get [`Task`]s +/// addresses for polling. +/// +/// # Safety +/// +/// Must ONLY be called by Dart during FFI initialization. +#[no_mangle] +pub unsafe extern "C" fn rust_executor_init(wake_port: Dart_Port) { + WAKE_PORT = Some(wake_port); +} + +/// Polls an incomplete [`Task`]. +/// +/// This function returns `true` if the [`Task`] is still [`Pending`], and +/// `false` once the [`Task`] is [`Ready`]. In this latter case it should be +/// dropped with the [`rust_executor_drop_task()`] function. +/// +/// # Safety +/// +/// Valid [`Task`] pointer must be provided. Must not be called if the provided +/// [`Task`] has been dropped (with the [`rust_executor_drop_task()`] function). +/// +/// [`Pending`]: std::task::Poll::Pending +/// [`Ready`]: std::task::Poll::Ready +#[no_mangle] +pub unsafe extern "C" fn rust_executor_poll_task(task: *mut Task) -> bool { + let task = task.as_mut().unwrap(); + + task.poll().is_pending() +} + +/// Drops a [`Task`]. +/// +/// Completed [`Task`]s should be dropped to avoid leaks. +/// +/// In some unusual cases (say on emergency shutdown or when executed too long) +/// [`Task`]s may be deleted before completion. +/// +/// # Safety +/// +/// Valid [`Task`] pointer must be provided. Must be called only once for a +/// specific [`Task`]. +#[no_mangle] +pub unsafe extern "C" fn rust_executor_drop_task(task: *const Task) { + drop(Rc::from_raw(task)) +} + +/// Commands an external Dart executor to poll the provided [`Task`]. +/// +/// Sends command that contains the provided [`Task`] to the configured +/// [`WAKE_PORT`]. When received, Dart must poll it by calling the +/// [`rust_executor_poll_task()`] function. +fn task_wake(task: *const Task) { + let wake_port = unsafe { WAKE_PORT }.unwrap(); + + let mut task_addr = Dart_CObject { + type_: Dart_CObject_Type::Int64, + value: Dart_CObjectValue { + as_int64: task as i64, + }, + }; + + let enqueued = + unsafe { Dart_PostCObject_DL_Trampolined(wake_port, &mut task_addr) }; + if !enqueued { + log::warn!("Could not send message to Dart's native port"); + unsafe { rust_executor_drop_task(task) }; + } +} diff --git a/jason/src/platform/dart/executor/task.rs b/jason/src/platform/dart/executor/task.rs new file mode 100644 index 000000000..e0d2d388c --- /dev/null +++ b/jason/src/platform/dart/executor/task.rs @@ -0,0 +1,108 @@ +//! [`Task`] for execution by a [`platform::dart::executor`]. + +use std::rc::Rc; + +use std::{ + cell::RefCell, + mem::ManuallyDrop, + task::{Context, Poll, RawWaker, RawWakerVTable, Waker}, +}; + +use futures::future::LocalBoxFuture; + +use crate::platform::dart::executor::task_wake; + +/// Inner [`Task`]'s data. +struct Inner { + /// An actual [`Future`] that this [`Task`] is driving. + future: LocalBoxFuture<'static, ()>, + + /// Handle for waking up this [`Task`]. + waker: Waker, +} + +/// Wrapper for a [`Future`] that can be polled by an external single threaded +/// Dart executor. +pub struct Task { + /// [`Task`]'s inner data containing an actual [`Future`] and its + /// [`Waker`]. Dropped on the [`Task`] completion. + inner: RefCell>, +} + +impl Task { + /// Creates a new [`Task`] out of the given [`Future`]. + #[must_use] + pub fn new(future: LocalBoxFuture<'static, ()>) -> Rc { + let this = Rc::new(Self { + inner: RefCell::new(None), + }); + + let waker = + unsafe { Waker::from_raw(Task::into_raw_waker(Rc::clone(&this))) }; + this.inner.borrow_mut().replace(Inner { future, waker }); + + this + } + + /// Polls the underlying [`Future`]. + /// + /// Polling after [`Future`]'s completion is no-op. + pub fn poll(&self) -> Poll<()> { + let mut borrow = self.inner.borrow_mut(); + + // Just ignore poll request if the `Future` is completed. + let inner = match borrow.as_mut() { + Some(inner) => inner, + None => return Poll::Ready(()), + }; + + let poll = { + let mut cx = Context::from_waker(&inner.waker); + inner.future.as_mut().poll(&mut cx) + }; + + // Cleanup resources if future is ready. + if poll.is_ready() { + *borrow = None; + } + + poll + } + + /// Calls the [`task_wake()`] function by the provided reference. + fn wake_by_ref(this: &Rc) { + task_wake(Rc::as_ptr(this)); + } + + /// Pretty much a copy of [`std::task::Wake`] implementation but for + /// `Rc` instead of `Arc` since we are sure + /// that everything will run on a single thread. + #[inline(always)] + fn into_raw_waker(this: Rc) -> RawWaker { + // Refer to `RawWakerVTable::new()` documentation for better + // understanding of what following functions do. + unsafe fn raw_clone(ptr: *const ()) -> RawWaker { + let ptr = ManuallyDrop::new(Rc::from_raw(ptr.cast::())); + Task::into_raw_waker(Rc::clone(&(*ptr))) + } + + unsafe fn raw_wake(ptr: *const ()) { + let ptr = Rc::from_raw(ptr.cast::()); + Task::wake_by_ref(&ptr); + } + + unsafe fn raw_wake_by_ref(ptr: *const ()) { + let ptr = ManuallyDrop::new(Rc::from_raw(ptr.cast::())); + Task::wake_by_ref(&ptr); + } + + unsafe fn raw_drop(ptr: *const ()) { + drop(Rc::from_raw(ptr.cast::())); + } + + const VTABLE: RawWakerVTable = + RawWakerVTable::new(raw_clone, raw_wake, raw_wake_by_ref, raw_drop); + + RawWaker::new(Rc::into_raw(this).cast::<()>(), &VTABLE) + } +} diff --git a/jason/src/platform/dart/mod.rs b/jason/src/platform/dart/mod.rs index 4eb78fc3a..8b73f4edf 100644 --- a/jason/src/platform/dart/mod.rs +++ b/jason/src/platform/dart/mod.rs @@ -10,6 +10,7 @@ pub mod constraints; pub mod error; +pub mod executor; pub mod ice_server; pub mod input_device_info; pub mod media_devices; @@ -22,11 +23,10 @@ pub mod utils; use std::time::Duration; -use futures::Future; - pub use self::{ constraints::{DisplayMediaStreamConstraints, MediaStreamConstraints}, error::Error, + executor::spawn, input_device_info::InputDeviceInfo, media_devices::{enumerate_devices, get_display_media, get_user_media}, media_track::MediaStreamTrack, @@ -51,15 +51,6 @@ pub fn init_logger() { ); } -/// Runs a Rust [`Future`] on the current thread. -#[inline] -pub fn spawn(task: F) -where - F: Future + 'static, -{ - unimplemented!() -} - /// [`Future`] which resolves after the provided [`Duration`]. /// /// [`Future`]: std::future::Future diff --git a/jason/src/platform/dart/utils/completer.rs b/jason/src/platform/dart/utils/completer.rs index f796ad993..acb977ba6 100644 --- a/jason/src/platform/dart/utils/completer.rs +++ b/jason/src/platform/dart/utils/completer.rs @@ -81,38 +81,38 @@ type CompleterFutureCaller = extern "C" fn(Dart_Handle) -> Dart_Handle; /// Stores pointer to the [`CompleterNewCaller`] extern function. /// -/// Should be initialized by Dart during FFI initialization phase. +/// Must be initialized by Dart during FFI initialization phase. static mut COMPLETER_NEW_CALLER: Option = None; /// Stores pointer to the [`CompleterCompleteVoidCaller`] extern function. /// -/// Should be initialized by Dart during FFI initialization phase. +/// Must be initialized by Dart during FFI initialization phase. static mut COMPLETER_COMPLETE_VOID_CALLER: Option = None; /// Stores pointer to the [`CompleterCompletePtrCaller`] extern function. /// -/// Should be initialized by Dart during FFI initialization phase. +/// Must be initialized by Dart during FFI initialization phase. static mut COMPLETER_COMPLETE_PTR_CALLER: Option = None; /// Stores pointer to the [`CompleterCompleteErrorCaller`] extern function. /// -/// Should be initialized by Dart during FFI initialization phase. +/// Must be initialized by Dart during FFI initialization phase. static mut COMPLETER_COMPLETE_ERROR_CALLER: Option< CompleterCompleteErrorCaller, > = None; /// Stores pointer to [`CompleterCompletePtrArrayCaller`] extern function. /// -/// Should be initialized by Dart during FFI initialization phase. +/// Must be initialized by Dart during FFI initialization phase. static mut COMPLETER_COMPLETE_PTR_ARRAY_CALLER: Option< CompleterCompletePtrArrayCaller, > = None; /// Stores pointer to [`CompleterFutureCaller`] extern function. /// -/// Should be initialized by Dart during FFI initialization phase. +/// Must be initialized by Dart during FFI initialization phase. static mut COMPLETER_FUTURE_CALLER: Option = None; /// Registers the provided [`CompleterNewCaller`] as [`COMPLETER_NEW_CALLER`]. diff --git a/jason/src/platform/dart/utils/dart_api.rs b/jason/src/platform/dart/utils/dart_api.rs index 6c50012e0..a3b5250a7 100644 --- a/jason/src/platform/dart/utils/dart_api.rs +++ b/jason/src/platform/dart/utils/dart_api.rs @@ -4,7 +4,7 @@ use std::ffi::c_void; -use dart_sys::{Dart_Handle, Dart_PersistentHandle}; +use dart_sys::{Dart_CObject, Dart_Handle, Dart_PersistentHandle, Dart_Port}; /// TODO: We should check everything returned from API with `Dart_IsError` and /// panic or `Dart_PropagateError`. @@ -37,6 +37,25 @@ extern "C" { pub fn Dart_DeletePersistentHandle_DL_Trampolined( object: Dart_PersistentHandle, ); + + /// Posts a `message` on some port. It will contain a [`Dart_CObject`] + /// object graph rooted in the `message`. + /// + /// While the `message` is being sent the state of the graph of + /// [`Dart_CObject`] structures rooted in the `message` should not be + /// accessed, as the message generation will make temporary modifications to + /// the data. When the message has been sent the graph will be fully + /// restored. + /// + /// If `true` is returned, the `message` was enqueued, and finalizers for + /// external typed data will eventually run, even if the receiving isolate + /// shuts down before processing the `message`. If `false` is returned, the + /// `message` was not enqueued and ownership of external typed data in the + /// `message` remains with the caller. + pub fn Dart_PostCObject_DL_Trampolined( + port_id: Dart_Port, + message: *mut Dart_CObject, + ) -> bool; } /// Initializes usage of Dynamically Linked Dart API.