fix(desktop): bound wedged stream-swaps with a timeout + recover a missed first-frame race - #327
Merged
Merged
Conversation
…ssed first-frame race A wall tile's stream-swap (main/sub override, zoom-to-main) parks the replacement player in _pending and keeps the old player painting until the replacement's first frame. If the new stream's URL never decodes, the swap never completed and _reconnectStalled's "a swap is in flight" guard (_pending != null) permanently disabled that tile's stall watchdog too - a later, unrelated real freeze on the same tile was then never auto-reconnected either. Arm a 15s timeout whenever a player is parked in _pending; on expiry, retire it (detached, #105) and clear _pending so the watchdog resumes normal operation. Also re-check player.state.width once right after _pending is assigned, since a first-frame width event that fires during the preceding `await player.open()` (before _pending is set) is otherwise silently dropped by _onFirstFrame's identical(player, _pending) check. Fixes #318 Signed-off-by: badbread <badbread@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes #318 — a wall tile's stream-swap (main/sub override, zoom-to-main) could wedge forever with no recovery, and permanently disable that tile's stall watchdog in the process.
_WallTileState._loadkeeps the old player painting while the replacement ("pending") player decodes toward its first keyframe, promoting it once_onFirstFramesees a real frame. There was no timeout: if the newly-selected stream's URL never decodes,_pendingis never cleared. Since_reconnectStalled(the stall watchdog's recovery path) refuses to run at all whenever_pending != null— reasoning that a user-initiated swap must be resolving on its own — a wedged swap also silently disabled the watchdog for any later, unrelated real freeze on that same tile.Separately: if the replacement player's
widthstream event fires very early — during theawait player.open()call, before_pendingis assigned —_onFirstFrame'sidentical(player, _pending)check drops it, since_pendingisn't set yet. That first-frame signal is then lost, leaving the swap dependent on a second signal that may never come.Changes
Timer? _pendingTimeoutfield +_pendingSwapTimeout(15s) constant. Armed whenever a player is parked in_pending; on expiry,_onPendingSwapTimedOutretires the wedged player (detached, per the existing_disposePlayerDetached/Desktop: ~10s UI freeze on maximize/restore transitions (main-stream player teardown blocks the UI thread) #105 discipline) and clears_pending, re-enabling the stall watchdog._pendingis otherwise cleared:_onFirstFrame(successful promotion) anddispose()(tile teardown)._pending = playeris assigned, re-checkplayer.state.widthonce and call_onFirstFramesynchronously if it's already positive — recovers the race where the first-frame signal arrived during the precedingopen()await.Verification
Not build-verified — the Flutter desktop client builds on a separate Windows host (winbuild) not available this session; no
flutter analyze/flutter testwas run. Read the full_WallTileStateswap/promote/dispose lifecycle by hand to confirm the timer is armed/cancelled on every path that sets or clears_pending(swap-start, promote, timeout, and tile dispose) with no double-dispose or leaked timer. Please runflutter analyzeand exercise a stream-swap to a dead URL on winbuild before merging.🤖 Generated with Claude Code