Repository navigation
Desktop: fix quit hang when a stream fetch is cancelled - #239
Merged
Merged
Conversation
Fixes #214. When the core stopped, the downloader task was cancelled but nothing set `closed`, and the resulting `cancelled` fetch error went through `record_fetch_error` as if transient — so a decoder blocked in `HttpSource::read` waited through it forever and shutdown hung. - Add a drop guard in `downloader` that sets `closed` and notifies `data_ready` whenever the task ends, however it ends (error return, task cancellation, runtime shutdown). - Treat `FetchError::Cancelled` as final: the downloader stops instead of retrying, and `read` surfaces the error instead of waiting through it. A cancelled body stream is final too. - Tests: a cancelled fetch surfaces from `read` promptly, and dropping the runtime wakes a decoder blocked on a stalled fetch. Generated by GLM-5.3-Flash in [OpenCode](https://opencode.ai) Co-authored-by: GLM-5.3-Flash <opencode-agent[bot]@users.noreply.github.com>
- CancelFetcher reuses the Arc<Mutex<Vec<u64>>> request log and the test pins that the downloader stops after the first cancelled body (exactly one request, no retries). - The runtime-shutdown test asserts the read error is "source closed" rather than just any error. Generated by GLM-5.3-Flash in [OpenCode](https://opencode.ai) Co-authored-by: GLM-5.3-Flash <opencode-agent[bot]@users.noreply.github.com>
The stream reader also cancels handles it evicts as idle (after a long pause), and refetching from the gap recovers from that. A cancelled fetch stays final, so a stopped reader still ends the downloader after one refetch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.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.
Fixes #214.
Cause
When the core stopped, the
downloadertask was cancelled: reader handles dropped, fetch returnedcancelled, and the task was dropped mid-backoff or by runtime shutdown. Nothing setclosed, andcancelledwent throughrecord_fetch_erroras if transient — so the decoder blocked inHttpSource::readlogged "waiting through fetch error" every 200 ms forever, and shutdown blocked on the decode thread.Fix (
crates/hocket-core/src/audio/native/http.rs)CloseGuard: a drop guard indownloadersetsclosedand notifiesdata_readywhenever the task exits, however it exits — error return, task cancellation (engine stop), or runtime shutdown. Any decoder blocked inreadwakes and surfaces an error.cancelledis final:is_final_fetch_errortreatsFetchError::Cancelledas non-retryable. The downloader returns instead of backing off and retrying, andreadsurfaces the error ("cancelled") instead of waiting through it. Acancelledbody-stream error stays retryable (the stream reader also cancels handles it evicts as idle after a long pause); the refetch then fails as cancelled after a real stop and ends the downloader.Tests
cancelled_fetch_surfaces_from_read_instead_of_waiting: body cancelled after the first chunk →readreturns an error containing "cancelled" promptly.runtime_shutdown_wakes_a_blocked_decoder: fetch stalls forever, decoder blocked inread_to_endon another thread; dropping the runtime (engine stop) wakes it and the thread exits.Generated by GLM-5.3-Flash in OpenCode