Concepts
Concurrency and tasks
The foreground thread, background work, and how tasks are kept alive.
GPUI has one foreground thread. All entity access and all rendering happen there. Background threads do work that touches no entity.
// Foreground: may touch entities through the AsyncApp it receives.
cx.spawn(async move |this: WeakEntity<Self>, cx: &mut AsyncApp| {
let result = cx.background_spawn(async move { expensive() }).await;
this.update(cx, |this, cx| {
this.result = Some(result);
cx.notify();
})
})
.detach();Both spawn and background_spawn return a Task<R>. Dropping a task
cancels it, so every task must be awaited, detached
(.detach() or .detach_and_log_err(cx)), or stored in a field for as long
as the work should run.
Two conventions from Zed that this fork keeps:
-
Scope clones with shadowing inside the spawn block:
executor.spawn({ let task_ran = task_ran.clone(); async move { *task_ran.borrow_mut() = true; } }); -
In tests, use the executor’s timers (
cx.background_executor().timer(duration).await) rather thansmoltimers, sorun_until_parked()can drive them.