fix(quickfiler-test): bound TransactionGate acquisition in UiThreadDispatcherFixture (882) - #934
Merged
drmoisan merged 13 commits intoSep 29, 2026
Conversation
…ctiongate-permit-leak-unexcluded-882
…d atomic plan for bounded TransactionGate acquisition Preparation for issue 882 in run bugs-2026-09-28 after merging origin/main. Adds the 2026-09-28 research refresh, revises spec.md to v1.1 (parallel-safe single test, counter placement, evidence constraints), and replaces the plan stub with the atomic plan that cleared executor preflight in five rounds. Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com Claude-Session: https://claude.ai/code/session_01KNZiXntshsLY8vqqCHUvHm
…-level fail-before dossier
…eoutException on expiry
…and throw TRANSACTIONGATE_ACQUIRE_TIMEOUT on expiry (#882)
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.
Suggested title
fix(quickfiler-test): bound TransactionGate acquisition in UiThreadDispatcherFixture (issue 882)
Summary
UiThreadDispatcherFixture.BeginTransactionAsyncnow acquires the process-wide one-permitTransactionGatewith a bounded wait (production default 120000 ms) instead of the unbounded parameterlessSemaphoreSlim.WaitAsync().System.TimeoutExceptioncarrying the tokenTRANSACTIONGATE_ACQUIRE_TIMEOUT, namingTransactionGateand the elapsed bound, before anyUiThreadDispatcherTransactionexists and before any counter is incremented.BeginTransactionAsync(TimeSpan bound)exposes the bound to tests; the parameterless overload delegates to it, so no call site changes.QuickFiler.Testproject. No shipped add-in production file is modified.Why
TransactionGatecould leak or late-release a permit, and nothing in the repository excluded that possibility. With the parameterlessWaitAsync()a leaked permit produces an unbounded wait that surfaces only as a runner hang.WaitAsync(TimeSpan)satisfies both clauses.What Changed
Test-support fixture
QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixture.csTransactionGateAcquireTimeoutMs = 120000.BeginTransactionAsync()delegates toBeginTransactionAsync(TimeSpan)with the production default.falseaTimeoutExceptionis thrown;_transactionAcquisitionsis incremented only on the successful branch.UiThreadDispatcherTransactioncref is updated toBeginTransactionAsync()because the method group is now overloaded.Tests
QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.csBeginTransactionAsync_ZeroBoundWhileThisTestHoldsThePermit_ThrowsTimeoutExceptionAndReleasesNothing: holds a transaction, probes the internal overload withTimeSpan.Zero, assertsTimeoutExceptionwith the token, asserts acquisitions minus releases equals exactly one, asserts the contended counter advanced, asserts the holder's own disposal does not throwSemaphoreFullException, then round-trips a further transaction through the production entry point.[Timeout(GateTimeoutMs)]; noDoNotParallelize, retry, sleep, delay, or elapsed-time assertion.Docs and evidence
docs/features/active/2026-09-13-quickfiler-transactiongate-permit-leak-unexcluded-882/: spec v1.1, atomic plan, research records, baseline / regression-testing / qa-gates evidence, and the policy, code-review, and feature audits.Architecture / How It Fits Together
TransactionGateprovides mutual exclusion between install-to-restore transactions over the staticUtilitiesCS.UiThread._dispatcher.UiThreadDispatcherTransactionis the only releaser and is constructed only on the branch where the bounded wait returnedtrue, so the failure path has no object to dispose and no release to omit.usingandtry/finallyaround the acquisition sites stays correct unchanged, because a throw fromBeginTransactionAsyncoccurs before the scope is entered or the assignment completes.Verification
Completed (from committed evidence)
evidence/regression-testing/fail-before-exception.2026-09-29T09-06.md. Before the fix the new test does not compile (error CS1501: No overload for method 'BeginTransactionAsync' takes 1 arguments), so a runtime fail-before run is structurally impossible.evidence/regression-testing/pass-after-scoped-run.md).evidence/qa-gates/qa-loop-closure.md):dotnet tool run csharpier check .: exit 0, 1623 files checked, no drift.msbuild TaskMaster.sln /t:Rebuild /m /p:Configuration=Debug "/p:Platform=Any CPU" /p:EnableNETAnalyzers=true /p:EnforceCodeStyleInBuild=true:Build succeeded., 0 warnings, 0 errors.msbuild TaskMaster.sln /t:Rebuild /m /p:Configuration=Debug "/p:Platform=Any CPU" /p:TreatWarningsAsErrors=true:Build succeeded., 0 warnings, 0 errors.QuickFiler.Testunder dotnet-coverage with the parallel CLI runsettings: 1469 total, 1469 passed, 0 failed (baseline 1468 plus the new test) (evidence/qa-gates/mstest-test-result-summary.md).QuickFiler.Test-only observation (lines 24.42%, branches 23.20%). Both changed files are test code outside the first-party coverage denominator, so no first-party coverage movement is possible; the repository-wide figure is not measured in this PR (evidence/qa-gates/qa-coverage-comparison.md).Recommended
Backward Compatibility / Migration Notes
internalto the test project, and existing callers resolve to the parameterless overload unchanged.Risks and Mitigations
Transaction_SecondCallerCannotInstallUntilTheFirstRestorestest tracked in issue Bug: quickfiler-teardown-review-residuals #823 remains out of scope; it passed in every run recorded here.Review Guide
QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixture.cs(the bounded overload and counter ordering).QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs(the new test, lines 395 to 456).docs/features/active/2026-09-13-quickfiler-transactiongate-permit-leak-unexcluded-882/spec.mdand theevidence/folders. The remaining diff is feature-folder documentation and evidence.Follow-ups
GitHub Auto-close
🤖 Generated with Claude Code
https://claude.ai/code/session_01KNZiXntshsLY8vqqCHUvHm