I'm trying to turn a blocking function into an async function. The sync
function on Repo
takes &mut self
, and blocks for a while, so I'd like to be able to use await
on it.
The Repo
is a member of RepoManager
, which acts as an async interface to the Repo
struct with a lot of additional functionality on top.
#[derive(Debug)]
struct RepoManager {
repo: Mutex<Repo>,
status: RwLock<ManagerStatus>,
path: PathBuf,
}
impl RepoManager {
async fn resync(&self) -> Result<(), SyncError> {
let new_paths = vec!["a"]; // example paths
let mut repo = self.repo.lock().await;
tokio::task::spawn_blocking(move || repo.sync(new_paths))
.await
.expect("failed to join with thread that's batch-updating the database")?;
Ok(())
}
}
My hope is to move the execution of sync()
to a separate thread, so the current thread can keep working on something else. So I created a blocking task using tokio
and ran sync()
there, then called .await
on it to wait for its execution to stop.
However, when I try compiling it, I get an error:
error[E0521]: borrowed data escapes outside of associated function
--> src-tauri\src\manager.rs:54:40
|
45 | async fn resync(&self) -> Result<(), SyncError> {
| -----
| |
| `self` is a reference that is only valid in the associated function body
| let's call the lifetime of this reference `'1`
...
54 | let mut repo = self.repo.lock().await;
| ^^^^^^
| |
| `self` escapes the associated function body here
| argument requires that `'1` must outlive `'static`
I'm not sure I understand why it's saying the data outlives the function. Since I'm calling .await
on the thread, the current function won't progress or end until the thread terminates.
How can I modify this to move the execution of sync()
to a separate thread?
Other relevant structs and definitions:
#[derive(Debug)]
pub struct Repo {
path: PathBuf,
conn: rusqlite::Connection,
}
impl Repo {
pub fn sync(
&mut self,
new_paths: impl IntoIterator<Item = RelativePathBuf>,
) -> Result<(), SyncError> {
// (do something mutable with self, this blocks for a while!)
Ok(())
}
}
#[derive(Debug, Copy, Clone)]
enum ManagerStatus {
Idle,
ScanningDirectory,
UpdatingRepo,
}
You can use block_in_place, which does not require a 'static
lifetime on the closure.
async fn resync(&self) -> Result<(), SyncError> {
let new_paths = vec!["a"]; // example paths
let mut repo = self.repo.lock().await;
tokio::task::block_in_place(move || repo.sync(new_paths))?;
Ok(())
}
Otherwise, having an Arc<Mutex<Repo>>
, or even Arc<Self>
might help.