Rust "future cannot be sent between threads safely"
Asked Answered
B

2

20

I'm invoking an async implemented method:

let mut safebrowsing: MutexGuard<Safebrowsing> = self.safebrowsing.lock().unwrap();
safebrowsing.is_safe(input: &message.content).await;

The is_safe-Method:

pub async fn is_safe(&mut self, input: &str) {
    let links = self.finder.links(input);

    for link in links {
        match reqwest::get("url").await {
            Ok(response) => {
                println!(
                    "{}",
                    response.text().await.expect("response has no content")
                );
            }
            Err(_) => {
                println!("Unable to get safebrowsing-response")
            }
        }
    }
}

But unfortunately by invoking the is_safe-Method asynchronously, the compiler tells me that threads cannot be sent safely. The error is about:

future cannot be sent between threads safely
within `impl std::future::Future`, the trait `std::marker::Send` is not implemented for `std::sync::MutexGuard<'_, Safebrowsing>`
required for the cast to the object type `dyn std::future::Future<Output = ()> + std::marker::Send`

handler.rs(31, 9): future is not `Send` as this value is used across an await
           ^-- safebrowsing.is_safe(input: &message.content).await;
---

future cannot be sent between threads safely
the trait `std::marker::Send` is not implemented for `(dyn for<'r> Fn(&'r [u8]) -> Option<usize> + 'static)`
required for the cast to the object type `dyn std::future::Future<Output = ()> + std::marker::Send`

safebrowsing.rs(22, 19): future is not `Send` as this value is used across an await
               ^-- match reqwest::get("url").await

I already tried to implement the Send-Trait to my Safebrowsing-Struct, but that does also not work. Is there something I need to do to get it working? Because I have no clue why that is appearing

Biretta answered 29/8, 2021 at 21:10 Comment(0)
S
15

The key of this error is that MutexGuard<T> is not Send. This means that you are trying to do an await while the mutex is locked, and that is usually a bad idea, if you think about it: await may wait, in principle, indefinitely, but by waiting so long with the mutex held, any other thread that tries to lock the mutex will block, also indefinitely (unless you set a timeout, of course).

So, as a rule of thumb, you should never sleep with a mutex locked. For example your code could be rewritten as (totally untested):

pub async fn is_safe(this: &Mutex<Safebrowsing>, input: &str) {
    //lock, find, unlock
    let links = this.lock().unwrap().finder.links(input);
    //now we can await safely
    for link in links {
        match reqwest::get("url").await {
            Ok(response) => {
                println!(
                    "{}",
                    response.text().await.expect("response has no content")
                );
            }
            Err(_) => {
                println!("Unable to get safebrowsing-response")
            }
        }
    }
}

If you need to lock the Mutex later in the function, beware of the races! It may have been modified by other thread, maybe that input is no longer a thing.

Scrambler answered 29/8, 2021 at 21:27 Comment(0)
T
28

Use Mutex implementation from the async runtime you're using.

Before 😭

Using mutex from standard library:

use std::sync::Mutex;  // stdlib

let m = Mutex::new(...);

let v = m.lock().unwrap();

After 😁

Using mutex from tokio:

use tokio::sync::Mutex;  // tokio async runtime

let m = Mutex::new(...);  // the same!

let v = m.lock().await;

But why?

Roughly speaking, the native mutex forces the lock to be kept in the same thread, but async runtime does not understand it.

If your lock does not cross with an async, then you can use mutex from stdlib (it can be faster).

See the discussion from tokio documentation.

Tachymetry answered 20/12, 2022 at 18:45 Comment(2)
This was the solution for me too.Derwon
Worked for me as well. The fun thing is that when I did not return anything from the function (only a Result<(),MyError>), it did not complain about it being a std:.sync::Mutex. Weird indeed.Moussaka
S
15

The key of this error is that MutexGuard<T> is not Send. This means that you are trying to do an await while the mutex is locked, and that is usually a bad idea, if you think about it: await may wait, in principle, indefinitely, but by waiting so long with the mutex held, any other thread that tries to lock the mutex will block, also indefinitely (unless you set a timeout, of course).

So, as a rule of thumb, you should never sleep with a mutex locked. For example your code could be rewritten as (totally untested):

pub async fn is_safe(this: &Mutex<Safebrowsing>, input: &str) {
    //lock, find, unlock
    let links = this.lock().unwrap().finder.links(input);
    //now we can await safely
    for link in links {
        match reqwest::get("url").await {
            Ok(response) => {
                println!(
                    "{}",
                    response.text().await.expect("response has no content")
                );
            }
            Err(_) => {
                println!("Unable to get safebrowsing-response")
            }
        }
    }
}

If you need to lock the Mutex later in the function, beware of the races! It may have been modified by other thread, maybe that input is no longer a thing.

Scrambler answered 29/8, 2021 at 21:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.