Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions frb_rust/src/for_generated/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,12 @@ pub use crate::handler::error_listener::ErrorListener;
pub use crate::handler::executor::Executor;
pub use crate::handler::handler::{FfiCallMode, TaskInfo, Thread};
pub use crate::handler::handler::{TaskContext, TaskRetFutTrait};
pub use crate::handler::implementation::thread_executor::{ThreadExecutor, ThreadJob, ThreadRouter};
pub use crate::handler::implementation::error_listener::NoOpErrorListener;
pub use crate::handler::implementation::executor::SimpleExecutor;
pub use crate::handler::implementation::handler::SimpleHandler;
pub use crate::handler::implementation::thread_executor::{
ThreadExecutor, ThreadJob, ThreadRouter,
};
pub use crate::lifetimeable::lifetime_changer::{
ouroboros_change_lifetime, ouroboros_change_lifetime_mut,
};
Expand Down Expand Up @@ -63,9 +65,9 @@ pub use crate::rust_auto_opaque::rust2dart_explicit::rust_auto_opaque_explicit_e
pub use crate::rust_auto_opaque::{inner::RustAutoOpaqueInner, RustAutoOpaqueBase};
pub use crate::rust_opaque::{dart2rust::decode_rust_opaque_nom, RustOpaqueBase};
pub use crate::stream::stream_sink::StreamSinkBase;
pub use crate::thread_pool::{BaseThreadPool, SimpleThreadPool};
#[cfg(all(feature = "rust-async", not(target_family = "wasm")))]
pub use crate::thread_pool::SingleThreadAsyncLane;
pub use crate::thread_pool::{BaseThreadPool, SimpleThreadPool};
#[cfg(target_family = "wasm")]
pub use crate::web_transfer::transfer_closure::TransferClosure;
#[cfg(feature = "anyhow")]
Expand Down
60 changes: 56 additions & 4 deletions frb_rust/src/handler/implementation/thread_executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,7 @@ pub trait ThreadRouter {
#[cfg(not(target_family = "wasm"))]
pub type ThreadJob = Box<dyn FnOnce() + Send + 'static>;
#[cfg(target_family = "wasm")]
pub type ThreadJob =
crate::web_transfer::transfer_closure::TransferClosure<wasm_bindgen::JsValue>;
pub type ThreadJob = crate::web_transfer::transfer_closure::TransferClosure<wasm_bindgen::JsValue>;

/// Build a [`ThreadJob`] from a `transfer!`-style closure. On native it boxes
/// the closure into `Box<dyn FnOnce() + Send>`; on web `transfer!` already
Expand All @@ -73,7 +72,9 @@ macro_rules! thread_job {

/// An [`Executor`] that routes calls to per-thread lanes.
///
/// - `execute_normal` / `execute_async`: build the same closure
/// - wasm `Thread::Main` normal/async calls run inline on the caller so app
/// startup does not prewarm a general worker pool for main-thread-safe work.
/// - other `execute_normal` / `execute_async` calls build the same closure
/// [`SimpleExecutor`](super::executor::SimpleExecutor) builds, then route it
/// to the `thread`'s lane via [`ThreadRouter::execute_on`].
/// - `execute_sync`: run inline on the caller (main thread), exactly like
Expand Down Expand Up @@ -112,6 +113,26 @@ impl<EL: ErrorListener + Sync, P: ThreadRouter> Executor for ThreadExecutor<EL,
let TaskInfo { port, .. } = task_info;
let port: MessagePort = port.unwrap();

#[cfg(target_family = "wasm")]
if thread == Thread::Main {
#[allow(clippy::clone_on_copy)]
let port2 = port.clone();
let thread_result = PanicBacktrace::catch_unwind(AssertUnwindSafe(|| {
#[allow(clippy::clone_on_copy)]
let sender = Rust2DartSender::new(Channel::new(port2.clone()));
let task_context = TaskContext::new();

let ret = task(task_context);

ThreadExecuteUtils::handle_result::<Rust2DartCodec, _>(ret, sender, el2);
}));

if let Err(error) = thread_result {
handle_non_sync_panic_error::<Rust2DartCodec>(el, port, error);
}
return;
}

// Identical body to SimpleExecutor::execute_normal (executor.rs), only
// the destination differs: route to the thread lane instead of "any
// idle worker".
Expand Down Expand Up @@ -173,6 +194,32 @@ impl<EL: ErrorListener + Sync, P: ThreadRouter> Executor for ThreadExecutor<EL,
let TaskInfo { port, .. } = task_info;
let port: MessagePort = port.unwrap();

#[cfg(target_family = "wasm")]
if thread == Thread::Main {
spawn_local_compat(async move {
#[allow(clippy::clone_on_copy)]
let port2 = port.clone();

let async_result = AssertUnwindSafe(async {
#[allow(clippy::clone_on_copy)]
let sender = Rust2DartSender::new(Channel::new(port2.clone()));
let task_context = TaskContext::new();

let ret = task(task_context).await;

ThreadExecuteUtils::handle_result::<Rust2DartCodec, _>(ret, sender, el2);
})
.catch_unwind()
.await;

if let Err(err) = async_result {
let err = CatchUnwindWithBacktrace::new(err, PanicBacktrace::take_last());
handle_non_sync_panic_error::<Rust2DartCodec>(el, port, err);
}
});
return;
}

// Off-main async: transfer the `Send` closure (capturing the
// transferable `MessagePort`, exactly like the normal branch) to the
// thread lane; the lane worker drives the (possibly non-`Send`)
Expand Down Expand Up @@ -262,7 +309,12 @@ impl ThreadExecuteUtils {
// This routes two async tasks to one single-worker lane with a cross-task
// dependency: cooperative scheduling completes both; a parking driver would
// deadlock (A holds the worker awaiting B, which can never start).
#[cfg(all(test, feature = "rust-async", feature = "thread-pool", not(target_family = "wasm")))]
#[cfg(all(
test,
feature = "rust-async",
feature = "thread-pool",
not(target_family = "wasm")
))]
mod single_worker_lane_pinning_tests {
use super::*;
use crate::codec::dco::DcoCodec;
Expand Down
2 changes: 1 addition & 1 deletion frb_rust/src/handler/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ pub use error_listener::ErrorListener;
pub use executor::Executor;
pub use handler::{FfiCallMode, TaskInfo, Thread};
pub use handler::{TaskContext, TaskRetFutTrait};
pub use implementation::thread_executor::{ThreadExecutor, ThreadJob, ThreadRouter};
pub use implementation::error_listener::NoOpErrorListener;
pub use implementation::executor::SimpleExecutor;
pub use implementation::handler::SimpleHandler;
pub use implementation::thread_executor::{ThreadExecutor, ThreadJob, ThreadRouter};
Loading
Loading