#Async task using `Mut<Resource>`

14 messages · Page 1 of 1 (latest)

odd hull
#

I have a resource ResMut<ClientConnection> and I would like to start an async task on it, like so:

fn connect(mut netclient: ResMut<ClientConnection>,) {
    info!("calling connect on netclient");
    let task_handle = IoTaskPool::get().spawn(Compat::new(async { netclient.connect().await }));

    // do other stuff with the task handle
}

The issue is that I can't pass the &mut Res into the task; I get an error like

407 | fn connect(mut netclient: ResMut<ClientConnection>, mut state: ResMut<ConnectingState>) {
    |            -------------
    |            |
    |            `netclient` is a reference that is only valid in the function body
    |            has type `bevy::prelude::ResMut<'1, connection::client::ClientConnection>`
408 |     info!("calling connect on netclient");
409 |     let connection_task = IoTaskPool::get().spawn(Compat::new(async { netclient.connect().await }));
    |                                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |                                                   |
    |                                                   `netclient` escapes the function body here
    |                                                   argument requires that `'1` must outlive `'static`
    |
    = note: requirement occurs because of a mutable reference to `bevy::prelude::ResMut<'_, connection::client::ClientConnection>`
    = note: mutable references are invariant over their type parameter
    = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance

I can't use async move either because I don't own the resource.

I guess a solution would be to remove the resource from the world so that I own it, launch the task, and then maybe re-add the resource to the world after the task ends, but that isn't very ergonomic.

Is there a good way to do this?

hexed forge
#

Alternatively you can put some internal state behind an Arc and clone the resource into the task like AssetServer

untold surge
#

You can take a page out of bevy_defer's book and spam channels for 2 way communication

#

you can try some actor pattern shenanigans on the async side

odd hull
odd hull
untold surge
#

It's hard to explain like this but in general a good async pattern is not to pass state around but to communicate

#

this video might be good inspiration (although it's 3 hours long 😅 )
https://www.youtube.com/watch?v=o2ob8zkeq2s

In this stream, we peeled back the crust on the tokio crate — https://github.com/tokio-rs/tokio/ — and explored its interface, structure, and mechanisms. We talked about blocking, cancellation, spawning, and mechanisms for synchronization. We also dug into some of what goes on under the hood where that ends up being relevant to you as an applica...

▶ Play video
odd hull
untold surge
#

yeah that can work

odd hull
#

Would something like your AsyncResource in bevy_defer be helpful for my problem?

untold surge
#

I'm not sure, but be skeptical since bevy defer is not designed for networking and may block the main thread

#

but if you push the resource to thread locals you can get immediate access

odd hull