Fix RegFree manifest-file race under parallel MSBuild - #988
Conversation
33cdfd2 to
a646ea3
Compare
Code review (verified against the code at the PR head)Overall: the mutex approach is sound and the regression test (12 concurrent MEDIUM —
|
|
Fixed in 73ea066: wrapped |
jasonleenaylor
left a comment
There was a problem hiding this comment.
@jasonleenaylor reviewed 3 files and all commit messages, and made 1 comment.
Reviewable status:complete! all files reviewed, all discussions resolved (waiting on johnml1135).
|
@copilot resolve the merge conflicts in this pull request |
73ea066 to
e4bf065
Compare
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #988 +/- ##
=======================================
Coverage 38.05% 38.05%
=======================================
Files 1499 1499
Lines 350117 350117
Branches 40233 40233
=======================================
+ Hits 133226 133234 +8
+ Misses 187607 187598 -9
- Partials 29284 29285 +1 🚀 New features to boost your workflow:
|
e4bf065 to
ab11843
Compare
This comment has been minimized.
This comment has been minimized.
Serialize manifest updates by output path so parallel build workers cannot corrupt a shared file. Add concurrency regression coverage. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ab11843 to
8d57a97
Compare
jasonleenaylor
left a comment
There was a problem hiding this comment.
@jasonleenaylor reviewed 3 files and all commit messages.
Reviewable status:complete! all files reviewed, all discussions resolved (waiting on johnml1135).
Parallel FieldWorks builds can now generate registration-free COM manifests without racing when multiple MSBuild workers target the same output file.
RegFree.Execute()serializes each manifest's complete read-modify-write operation across processes while unrelated manifests remain independent.The key review question is whether the lock is narrow enough to preserve build parallelism but broad enough to protect every manifest mutation. The mutex is keyed by the normalized manifest path and encloses the load, update, write, and delete paths. Its name uses a deterministic digest because .NET string hash codes are process-specific. An abandoned mutex is treated as acquired, matching the platform contract, so the existing task error handling can report any damaged manifest instead of leaking an unhandled exception.
Where to look
RegFree.Execute()owns the mutex for the complete manifest transaction and releases it infinally.ManifestLockName()creates the same lock identity in every worker without globally serializing different manifests.AbandonedMutexExceptionhandling preserves ownership and lets normal manifest validation and error reporting continue.RegFreeConcurrencyTestsruns 12 tasks against one manifest and verifies every result plus the final XML content.Deliberately not here
Verification
./test.ps1 -TestProject Build/Src/FwBuildTasks/FwBuildTasksTests -StartedBy agentpassed locally: 146 managed tests passed, 3 skipped, and the repository script's 31 native smoke tests passed. The full FieldWorks suite was not rerun locally after the rebase; CI remains the full gate.Reading this a year from now -- start here
This focused branch carried no Markdown research or working notes to preserve or delete. The decision record lives here because the source comments intentionally retain only the local concurrency invariants a maintainer needs while reading the code.
Decisions, and why
XmlWriter.Create(). A worker must not read another worker's partially updated state or overwrite changes made after its own read.string.GetHashCode()was rejected because its value can differ between worker processes. MD5 is used only as a stable identifier, not for security.AbandonedMutexExceptionis a successful acquisition signal with warning semantics. Continuing under ownership allows the existing XML load and task error handling to detect and report a manifest left incomplete by the previous holder.Regression evidence
The original failure was an intermittent
IOExceptionwhile parallel workers wrote the same manifest during PR #964; rerunning the same CI job passed, which isolated the problem to timing rather than deterministic input.The regression test compiles a COM-visible assembly, launches 12 concurrent
RegFree.Execute()calls against one manifest, requires every call to succeed, loads the result as XML, and verifies the expectedclrClass. During development it failed in 3 of 3 runs with the mutex removed and passed in 5 of 5 runs with the mutex restored. The final squashed commit passed the completeFwBuildTasksTestsproject locally: 146 passed and 3 skipped.