Skip to content

Commit 67142a6

Browse files
committed
fix(config): migrate Channels TUI config writes to async, fixing macOS test deadlock
The Channels config viewmodel bridged async work (label-refresh cancel-and-await, save, autosave, add-channel) to Termina's synchronous key handlers via `.GetAwaiter().GetResult()`. That bridge is only safe when no SynchronizationContext is captured — true in the real Termina loop, but NOT under the xunit v3 test runner, which installs a MaxConcurrencySyncContext sized to the core count. On macOS CI's smaller worker pool the blocked `.GetResult()` held the only free worker while the cancelled probe's continuation was posted back to that same context — a sync-over-async deadlock. `dotnet test` hung ~30 min and was killed by the job timeout; it passed on higher-core Linux/Windows runners. Remove all four sync-over-async bridges in ChannelsConfigViewModel: - Save/ApplyAddChannel/ApplyResetConfirmation are now async; reset and add are dispatched fire-and-forget from the page key handlers. - Autosave handlers enqueue the persist instead of blocking; the on-loop state mutation is unchanged. - Add one explicit serialization point (PendingConfigWrite) to preserve the write ordering the loop-block provided for free, so two rapid mutations cannot race the disk write + state reload. It runs synchronously inline in the common case. - ApplyAddChannelAsync settles screen + row focus synchronously before the async persist so a subsequent keypress navigates from a deterministic position. Add a deterministic regression test (SingleThreadSynchronizationContext) that drives the reset-with-in-flight-refresh scenario under a single-worker context: it deadlocks the old blocking code (watchdog trips) and passes the async path.
1 parent 5f72db6 commit 67142a6

4 files changed

Lines changed: 285 additions & 107 deletions

File tree

0 commit comments

Comments
 (0)