Commit 355d87e
Fix race condition in TaskRegistry/TypeLoader when building with /mt /m mode (#12653)
Fixes: #12735 #12645
## Context
A race condition in the TypeLoader class caused
`System.ObjectDisposedException` crashes when building projects with
`/mt /m` (multi-threaded mode). The error occurred when multiple threads
simultaneously loaded task types using MetadataLoadContext:
```
MSBUILD : error : System.ObjectDisposedException: This object is no longer valid because the MetadataLoadContext that created it has been disposed.
at System.Reflection.TypeLoading.RoProperty.get_Name()
at Microsoft.Build.Execution.ReflectableTaskPropertyInfo..ctor(PropertyInfo propertyInfo, ...)
at Microsoft.Build.Shared.LoadedType..ctor(Type type, ...)
```
### Root Cause
The `MetadataLoadContext _context` field in TypeLoader.cs (line 46) was
declared as static, causing it to be shared across all threads:
1. Thread A creates a MetadataLoadContext and stores it in the static
`_context` field
2. Thread B overwrites the static `_context` with its own
MetadataLoadContext
3. Thread A attempts to access properties on types loaded from its
(now-replaced) context
4. Thread B disposes the `_context`
5. Thread A crashes with ObjectDisposedException when accessing
`propertyInfo.Name`
## Changes Made
Made the MetadataLoadContext instance-local instead of static to
eliminate the shared state:
1. **Removed the static `_context` field** - Eliminated the race
condition source
2. **Renamed and refactored `CreateMetadataLoadContext`** - Changed from
`LoadAssemblyUsingMetadataLoadContext` to return only
`MetadataLoadContext` to enable idiomatic C# resource management with
`using` statement. The function name now accurately reflects that it
only creates the context without loading assemblies.
3. **Updated `GetLoadedTypeFromTypeNameUsingMetadataLoadContext`** -
Uses `using` statement for automatic disposal instead of manual disposal
in finally block. Assembly loading now happens separately after context
creation.
### Why This Works
The MetadataLoadContext is only used to extract metadata (property
names, types, attributes) which is stored as strings/primitives during
LoadedType construction. All metadata extraction completes before
context disposal via the `using` statement. The actual task execution
happens in the TaskHost process where assemblies are loaded normally, so
no access to disposed contexts occurs during runtime.
## Testing
- ✅ Full build completes successfully
- ✅ All TypeLoader unit tests pass
- ✅ All 59 TaskRegistry unit tests pass
- ✅ Sample projects build correctly
## Notes
The use of C#'s `using` statement for resource disposal follows .NET
best practices and makes the code cleaner and more maintainable compared
to manual disposal in a finally block. The function naming has been
updated to accurately reflect its purpose (creating a context rather
than loading an assembly).
<!-- START COPILOT CODING AGENT TIPS -->
---
💡 You can make Copilot smarter by setting up custom instructions,
customizing its development environment and configuring Model Context
Protocol (MCP) servers. Learn more [Copilot coding agent
tips](https://gh.io/copilot-coding-agent-tips) in the docs.
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: AR-May <67507805+AR-May@users.noreply.github.com>1 parent 7085356 commit 355d87e
File tree
1 file changed
+5
-9
lines changed1 file changed
+5
-9
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
43 | 43 | | |
44 | 44 | | |
45 | 45 | | |
46 | | - | |
47 | | - | |
48 | 46 | | |
49 | 47 | | |
50 | 48 | | |
| |||
188 | 186 | | |
189 | 187 | | |
190 | 188 | | |
191 | | - | |
| 189 | + | |
192 | 190 | | |
193 | 191 | | |
194 | 192 | | |
| |||
205 | 203 | | |
206 | 204 | | |
207 | 205 | | |
208 | | - | |
209 | | - | |
| 206 | + | |
210 | 207 | | |
211 | 208 | | |
212 | 209 | | |
| |||
398 | 395 | | |
399 | 396 | | |
400 | 397 | | |
401 | | - | |
| 398 | + | |
| 399 | + | |
402 | 400 | | |
403 | 401 | | |
404 | 402 | | |
| |||
437 | 435 | | |
438 | 436 | | |
439 | 437 | | |
440 | | - | |
| 438 | + | |
441 | 439 | | |
442 | | - | |
443 | | - | |
444 | 440 | | |
445 | 441 | | |
446 | 442 | | |
| |||
0 commit comments