diff --git a/tools/tht/internal/safeio/claim_windows_test.go b/tools/tht/internal/safeio/claim_windows_test.go index a847bdd5..276f30c2 100644 --- a/tools/tht/internal/safeio/claim_windows_test.go +++ b/tools/tht/internal/safeio/claim_windows_test.go @@ -201,6 +201,7 @@ func TestCanonicalPrivateClaimConsumeHasOneConcurrentWinner(t *testing.T) { type result struct { found bool + stage string err error } results := make(chan result, 2) @@ -211,20 +212,20 @@ func TestCanonicalPrivateClaimConsumeHasOneConcurrentWinner(t *testing.T) { defer group.Done() claimed, err := ClaimCanonicalPrivateRegular(source, claim) if err != nil || !claimed { - results <- result{err: err} + results <- result{stage: "claim", err: err} return } contents, found, err := ReadCanonicalPrivateClaim(source, claim, 32) if err != nil || !found || string(contents) != "state" { - results <- result{err: err} + results <- result{stage: "read", err: err} return } removed, err := RemoveCanonicalPrivateClaim(source, claim) if err != nil || !removed { - results <- result{err: err} + results <- result{stage: "remove", err: err} return } - results <- result{found: true} + results <- result{found: true, stage: "complete"} }() } group.Wait() @@ -233,7 +234,7 @@ func TestCanonicalPrivateClaimConsumeHasOneConcurrentWinner(t *testing.T) { winners := 0 for got := range results { if got.err != nil { - t.Fatalf("iteration %d concurrent consume error = %v", iteration, got.err) + t.Fatalf("iteration %d concurrent consume %s error = %v", iteration, got.stage, got.err) } if got.found { winners++ diff --git a/tools/tht/internal/safeio/private_root_windows.go b/tools/tht/internal/safeio/private_root_windows.go index 1135643d..84f95b42 100644 --- a/tools/tht/internal/safeio/private_root_windows.go +++ b/tools/tht/internal/safeio/private_root_windows.go @@ -1108,7 +1108,14 @@ func (directory *windowsPrivateDirectory) RemoveClaim(source, claim string) (boo claimed.Close, func() error { return closeAndDeleteWindowsPrivateRegular(value) }, func() error { - remaining, remainingErr := openWindowsPrivateRegularAt(directory.handle, claim, windows.FILE_GENERIC_READ|windows.DELETE, 1) + // A concurrent loser can briefly hold the orphan observation read-only after + // source deletion. Preserve no-delete sharing and wait only for that close. + remaining, remainingErr := openWindowsPrivateClaimRegular( + directory.handle, + claim, + windows.FILE_GENERIC_READ|windows.DELETE, + 1, + ) if remainingErr != nil { return ErrUnsafeFile }