I'd like to implement an async_trait and use it like this:
#[async_trait]
pub trait Runner {
async fn execute(&self, id: usize, &self, task: Task) -> Result<TaskAttempt>;
async fn kill(&self, id: usize);
}
struct LocalExecutor {
tasks: Arc<Mutex<HashMap<usize, bool>>,
}
impl Executor for LocalExecutor {
async fn execute(&self, id: usize, &self, task: Task) -> Result<TaskAttempt> {
{
let tasks = self.tasks.lock().unwrap();
tasks.insert(id, true);
}
// Spawn the command, intermittently check the run flag
// and kill subprocess if it is false
}
async fn kill(&self, id: usize) {
let tasks = self.tasks.lock().unwrap();
if let Some(run_flag) = tasks.get_mut(&id) {
*run_flag = false;
}
}
}
// The problem is here:
#[tokio::test]
async fn test_stop_task() {
let le = LocalExecutor::new();
// Spawn a long-running task
// Error: borrowed value does not live long enough
let handle = spawn_local(le.execute(0, task));
// Cannot
le.stop_task(0, 0).await.unwrap();
}
The idea is that I can submit a task to get executed with an ID, and at a later time choose to stop the task (e.g. at a user's request). The execute
method should return a TaskAttempt
even if the task was killed, so I can't just kill the joinhandle.
It looks like le
needs to be moved to spawn_local
to avoid the lifetime error, but then I lose the ability to call stop_task
on it.
Am I approaching this with a completely wrong design idea?