From e26fbb8f591f78f79e6cea19a2b2b9bb42ee8f4d Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Tue, 10 Sep 2024 00:12:04 +0900 Subject: [PATCH 1/5] allow choosing custom Rust runtime --- .github/workflows/quality_control.yaml | 4 +- documentation/docs/configuration.md | 1 - .../example/native/hub/Cargo.toml | 9 +- .../example/native/hub/src/lib.rs | 12 +- .../native/hub/src/sample_functions.rs | 14 +- .../template/native/hub/src/lib.rs | 12 ++ rust_crate/Cargo.toml | 1 - rust_crate/src/error.rs | 10 +- rust_crate/src/interface.rs | 14 +- rust_crate/src/interface_os.rs | 173 ++---------------- rust_crate/src/interface_web.rs | 14 +- rust_crate/src/lib.rs | 8 +- rust_crate/src/macros.rs | 4 +- rust_crate/src/shutdown.rs | 143 +++++++++++++++ 14 files changed, 222 insertions(+), 197 deletions(-) create mode 100644 rust_crate/src/shutdown.rs diff --git a/.github/workflows/quality_control.yaml b/.github/workflows/quality_control.yaml index 9051d3da..c01bf7b1 100644 --- a/.github/workflows/quality_control.yaml +++ b/.github/workflows/quality_control.yaml @@ -68,7 +68,9 @@ jobs: # That's why we are checking only the library crate. - name: Check for errors with all features enabled working-directory: rust_crate/ - run: cargo clippy --all-features + run: | + cargo clippy --all-features + cargo clippy --all-features --target wasm32-unknown-unknown - name: Analyze code run: | diff --git a/documentation/docs/configuration.md b/documentation/docs/configuration.md index 092dccc4..9be5e239 100644 --- a/documentation/docs/configuration.md +++ b/documentation/docs/configuration.md @@ -27,6 +27,5 @@ Customizing the behavior of the Rinf crate is possible through its crate feature rinf = { version = "0.0.0", features = ["feature-name"] } ``` -- `rt-multi-thread`: Starts a worker thread for each CPU core available on the system within the `tokio` runtime by enabling its `rt-multi-thread` feature. By default, the `tokio` runtime uses only one thread. Enabling this feature allows the `tokio` runtime to utilize all the cores on your computer. This feature does not affect applications on the web platform. - `show-backtrace`: Prints the full backtrace in the CLI when a panic occurs in debug mode. In general, backtrace is not very helpful when debugging async apps, so consider using [`tracing`](https://crates.io/crates/tracing) for logging purposes. Note that this feature does not affect debugging on the web platform. - `bevy`: Implements the `Event` trait from `bevy_ecs` for `DartSignal`, allowing Bevy's entity component system to listen for events from Dart. diff --git a/flutter_ffi_plugin/example/native/hub/Cargo.toml b/flutter_ffi_plugin/example/native/hub/Cargo.toml index a0dc4368..65e0464b 100644 --- a/flutter_ffi_plugin/example/native/hub/Cargo.toml +++ b/flutter_ffi_plugin/example/native/hub/Cargo.toml @@ -14,7 +14,12 @@ crate-type = ["lib", "cdylib", "staticlib"] [dependencies] rinf = "6.15.0" prost = "0.13.0" -tokio = { version = "1", features = ["rt", "sync", "time"] } -tokio_with_wasm = { version = "0.6.3", features = ["rt", "sync", "time"] } +tokio = { version = "1", features = ["rt", "sync", "time", "macros"] } +tokio_with_wasm = { version = "0.7.1", features = [ + "rt", + "sync", + "time", + "macros", +] } wasm-bindgen = "0.2.93" sample_crate = { path = "../sample_crate" } diff --git a/flutter_ffi_plugin/example/native/hub/src/lib.rs b/flutter_ffi_plugin/example/native/hub/src/lib.rs index 017c2991..ba951808 100755 --- a/flutter_ffi_plugin/example/native/hub/src/lib.rs +++ b/flutter_ffi_plugin/example/native/hub/src/lib.rs @@ -6,6 +6,7 @@ mod messages; mod sample_functions; // use tokio; +use common::*; use tokio_with_wasm::alias as tokio; rinf::write_interface!(); @@ -15,8 +16,17 @@ rinf::write_interface!(); // such as `tokio::fs::File::open`. // If you really need to use blocking code, // use `tokio::task::spawn_blocking`. -async fn main() { +#[tokio::main(flavor = "current_thread")] +async fn main() -> Result<()> { + // Spawn the concurrent tasks. tokio::spawn(sample_functions::tell_numbers()); tokio::spawn(sample_functions::stream_fractal()); tokio::spawn(sample_functions::run_debug_tests()); + + // Get the shutdown receiver from Rinf. + // This receiver will await a signal from Dart shutdown. + let shutdown_receiver = rinf::get_shutdown_receiver()?; + shutdown_receiver.await; + + Ok(()) } diff --git a/flutter_ffi_plugin/example/native/hub/src/sample_functions.rs b/flutter_ffi_plugin/example/native/hub/src/sample_functions.rs index 14ff51c7..ac304caa 100755 --- a/flutter_ffi_plugin/example/native/hub/src/sample_functions.rs +++ b/flutter_ffi_plugin/example/native/hub/src/sample_functions.rs @@ -5,22 +5,19 @@ use crate::messages; use crate::tokio; use rinf::debug_print; use std::time::Duration; -use tokio::sync::Mutex; // Using the `cfg` macro enables conditional statement. #[cfg(debug_assertions)] -const IS_DEBUG_MODE: bool = true; +static IS_DEBUG_MODE: bool = true; #[cfg(not(debug_assertions))] -const IS_DEBUG_MODE: bool = false; - -// This is one of the ways to keep a global mutable state in Rust. -// You can also use `tokio::sync::RwLock` or `std::lazy::LazyLock`. -static VECTOR: Mutex> = Mutex::const_new(Vec::new()); +static IS_DEBUG_MODE: bool = false; // Business logic for the counter widget. pub async fn tell_numbers() -> Result<()> { use messages::counter_number::*; + let mut vector = Vec::new(); + // Stream getter is generated from a marked Protobuf message. let receiver = SampleNumberInput::get_dart_signal_receiver()?; while let Some(dart_signal) = receiver.recv().await { @@ -30,8 +27,7 @@ pub async fn tell_numbers() -> Result<()> { let letter = number_input.letter; debug_print!("{letter}"); - // Use the global state and perform a simple calculation. - let mut vector = VECTOR.lock().await; + // Perform a simple calculation. vector.push(true); let current_number = (vector.len() as i32) * 7; diff --git a/flutter_ffi_plugin/template/native/hub/src/lib.rs b/flutter_ffi_plugin/template/native/hub/src/lib.rs index 981f304e..2c386fbc 100644 --- a/flutter_ffi_plugin/template/native/hub/src/lib.rs +++ b/flutter_ffi_plugin/template/native/hub/src/lib.rs @@ -15,19 +15,31 @@ rinf::write_interface!(); // such as `tokio::fs::File::open`. // If you really need to use blocking code, // use `tokio::task::spawn_blocking`. +#[tokio::main(flavor = "current_thread")] async fn main() { + // Spawn the concurrent tasks. tokio::spawn(communicate()); + + // Get the shutdown receiver from Rinf. + // This receiver will await a signal from Dart shutdown. + let shutdown_receiver = rinf::get_shutdown_receiver()?; + shutdown_receiver.await; + + Ok(()) } async fn communicate() -> Result<()> { use messages::basic::*; + // Send signals to Dart like below. SmallNumber { number: 7 }.send_signal_to_dart(); + // Get receivers that listen to Dart signals like below. let receiver = SmallText::get_dart_signal_receiver()?; while let Some(dart_signal) = receiver.recv().await { let message: SmallText = dart_signal.message; rinf::debug_print!("{message:?}"); } + Ok(()) } diff --git a/rust_crate/Cargo.toml b/rust_crate/Cargo.toml index 6c72b1fd..0e48b650 100644 --- a/rust_crate/Cargo.toml +++ b/rust_crate/Cargo.toml @@ -9,7 +9,6 @@ documentation = "https://rinf.cunarist.com" rust-version = "1.70" [features] -rt-multi-thread = ["tokio/rt-multi-thread"] show-backtrace = ["backtrace"] bevy = ["bevy_ecs"] diff --git a/rust_crate/src/error.rs b/rust_crate/src/error.rs index e0377050..0bb19662 100644 --- a/rust_crate/src/error.rs +++ b/rust_crate/src/error.rs @@ -5,7 +5,8 @@ use std::fmt; pub enum RinfError { LockDartIsolate, NoDartIsolate, - BuildRuntime, + LockShutdownReceiver, + NoShutdownReceiver, LockMessageChannel, BrokenMessageChannel, ClosedMessageChannel, @@ -24,8 +25,11 @@ impl fmt::Display for RinfError { RinfError::NoDartIsolate => { write!(f, "Dart isolate for Rust signals was not created.") } - RinfError::BuildRuntime => { - write!(f, "Could not build the tokio runtime.") + RinfError::LockShutdownReceiver => { + write!(f, "Could not acquire the shutdown receiver lock.") + } + RinfError::NoShutdownReceiver => { + write!(f, "Shutdown receiver was not created.") } RinfError::LockMessageChannel => { write!(f, "Could not acquire the message channel lock.") diff --git a/rust_crate/src/interface.rs b/rust_crate/src/interface.rs index 05b78f00..4e6be5d6 100644 --- a/rust_crate/src/interface.rs +++ b/rust_crate/src/interface.rs @@ -1,5 +1,4 @@ use crate::error::RinfError; -use std::future::Future; #[cfg(not(target_family = "wasm"))] use super::interface_os::*; @@ -27,23 +26,22 @@ pub struct DartSignal { /// the `Runtime` object itself might be moved between threads, /// along with all the tasks it manages. #[cfg(not(target_family = "wasm"))] -pub fn start_rust_logic(main_future: F) -> Result<(), RinfError> +pub fn start_rust_logic(main_fn: F) -> Result<(), RinfError> where - F: Future + Send + 'static, - T: Send + 'static, + F: Fn() -> T + Send + 'static, { - start_rust_logic_real(main_future) + start_rust_logic_real(main_fn) } /// Runs the async main function in Rust. /// On the web, futures usually don't implement the `Send` trait /// because JavaScript environment is fundamentally single-threaded. #[cfg(target_family = "wasm")] -pub fn start_rust_logic(main_future: F) -> Result<(), RinfError> +pub fn start_rust_logic(main_fn: F) -> Result<(), RinfError> where - F: Future + 'static, + F: Fn() -> T + 'static, { - start_rust_logic_real(main_future) + start_rust_logic_real(main_fn) } /// Send a signal to Dart. diff --git a/rust_crate/src/interface_os.rs b/rust_crate/src/interface_os.rs index 7b3015d9..48f6f563 100644 --- a/rust_crate/src/interface_os.rs +++ b/rust_crate/src/interface_os.rs @@ -1,14 +1,10 @@ use crate::error::RinfError; +use crate::shutdown::{create_shutdown_channel, SHUTDOWN_SENDER}; use allo_isolate::{IntoDart, Isolate, ZeroCopyBuffer}; use os_thread_local::ThreadLocal; use std::cell::RefCell; -use std::future::Future; -use std::pin::Pin; -use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::{Arc, Condvar, Mutex, OnceLock}; -use std::task::{Context, Poll, Waker}; +use std::sync::Mutex; use std::thread; -use tokio::runtime::Builder; static DART_ISOLATE: Mutex> = Mutex::new(None); @@ -26,20 +22,9 @@ pub extern "C" fn prepare_isolate_extern(port: i64) { guard.replace(dart_isolate); } -// We use `os_thread_local` so that when the program fails -// and the main thread exits unexpectedly, -// the whole async tokio runtime can shut down as well -// by receiving a signal via the shutdown channel. -// Without this solution, -// zombie threads inside the tokio runtime might outlive the app. -// This `ThreadLocal` is intended to be used only on the main thread. -type ShutdownSenderLock = OnceLock>>>; -static SHUTDOWN_SENDER: ShutdownSenderLock = OnceLock::new(); - -pub fn start_rust_logic_real(main_future: F) -> Result<(), RinfError> +pub fn start_rust_logic_real(main_fn: F) -> Result<(), RinfError> where - F: Future + Send + 'static, - T: Send + 'static, + F: Fn() -> T + Send + 'static, { // Enable backtrace output for panics. #[cfg(debug_assertions)] @@ -59,63 +44,16 @@ where } } - // Prepare the channel that will notify tokio runtime to shutdown + // Prepare the channel that will help notify async runtime to shutdown // after the main Dart thread has gone. - let (shutdown_sender, shutdown_receiver, shutdown_reporter) = shutdown_channel(); - let sender_lock = SHUTDOWN_SENDER.get_or_init(move || ThreadLocal::new(|| RefCell::new(None))); - sender_lock.with(|cell| cell.replace(Some(shutdown_sender))); + let shutdown_reporter = create_shutdown_channel()?; - // Build the tokio runtime. - #[cfg(not(feature = "rt-multi-thread"))] - { - let tokio_runtime = Builder::new_current_thread() - .enable_all() - .build() - .map_err(|_| RinfError::BuildRuntime)?; - thread::spawn(move || { - tokio_runtime.spawn(main_future); - tokio_runtime.block_on(shutdown_receiver); - // Dropping the tokio runtime makes it shut down. - drop(tokio_runtime); - // After dropping the runtime, tell the main thread to stop waiting. - drop(shutdown_reporter); - }); - } - #[cfg(feature = "rt-multi-thread")] - { - static TOKIO_RUNTIME: Mutex> = Mutex::new(None); - let tokio_runtime = Builder::new_multi_thread() - .enable_all() - .build() - .map_err(|_| RinfError::BuildRuntime)?; - tokio_runtime.spawn(async { - main_future.await; - }); - tokio_runtime.spawn(async { - shutdown_receiver.await; - thread::spawn(|| { - if let Ok(mut guard) = TOKIO_RUNTIME.lock() { - let runtime_option = guard.take(); - if let Some(runtime) = runtime_option { - // Dropping the tokio runtime makes it shut down. - drop(runtime); - } - // After dropping the runtime, tell the main thread to stop waiting. - drop(shutdown_reporter); - } - }) - }); - if let Ok(mut guard) = TOKIO_RUNTIME.lock() { - // If there was already a tokio runtime previously, - // most likely due to Dart's hot restart, - // its tasks as well as itself will be terminated, - // being replaced with the new one. - let runtime_option = guard.replace(tokio_runtime); - if let Some(previous_runtime) = runtime_option { - drop(previous_runtime); - } - } - } + // Run the async runtime. + thread::spawn(move || { + main_fn(); + // After the runtime is closed, tell the main thread to stop waiting. + drop(shutdown_reporter); + }); Ok(()) } @@ -125,9 +63,9 @@ pub extern "C" fn stop_rust_logic_extern() { let sender_lock = SHUTDOWN_SENDER.get_or_init(move || ThreadLocal::new(|| RefCell::new(None))); let sender_option = sender_lock.with(|cell| cell.take()); if let Some(shutdown_sender) = sender_option { - // Dropping the sender tells the tokio runtime to stop running. + // Dropping the sender tells the async runtime to stop running. // Also, it blocks the main thread until - // it gets the report that tokio shutdown is dropped. + // it gets the report that async runtime is dropped. drop(shutdown_sender); } } @@ -169,86 +107,3 @@ pub fn send_rust_signal_real( Ok(()) } - -struct ShutdownSender { - should_shutdown: Arc, - waker: Arc>>, - did_shutdown: Arc>, - is_done: Arc, -} - -impl Drop for ShutdownSender { - fn drop(&mut self) { - self.should_shutdown.store(true, Ordering::SeqCst); - if let Ok(mut guard) = self.waker.lock() { - if let Some(waker) = guard.take() { - waker.wake(); - } - } - while let Ok(guard) = self.did_shutdown.lock() { - if *guard { - break; - } else { - let _unused = self.is_done.wait(guard); - } - } - } -} - -struct ShutdownReceiver { - should_shutdown: Arc, - waker: Arc>>, -} - -impl Future for ShutdownReceiver { - type Output = (); - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - if !self.should_shutdown.load(Ordering::SeqCst) { - if let Ok(mut guard) = self.waker.lock() { - guard.replace(cx.waker().clone()); - } - Poll::Pending - } else { - Poll::Ready(()) - } - } -} - -type ChannelTuple = (ShutdownSender, ShutdownReceiver, ShutdownReporter); -fn shutdown_channel() -> ChannelTuple { - let should_shutdown = Arc::new(AtomicBool::new(false)); - let waker = Arc::new(Mutex::new(None)); - let did_shutdown = Arc::new(Mutex::new(false)); - let is_done = Arc::new(Condvar::new()); - - let sender = ShutdownSender { - should_shutdown: should_shutdown.clone(), - waker: waker.clone(), - did_shutdown: did_shutdown.clone(), - is_done: is_done.clone(), - }; - let receiver = ShutdownReceiver { - should_shutdown, - waker, - }; - let reporter = ShutdownReporter { - did_shutdown, - is_done, - }; - - (sender, receiver, reporter) -} - -struct ShutdownReporter { - did_shutdown: Arc>, - is_done: Arc, -} - -impl Drop for ShutdownReporter { - fn drop(&mut self) { - if let Ok(mut guard) = self.did_shutdown.lock() { - *guard = true; - } - self.is_done.notify_all(); - } -} diff --git a/rust_crate/src/interface_web.rs b/rust_crate/src/interface_web.rs index 241e37a3..af29fc5c 100644 --- a/rust_crate/src/interface_web.rs +++ b/rust_crate/src/interface_web.rs @@ -1,12 +1,11 @@ use crate::error::RinfError; +use crate::shutdown::create_shutdown_channel; use js_sys::Uint8Array; -use std::future::Future; use wasm_bindgen::prelude::*; -use wasm_bindgen_futures::spawn_local; -pub fn start_rust_logic_real(main_future: F) -> Result<(), RinfError> +pub fn start_rust_logic_real(main_fn: F) -> Result<(), RinfError> where - F: Future + 'static, + F: Fn() -> T + 'static, { // Add kind description for panics. #[cfg(debug_assertions)] @@ -16,10 +15,11 @@ where })); } + // Prepare the channel to match the behavior of native platforms. + let _ = create_shutdown_channel(); + // Run the main function. - spawn_local(async { - main_future.await; - }); + main_fn(); Ok(()) } diff --git a/rust_crate/src/lib.rs b/rust_crate/src/lib.rs index 92319676..345fb2d7 100644 --- a/rust_crate/src/lib.rs +++ b/rust_crate/src/lib.rs @@ -1,6 +1,7 @@ mod channel; mod error; mod macros; +mod shutdown; mod interface; #[cfg(not(target_family = "wasm"))] @@ -8,6 +9,7 @@ mod interface_os; #[cfg(target_family = "wasm")] mod interface_web; -pub use channel::*; -pub use error::*; -pub use interface::*; +pub use channel::{message_channel, MessageReceiver, MessageSender}; +pub use error::RinfError; +pub use interface::{send_rust_signal, start_rust_logic, DartSignal}; +pub use shutdown::get_shutdown_receiver; diff --git a/rust_crate/src/macros.rs b/rust_crate/src/macros.rs index d9f690e5..e30b6bae 100644 --- a/rust_crate/src/macros.rs +++ b/rust_crate/src/macros.rs @@ -8,7 +8,7 @@ macro_rules! write_interface { #[cfg(not(target_family = "wasm"))] #[no_mangle] pub extern "C" fn start_rust_logic_extern() { - let result = $crate::start_rust_logic(main()); + let result = $crate::start_rust_logic(main); if let Err(error) = result { rinf::debug_print!("{error}"); } @@ -17,7 +17,7 @@ macro_rules! write_interface { #[cfg(target_family = "wasm")] #[wasm_bindgen::prelude::wasm_bindgen] pub fn start_rust_logic_extern() { - let result = $crate::start_rust_logic(main()); + let result = $crate::start_rust_logic(main); if let Err(error) = result { rinf::debug_print!("{error}"); } diff --git a/rust_crate/src/shutdown.rs b/rust_crate/src/shutdown.rs new file mode 100644 index 00000000..001822a7 --- /dev/null +++ b/rust_crate/src/shutdown.rs @@ -0,0 +1,143 @@ +use crate::error::RinfError; +use os_thread_local::ThreadLocal; +use std::cell::RefCell; +use std::future::Future; +use std::pin::Pin; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::{Arc, Condvar, Mutex, OnceLock}; +use std::task::{Context, Poll, Waker}; + +// We use `os_thread_local` so that when the program fails +// and the main thread exits unexpectedly, +// the whole async runtime can shut down as well +// by receiving a signal via the shutdown channel. +// Without this solution, +// zombie threads inside the async runtime might outlive the app. +// This `ThreadLocal` is intended to be used only on the main thread. +type ShutdownSenderLock = OnceLock>>>; +pub static SHUTDOWN_SENDER: ShutdownSenderLock = OnceLock::new(); + +type ShutdownReceiverLock = Mutex>; +pub static SHUTDOWN_RECEIVER: ShutdownReceiverLock = Mutex::new(None); + +pub fn get_shutdown_receiver() -> Result { + let mut reciver_lock = SHUTDOWN_RECEIVER + .lock() + .map_err(|_| RinfError::LockShutdownReceiver)?; + reciver_lock.take().ok_or(RinfError::NoShutdownReceiver) +} + +pub fn create_shutdown_channel() -> Result { + let (shutdown_sender, shutdown_receiver, shutdown_reporter) = shutdown_channel(); + + let sender_lock = SHUTDOWN_SENDER.get_or_init(move || ThreadLocal::new(|| RefCell::new(None))); + sender_lock.with(|cell| cell.replace(Some(shutdown_sender))); + + let mut reciver_lock = SHUTDOWN_RECEIVER + .lock() + .map_err(|_| RinfError::LockShutdownReceiver)?; + reciver_lock.replace(shutdown_receiver); + + Ok(shutdown_reporter) +} + +type ChannelTuple = (ShutdownSender, ShutdownReceiver, ShutdownReporter); +fn shutdown_channel() -> ChannelTuple { + let should_shutdown = Arc::new(AtomicBool::new(false)); + let waker = Arc::new(Mutex::new(None)); + let did_shutdown = Arc::new(Mutex::new(false)); + let is_done = Arc::new(Condvar::new()); + + let sender = ShutdownSender { + should_shutdown: should_shutdown.clone(), + waker: waker.clone(), + did_shutdown: did_shutdown.clone(), + is_done: is_done.clone(), + }; + let receiver = ShutdownReceiver { + should_shutdown, + waker, + }; + let reporter = ShutdownReporter { + did_shutdown, + is_done, + }; + + (sender, receiver, reporter) +} + +pub struct ShutdownSender { + should_shutdown: Arc, + waker: Arc>>, + did_shutdown: Arc>, + is_done: Arc, +} + +impl Drop for ShutdownSender { + fn drop(&mut self) { + self.should_shutdown.store(true, Ordering::SeqCst); + if let Ok(mut guard) = self.waker.lock() { + if let Some(waker) = guard.take() { + waker.wake(); + } + } + while let Ok(guard) = self.did_shutdown.lock() { + if *guard { + break; + } else { + let _unused = self.is_done.wait(guard); + } + } + } +} + +pub struct ShutdownReceiver { + should_shutdown: Arc, + waker: Arc>>, +} + +impl Future for ShutdownReceiver { + type Output = (); + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + if !self.should_shutdown.load(Ordering::SeqCst) { + if let Ok(mut guard) = self.waker.lock() { + guard.replace(cx.waker().clone()); + } + Poll::Pending + } else { + Poll::Ready(()) + } + } +} + +pub struct ShutdownReporter { + did_shutdown: Arc>, + is_done: Arc, +} + +impl Drop for ShutdownReporter { + fn drop(&mut self) { + if let Ok(mut guard) = self.did_shutdown.lock() { + *guard = true; + } + self.is_done.notify_all(); + } +} + +// `os_thread_local` is only available on native platforms, +// Let's simply mimic `ThreadLocal` on the web. +#[cfg(target_family = "wasm")] +mod os_thread_local { + pub struct ThreadLocal { + inner: T, + } + unsafe impl Sync for ThreadLocal {} + impl ThreadLocal { + pub fn new T>(inner: F) -> ThreadLocal { + ThreadLocal { inner: inner() } + } + pub fn with R>(&self, f: F) { + f(&self.inner); + } + } +} From 199da242f35348f3ab524f9ebbca47014eebfdb0 Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Tue, 10 Sep 2024 00:13:33 +0900 Subject: [PATCH 2/5] remove an unneeded dependency --- rust_crate/Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/rust_crate/Cargo.toml b/rust_crate/Cargo.toml index 0e48b650..de6b9fec 100644 --- a/rust_crate/Cargo.toml +++ b/rust_crate/Cargo.toml @@ -22,7 +22,6 @@ protoc-prebuilt = "0.3.0" home = "0.5.9" which = "6.0.0" allo-isolate = "0.1.25" -tokio = { version = "1", features = ["rt"] } [target.'cfg(target_family = "wasm")'.dependencies] js-sys = "0.3.69" From 4525619201ef5d504a7fda4aec5940bdb40d0be4 Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Tue, 10 Sep 2024 00:16:14 +0900 Subject: [PATCH 3/5] add a doc comment --- rust_crate/src/shutdown.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/rust_crate/src/shutdown.rs b/rust_crate/src/shutdown.rs index 001822a7..7d6bad05 100644 --- a/rust_crate/src/shutdown.rs +++ b/rust_crate/src/shutdown.rs @@ -20,6 +20,11 @@ pub static SHUTDOWN_SENDER: ShutdownSenderLock = OnceLock::new(); type ShutdownReceiverLock = Mutex>; pub static SHUTDOWN_RECEIVER: ShutdownReceiverLock = Mutex::new(None); +/// Retrieves the shutdown receiver that listens for +/// the Dart runtime's closure. +/// Awaiting this receiver in the async main Rust function +/// is necessary to prevent the async runtime in Rust from +/// finishing immediately. pub fn get_shutdown_receiver() -> Result { let mut reciver_lock = SHUTDOWN_RECEIVER .lock() From 6f8b5a1f2f3880ccda14a4651b1174ce9ddad75b Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Tue, 10 Sep 2024 00:20:15 +0900 Subject: [PATCH 4/5] update template code --- flutter_ffi_plugin/template/native/hub/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flutter_ffi_plugin/template/native/hub/src/lib.rs b/flutter_ffi_plugin/template/native/hub/src/lib.rs index 2c386fbc..69e56e97 100644 --- a/flutter_ffi_plugin/template/native/hub/src/lib.rs +++ b/flutter_ffi_plugin/template/native/hub/src/lib.rs @@ -16,7 +16,7 @@ rinf::write_interface!(); // If you really need to use blocking code, // use `tokio::task::spawn_blocking`. #[tokio::main(flavor = "current_thread")] -async fn main() { +async fn main() -> Result<()> { // Spawn the concurrent tasks. tokio::spawn(communicate()); From 42f0a8b6dc657ff2b4c280fc3d20b2defade9011 Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Tue, 10 Sep 2024 00:40:39 +0900 Subject: [PATCH 5/5] add necessary features --- flutter_ffi_plugin/template/native/hub/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flutter_ffi_plugin/template/native/hub/Cargo.toml b/flutter_ffi_plugin/template/native/hub/Cargo.toml index 3cd67d70..7a024794 100644 --- a/flutter_ffi_plugin/template/native/hub/Cargo.toml +++ b/flutter_ffi_plugin/template/native/hub/Cargo.toml @@ -14,8 +14,8 @@ crate-type = ["lib", "cdylib", "staticlib"] [dependencies] rinf = "6.15.0" prost = "0.12.6" -tokio = { version = "1", features = ["rt"] } +tokio = { version = "1", features = ["rt", "macros"] } # Uncomment below to target the web. -# tokio_with_wasm = { version = "0.6.0", features = ["rt"] } +# tokio_with_wasm = { version = "0.6.0", features = ["rt", "macros"] } # wasm-bindgen = "0.2.92"