Skip to content

fix(http): wait for the prefetch goroutine to exit before reusing its file - #1606

Open
Grinv wants to merge 2 commits into
GopeedLab:mainfrom
Grinv:fix/prefetch-stop-race
Open

Grinv wants to merge 2 commits into
GopeedLab:mainfrom
Grinv:fix/prefetch-stop-race

Conversation

@Grinv

@Grinv Grinv commented Oct 24, 2026 •

Copy link
Copy Markdown

Fixes #1563.

stopPrefetchAndCopyData waited at most 10 s for the prefetch goroutine, but a stalled read only times out after readTimeout (15 s). After the wait gave up, cleanupPrefetchFile removed the temp file while asyncPrefetch could still write to it.

Changes:

  • stopPrefetch() closes the Resolve body to interrupt a pending read, then waits on prefetchExitCh. stopPrefetchAndCopyData, Pause and completeFromResolveFallback all wait this way now.
  • The stop channel is closed under resolveRespLock, so a concurrent Pause and commitResolveTakeover can't close it twice.
  • downloadWithProxy calls Setup before Resolve, as the downloader does. This fixes the TestFetcher_DownloadWithProxy reports.

Tests:

  • New TestFetcher_StopPrefetchUnblocksStalledRead passes in about 0.05 s and fails after 15 s without the body close.
  • go test -race ./internal/protocol/http/ reports no races (12 on main) and takes about 180 s instead of 540 s. go test ./pkg/download/... passes.
  • Go 1.27.3, darwin/arm64, HTTP/1.1 only.

This may conflict with #1597 (TimeoutReader).

Grinv added 2 commits October 24, 2026 14:34
… file

stopPrefetchAndCopyData waited at most 10 s for the Resolve prefetch, but a
stalled body read only times out after readTimeout (15 s). The caller then
closed and removed the temporary file while the prefetch goroutine could
still write to it.

Stopping the prefetch now closes the Resolve body, which interrupts a
pending read, and waits on a channel closed when the goroutine exits. Pause
uses the same path. The stop channel is closed under resolveRespLock so a
concurrent Pause and first-connection takeover cannot close it twice.

The proxy test helper now calls Setup before Resolve, as the downloader
does, so the Resolve request also goes through the proxy.

Fixes GopeedLab#1563
Replaces the 10 ms polling loop with the exit channel added in the
previous commit, so every place that waits for the prefetch uses it.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] Data races between the resolve prefetch and the download takeover (go test -race)

1 participant