Esc

    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 than smol timers, so run_until_parked() can drive them.