I reached a strange error while using recursion inside an async.
I can use client
inside the async
. However, if I call do_something
again it complains about std::sync::MutexGuard<'_, Client>
not being send. I think it's trying to send c
to another thread. I thought do_something
executed in a thread, and do_something(client.clone()).await
in another. I don't see why c
is moving from the async
thread to this new thread.
use std::sync::{Arc, Mutex};
use std::future::Future;
use futures::future::{BoxFuture, FutureExt};
struct Client{
}
impl Client {}
fn do_something<'a>(
client: Arc<Mutex<Client>>,
) -> BoxFuture<'a, std::result::Result<(), ()>> {
async move {
let c = client.lock().unwrap();
do_something(client.clone()).await;
Ok(())
}.boxed()
}
fn main() {
let c = Arc::new(Mutex::new(Client{}));
do_something(c.clone());
}
Error:
error: future cannot be sent between threads safely
--> src/main.rs:17:7
|
17 | }.boxed()
| ^^^^^ future created by async block is not `Send`
|
= help: within `impl futures::Future`, the trait `std::marker::Send` is not implemented for `std::sync::MutexGuard<'_, Client>`
note: future is not `Send` as this value is used across an await
--> src/main.rs:15:9
|
14 | let c = client.lock().unwrap();
| - has type `std::sync::MutexGuard<'_, Client>` which is not `Send`
15 | do_something(client.clone()).await;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ await occurs here, with `c` maybe used later
16 | Ok(())
17 | }.boxed()
| - `c` is later dropped here
Playground
Traits with async methods can be used as trait objects as long as they meet the usual requirements for dyn – no methods with type parameters, no self by value, no associated types, etc. The one wrinkle is in traits that provide default implementations of async methods.
This trait is automatically implemented when the compiler determines it’s appropriate. The precise definition is: a type T is Sync if and only if &T is Send. In other words, if there is no possibility of undefined behavior (including data races) when passing &T references between threads.
Not all async traits need futures that are dyn Future + Send. To avoid having Send and Sync bounds placed on the async trait methods, invoke the async trait macro as # [async_trait (?Send)] on both the trait and the impl blocks. Be aware that async fn syntax does not allow lifetime elision outside of & and &mut references.
Locking the mutex will block the thread, which is generally something you want to avoid in async code as it can prevent other tasks from running. As seen in the compiler error, the mutex guard that you get by locking it can't be shared between threads safely, so it won't compile anyways.
There's two problems here, both stemming from the fact that the standard library Mutex
is designed to be used with regular blocking code, rather than async code:
Both of these issues can be fixed by using a Mutex
designed for async code. Since you're already using something from the futures
crate, you can just use futures::lock::Mutex
, changing client.lock().unwrap()
to client.lock().await
. This version of the lock will yield control back to the task executor when it needs to wait rather than blocking the thread, and will also allow it to be shared between threads if necessary.
The complete version of the code looks like this - however, it's not very useful because you're never actually executing the future returned by do_something
(note that you're not doing anything with the returned future) and even if you did, it would immediately deadlock due to the recursion:
use std::sync::Arc;
use std::future::Future;
use futures::future::{BoxFuture, FutureExt};
use futures::lock::Mutex;
struct Client {}
impl Client {}
fn do_something<'a>(
client: Arc<Mutex<Client>>,
) -> BoxFuture<'a, std::result::Result<(), ()>> {
async move {
let c = client.lock().await;
do_something(client.clone()).await;
Ok(())
}.boxed()
}
fn main() {
let c = Arc::new(Mutex::new(Client {}));
do_something(c.clone());
}
Playground
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With