Holding a MutexGuard across an .await

    When you try to spawn something that calls this function, you will encounter the following error message:

    1. error: future cannot be sent between threads safely
    2. --> src/lib.rs:13:5
    3. |
    4. 13 | tokio::spawn(async move {
    5. | ^^^^^^^^^^^^ future created by async block is not `Send`
    6. |
    7. ::: /playground/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.2.21/src/task/spawn.rs:127:21
    8. |
    9. 127 | T: Future + Send + 'static,
    10. | ---- required by this bound in `tokio::task::spawn::spawn`
    11. |
    12. note: future is not `Send` as this value is used across an await
    13. --> src/lib.rs:7:5
    14. 4 | let mut lock = mutex.lock().unwrap();
    15. | -------- has type `std::sync::MutexGuard<'_, i32>` which is not `Send`
    16. ...
    17. 7 | do_something_async().await;
    18. | ^^^^^^^^^^^^^^^^^^^^^^^^^^ await occurs here, with `mut lock` maybe used later
    19. 8 | }
    20. | - `mut lock` is later dropped here

    This happens because the std::sync::MutexGuard type is not Send. This means that you can’t send a mutex lock to another thread, and the error happens because the Tokio runtime can move a task between threads at every .await. To avoid this, you should restructure your code such that the mutex lock’s destructor runs before the .await.

    Note that this does not work:

    1. use std::sync::Mutex;
    2. // This fails too.
    3. async fn increment_and_do_stuff(mutex: &Mutex<i32>) {
    4. *lock += 1;
    5. drop(lock);
    6. do_something_async().await;
    7. }

    Note that the error discussed here is also discussed in the Send bound section from the spawning chapter.

    You should not try to circumvent this issue by spawning the task in a way that does not require it to be Send, because if Tokio suspends your task at an .await while the task is holding the lock, some other task may be scheduled to run on the same thread, and this other task may also try to lock that mutex, which would result in a deadlock as the task waiting to lock the mutex would prevent the task holding the mutex from releasing the mutex.

    We will discuss some approaches to fix the error message below:

    This pattern guarantees that you wont run into the Send error, because the mutex guard does not appear anywhere in an async function.

    This is the second approach mentioned in the start of this chapter, and is often used when the shared resource is an I/O resource. See the next chapter for more details.

    The type provided by Tokio can also be used. The primary feature of the Tokio mutex is that it can be held across an .await without any issues. That said, an asynchronous mutex is more expensive than an ordinary mutex, and it is typically better to use one of the two other approaches.

    1. use tokio::sync::Mutex; // note! This uses the Tokio mutex
    2. // This compiles!
    3. // (but restructuring the code would be better in this case)
    4. async fn increment_and_do_stuff(mutex: &Mutex<i32>) {
    5. let mut lock = mutex.lock().await;
    6. *lock += 1;
    7. do_something_async().await;