Project: AsyncApp Result Removal - Codebase Migration
Prerequisites
This brief depends on the completion of async-app-result-removal.md, which:
- Adds trampoline check infrastructure in dispatchers
- Updates
AsyncAppAPI withtry_update()/update()pattern - Removes
AppContext::Result<T>associated type - Removes
Flattentrait
Overview
This phase migrates the entire codebase to use the new AsyncApp API. Since foreground tasks are now automatically cancelled when the app dies, most callsites can remove their error handling.
Phase 1: Audit Cross-Boundary Awaits
Goal
Identify places where background tasks await foreground tasks, as these are the edge cases that need try_update().
Search Patterns
# Background tasks awaiting foreground work
background_executor().spawn(...).await
# Channels receiving from foreground tasks
receiver.recv().await # where sender is in foreground
# Task<T> held across thread boundaries
let task: Task<T> = cx.spawn(...) # then moved to background
Known Patterns to Check
- cx.spawn(...).await from background - If a background task stores an
AsyncAppand callsspawn, then awaits the result - Channel patterns - Foreground sends via channel, background receives
- Task handles passed to background - A
Task<T>created in foreground, awaited in background
Action Items
- Search for
background_spawncalls that contain awaits on foreground work - Audit
Task<T>usage to find cross-boundary cases - Create list of callsites requiring
try_update()instead ofupdate() - Document any patterns that cannot be safely migrated
Phase 2: Codebase Migration
Migration Strategy
- Automated pass: Use search-and-replace for simple patterns
- Manual review: Handle complex cases requiring
try_update()
Simple Patterns (Automated)
// Before
cx.update(|app| { ... })?
cx.update(|app| { ... }).ok();
cx.update(|app| { ... }).unwrap();
// After
cx.update(|app| { ... })
Complex Patterns (Manual)
// Background awaiting foreground - use try_update
cx.background_spawn(async move {
// Before
let result = task.await?;
// After - if task could be cancelled
let Some(result) = cx.try_update(|app| { ... }) else {
return; // or handle gracefully
};
});
Crate Priority Order
Migrate in dependency order to catch issues early:
crates/gpui- Core frameworkcrates/language- Language supportcrates/project- Project managementcrates/editor- Editor corecrates/workspace- Workspace managementcrates/agentandcrates/agent_ui- AI agent- Remaining crates alphabetically
Per-Crate Checklist
For each crate:
- Find all
AsyncApp/AsyncWindowContextusage - Categorize: simple removal vs. needs
try_update() - Apply changes
- Run
cargo test -p <crate> - Run
./script/clippy
Phase 3: Testing & Cleanup
Remove Dead Code
After migration, search for and remove:
// Dead imports
use anyhow::{Context, Result}; // if only used for AsyncApp errors
// Dead error handling
.context("app was released")
.context("window was closed")
// Unused Result type aliases
type Result<T> = anyhow::Result<T>; // if only used for AsyncApp
Update Documentation
- Update
AsyncApprustdoc to explain new semantics - Update GPUI documentation/examples
- Add migration guide for external users (if any)
New Tests
Add tests to prevent regression:
- Test:
update()works when app is alive - Test:
try_update()returnsNonewhen app is gone - Test: Tasks are cancelled (not panicking) when app dies
- Test: Nested tasks both cancel cleanly
Final Validation
-
cargo test(full suite) -
./script/clippy - Manual testing: heavy async workloads
- Manual testing: rapid window open/close
- Manual testing: quit app with pending tasks
Estimated Scope
Based on grep analysis:
- ~500+ callsites using
AsyncApp::update()or similar - ~50 crates with potential changes
- Most changes are mechanical
.unwrap()/?removal - ~10-20 complex cases requiring
try_update()
Risk Mitigation
Rollback Plan
If issues are discovered post-merge:
- Revert migration commits (codebase changes)
- Keep infrastructure changes (they're backwards compatible)
- Re-evaluate edge cases
Incremental Rollout
Consider migrating in stages:
- First:
crates/gpuionly - validate core behavior - Second: High-traffic crates (
editor,workspace,project) - Third: Remaining crates
Files Reference
Key files for finding callsites:
crates/gpui/src/app/async_context.rs-AsyncAppdefinition- Search:
update(|,update_entity(,read_entity( - Search:
.unwrap(),.ok(),?following async context calls