fzyzcjy / flutter_rust_bridge

Flutter/Dart <-> Rust binding generator, feature-rich, but seamless and simple.
https://fzyzcjy.github.io/flutter_rust_bridge/
MIT License
4.29k stars 301 forks source link

Dart Callback cannot be shared between threads #2399

Closed Persite54 closed 2 weeks ago

Persite54 commented 2 weeks ago

Describe the bug

It might be more of a limitation or misunderstanding than a bug.

I’m currently facing my first blocking issue while using the very exiting frb .

I'm trying to take advantage of callbacks that allow you to execute a Dart function from Rust. This functionality is described here.

My Rust function takes a dart_callback as a parameter and attempts to pass it to another function that will likely be executed on a different thread. To clarify, my function looks like this :

pub async fn subscribe_to_values(dart_callback: impl Fn(String) -> DartFnFuture<String>,session: Arc<RwLock<Session>>) -> Result<(Arc<RwLock<Session>>,String), StatusCode> {
    let session_lock = session.write();

    let subscription_id = session_lock.create_subscription(2000.0, 10, 30, 0, 0, true, DataChangeCallback::new(
    |changed_monitored_items| {
            println!("Data change from server:");
            dart_callback("Tom".to_owned()); //<------ THIS IS THE FAULTY LINE
            changed_monitored_items.iter().for_each(|item: &&MonitoredItem| print_value(item));
        }))?;

    Ok((session.clone(),"".to_string()))
}

However, i got the following error : impl Fn(String) -> DartFnFuture<String>` cannot be shared between threads safely required for `&impl Fn(String) -> DartFnFuture<String>` to implement `std::marker::Send

I tried using Arc at the beginning of the function :

let dart_callback = Arc::new(dart_callback);

But it didn’t work.

Is what I'm trying to do impossible, or am I simply going about it the wrong way? Is there a workaround or a trick I can use to achieve my goal ?

Steps to reproduce

I’m unable to provide a minimal example to reproduce the issue because I don’t encounter this specific error with the following simple example. I'm not sure why, as it also executes the callback in another thread.

pub async fn subscribe_to_values(dart_callback: impl Fn(String) -> DartFnFuture<String>) {

    let dart_callback = Arc::new(dart_callback);

    task::spawn( {
        dart_callback("Hello from Rust!".to_owned())

    });

}

Logs

`impl Fn(String) -> DartFnFuture<String>` cannot be shared between threads safely
required for `&impl Fn(String) -> DartFnFuture<String>` to implement `std::marker::Send`

Expected behavior

No response

Generated binding code

No response

OS

No response

Version of flutter_rust_bridge_codegen

No response

Flutter info

No response

Version of clang++

No response

Additional context

No response

welcome[bot] commented 2 weeks ago

Hi! Thanks for opening your first issue here! :smile:

Persite54 commented 2 weeks ago

Finally fix my code.

Change

pub async fn subscribe_to_values(dart_callback: impl Fn(String) -> DartFnFuture<String>,session: Arc<RwLock<Session>>) -> Result<(Arc<RwLock<Session>>,String), StatusCode>

Into

pub async fn subscribe_to_values(dart_callback: impl Fn(String) -> DartFnFuture<String> + Send + Sync + 'static,session: Arc<RwLock<Session>>) -> Result<(Arc<RwLock<Session>>,String), StatusCode>

It makes sense

A related question: Is it feasible for frb to support synchronous execution of callbacks in addition to asynchronous execution ? Maybe it's risky since Dart executes everything on the same thread and could be blocked by other tasks ?

fzyzcjy commented 2 weeks ago

Yes, need to +Send+Sync etc, which seems to be a restriction of Rust. Feel free to PR to mention it in https://cjycode.com/flutter_rust_bridge/guides/direction/rust-call-dart!

Is it feasible for frb to support synchronous execution of callbacks in addition to asynchronous execution ? Maybe it's risky since Dart executes everything on the same thread and could be blocked by other tasks ?

Yes, that's kind of risky because of threading. Could you please share your scenarios? Then maybe we can know a bit more whether we should use sync or async callbacks.

github-actions[bot] commented 6 hours ago

This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new issue.