Assert no enrichment pass is active instead of polling the caller's defer - #533
Merged
Merged
Conversation
…r's defer TestManager_CloseCancelsContextProviderAndWaitsForPass polled enrichDone without blocking, but that channel is closed by a deferred close in the test's own goroutine, which runs after the pass releases the active-pass gate Close waits on. Nothing orders that goroutine against Close's return, so a loaded runner that had not yet rescheduled it failed the assertion — the macOS CI failure, reproduced locally at 2/1500 under CPU saturation. Assert the invariant Close actually provides instead: the pass writes its terminal enrichment status before releasing the gate, so no pass may be running or draining the instant Close returns. That check is ordered by Close's own wait, and it catches a Close that stops waiting more often than the poll it replaces (19/25 vs 11/25 with the wait removed). The enrichDone handshake stays as a bounded wait, so a pass that genuinely never stops still fails the test.
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.
What broke
TestManager_CloseCancelsContextProviderAndWaitsForPassfailed onmain(macOS runner, run 31365875121):Why
The test polled
enrichDonewithout blocking. That channel is closed by adeferin the test's own goroutine, which runs afterrunEnrichOne's deferredendPassreleases theactivePassesgateClosewaits on. Nothing orders that goroutine's remaining instructions againstClosereturning in the test goroutine, so a runner that had not yet rescheduled it hitdefaultand failed.This is a test-timing defect, not a product bug:
runEnrichOnedrains its provider goroutine and writes its terminal status beforeendPass, soClose's guarantee — no admitted pass can still mutate the graph — holds.TestManager_CloseStopsLegacyProviderBeforeReturninghad the identical defect and was fixed in df855b9; this sibling was missed.Reproduced locally at 2/1500 (
-race, fullGOMAXPROCS, cores saturated), same line and message. It does not reproduce atGOMAXPROCS=1— the pass goroutine keeps its P afterDone()and finishes unwinding.The fix
Assert the invariant
Closeactually provides: no pass may be running or draining the instantClosereturns. The terminalsetEnrichStatushappens-beforeendPass, soEnrichmentActive()is ordered byClose's own wait. TheenrichDonehandshake stays, as a bounded wait, so a pass that genuinely never stops still fails.The replacement is a stronger guard, not just a quieter one. With
m.activePasses.Wait()removed fromClose:enrichDonepollEnrichmentActive()assertionVerification
-run TestManager_Close -raceunder CPU saturation — clean (old code failed at 1500)go test -race ./internal/semantic/...— all 5 packages greengolangci-lint run ./internal/semantic/...— no issues