Search code examples
rustmutexrust-tokio

Is it possible to pass a locked mutex to a spawned task?


I have a struct that wraps some shared state

struct Demo(Arc<Mutex<State>>);

I would like to return a Result from a function that spawns a task that indicates if the state was locked or whether the tasked was spawned successfully. What I am trying to do so far

impl Demo {
    async fn start(&self) -> Result<(), Busy> {
        let local_state = self.0.clone();
        if let Ok(state) = local_state.try_lock() {
            spawn(async move {
                state.some_function().await
            });
            Ok(())
        } else {
            Err(Busy)
        }
    }
}

Which gives

error[E0597]: `local_state` does not live long enough
  --> src/lib.rs:19:27
   |
18 |         let local_state = self.0.clone();
   |             ----------- binding `local_state` declared here
19 |         if let Ok(state) = local_state.try_lock() {
   |                           ^^^^^^^^^^^^^^^^^^^^^^
   |                           |
   |                           borrowed value does not live long enough
   |                           argument requires that `local_state` is borrowed for `'static`
...
27 |     }
   |     - `local_state` dropped here while still borrowed

Which makes sense but I can't figure out how to fix it. Is there a way to allow a locked mutex to be passed to a spawned task? And if not, what would be the best approach to allow the busy state to be returned to the caller?

Full minimal example here


Solution

  • After reading through the tokio Mutex docs there is a try_lock_owned method that the std version doesn't have. This returns an OwnedMutexGuard that works for this purpose that is only available when the mutex is wrapped in an Arc.

    The working version

    impl Demo {
        async fn start(&self) -> Result<(), Busy> {
            let local_state = self.0.clone();
            if let Ok(state) = local_state.try_lock_owned() {
                spawn(async move {
                    state.some_function().await
                });
                Ok(())
            } else {
                Err(Busy)
            }
        }
    }