Add thread safety constraints

This commit is contained in:
Mikayla Maki
2025-12-27 10:38:12 -08:00
parent 02b1387e61
commit 28dc83f076
2 changed files with 48 additions and 21 deletions

View File

@@ -9,48 +9,60 @@
"tasks": [
{
"id": "1.1",
"description": "Add Option<Weak<AppCell>> field to RunnableMeta struct",
"file": "crates/gpui/src/executor.rs",
"description": "Add MainThreadWeak<T> newtype with unsafe Send+Sync impls to platform.rs",
"file": "crates/gpui/src/platform.rs",
"status": "not_started"
},
{
"id": "1.2",
"description": "Update trampoline function in Mac dispatcher to check app status before run()",
"file": "crates/gpui/src/platform/mac/dispatcher.rs",
"description": "Update RunnableMeta to use Option<MainThreadWeak<AppCell>> for app field",
"file": "crates/gpui/src/platform.rs",
"status": "not_started"
},
{
"id": "1.3",
"description": "Update trampoline function in Test dispatcher to check app status before run()",
"file": "crates/gpui/src/platform/test/dispatcher.rs",
"description": "Update trampoline in Mac dispatcher to check app via unsafe upgrade() before run()",
"file": "crates/gpui/src/platform/mac/dispatcher.rs",
"status": "not_started"
},
{
"id": "1.4",
"description": "Modify ForegroundExecutor::spawn_with_priority to accept optional app weak pointer",
"file": "crates/gpui/src/executor.rs",
"description": "Update tick() in Test dispatcher to check app via unsafe upgrade() before run()",
"file": "crates/gpui/src/platform/test/dispatcher.rs",
"status": "not_started"
},
{
"id": "1.5",
"description": "Update AsyncApp::spawn to pass app weak pointer to executor",
"file": "crates/gpui/src/app/async_context.rs",
"description": "Add ForegroundExecutor::spawn_with_app that accepts Weak<AppCell>",
"file": "crates/gpui/src/executor.rs",
"status": "not_started"
},
{
"id": "1.6",
"description": "Update AsyncApp::spawn to pass self.app wrapped in MainThreadWeak",
"file": "crates/gpui/src/app/async_context.rs",
"status": "not_started"
},
{
"id": "1.7",
"description": "Update AsyncWindowContext::spawn to pass app wrapped in MainThreadWeak",
"file": "crates/gpui/src/app/async_context.rs",
"status": "not_started"
},
{
"id": "1.8",
"description": "Write unit test verifying task cancellation when app is dropped",
"file": "crates/gpui/src/executor.rs",
"status": "not_started"
},
{
"id": "1.7",
"id": "1.9",
"description": "Write unit test for nested tasks both cancelling cleanly",
"file": "crates/gpui/src/executor.rs",
"status": "not_started"
},
{
"id": "1.8",
"id": "1.10",
"description": "Create async_cancellation.rs example demonstrating behavior",
"file": "crates/gpui/examples/async_cancellation.rs",
"status": "not_started"

View File

@@ -130,27 +130,42 @@ The `Flatten` trait in `gpui.rs` exists solely to handle `Result<Result<T>>` whe
### RunnableMeta
Add optional app weak pointer:
Add optional app weak pointer using a `MainThreadWeak<T>` newtype that unsafely implements `Send + Sync`:
```rust
/// Weak reference that can cross thread boundaries but must only be accessed on the main thread.
/// SAFETY: Only create, access (upgrade), and drop on the main thread.
pub struct MainThreadWeak<T>(Weak<T>);
unsafe impl<T> Send for MainThreadWeak<T> {}
unsafe impl<T> Sync for MainThreadWeak<T> {}
pub struct RunnableMeta {
pub location: &'static Location<'static>,
pub app: Option<Weak<AppCell>>, // NEW
pub app: Option<MainThreadWeak<AppCell>>, // NEW
}
```
**Safety invariants:**
- `app` is `Some` only for foreground tasks (spawned via `ForegroundExecutor::spawn_with_app`)
- `ForegroundExecutor` is `!Send`, so spawn only happens on main thread
- Trampolines run on main thread, so `upgrade()` is main-thread only
- Background tasks always use `app: None`
## Implementation Phases
### Phase 1: Add Trampoline Check Infrastructure (macOS First)
### Phase 1: Add Trampoline Check Infrastructure (macOS + Test)
Start with macOS to validate the approach before implementing on other platforms.
Start with macOS and Test dispatcher to validate the approach.
1. Modify `RunnableMeta` to include `Option<Weak<AppCell>>`
2. Update trampoline function in Mac dispatcher to check app status before running:
1. Add `MainThreadWeak<T>` newtype to `crates/gpui/src/platform.rs` with unsafe `Send + Sync` impls
2. Update `RunnableMeta` to use `Option<MainThreadWeak<AppCell>>` for app field
3. Update Mac dispatcher trampoline to check `unsafe { app.upgrade() }` before `run()`:
- `crates/gpui/src/platform/mac/dispatcher.rs`
3. Update trampoline function in Test dispatcher (needed for tests):
4. Update Test dispatcher `tick()` to check `unsafe { app.upgrade() }` before `run()`:
- `crates/gpui/src/platform/test/dispatcher.rs`
4. Modify spawn paths in `AsyncApp` to populate the app weak pointer in metadata
5. Write tests to validate cancellation behavior on macOS
5. Add `ForegroundExecutor::spawn_with_app` that wraps `Weak<AppCell>` in `MainThreadWeak`
6. Update `AsyncApp::spawn` and `AsyncWindowContext::spawn` to use `spawn_with_app`
7. Write tests to validate cancellation behavior
### Phase 2: Extend to Other Platforms