fix: let a navigation-result collector take over a key from a cancelled holder - #200
Open
PavelMesicek wants to merge 1 commit into
Open
PavelMesicek wants to merge 1 commit into
PavelMesicek wants to merge 1 commit into
Conversation
…ed holder Co-Authored-By: Claude Fable 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 #199.
NavigationResultRegistry.results()used aSet<String>to fail fast on duplicate collectors, released in the collecting flow'sfinally. When a destination is recreated in place (bringToFrontwith a reseeded configuration), Decompose creates the replacement child before destroying the outgoing one, and both the successor's attach and the outgoing collector's cancellation cleanup go through the main queue in that order. The successor therefore found the key still held and thecheckthrew out of a barelaunchIn, killing the process. The issue has the full trace.This PR keeps the fail-fast contract but makes it precise about what "active" means: the registry now tracks the holder's
Jobper key, and a new collector may take the key over when the holder is no longer active.Job.cancel()flipsisActivesynchronously, so the successor attached on the same tick sees the outgoing collector as gone even though itsfinallyhas not been dispatched yet. A genuinely live holder still fails the check exactly as before. Thefinallyrelease is guarded so a collector that was taken over cannot remove its successor's registration.Nothing changes about delivery semantics: the pending durable value is consumed only after the check, so a handover loses no result, and the existing duplicate-collector test still passes unchanged.
The new test replays the create-before-destroy ordering against the real registry: it fails on
mainwith theIllegalStateExceptionfrom the check and passes with this change. Ran:decompose:allTestsandktlintCheck, both green.🤖 Generated with Claude Code