Repository navigation
Description
Description
Since 10.0.6, concurrent use of separate TypeBuilder objects created from the same ModuleBuilder can result in TypeLoadException when the type is used.
This was identified via this issue:
zzzprojects/EntityFramework-Effort#219
Reproduction Steps
A full reproduction can be found here:
https://github.com/reduckted/EntityFramework-Effort/blob/thread-repro/reproduction
The use of AssemblyBuilder is in this file:
https://github.com/reduckted/EntityFramework-Effort/blob/thread-repro/Main/Source/Effort.Shared/Internal/TypeGeneration/DataRowFactory.cs
From the reproduction folder, run dotnet run. You may need to run it multiple times to encounter the error due to it being a multi-threading problem.
A cut-down version of the code is something like this:
internal static class DataRowFactory {
private static int typeCount;
private static readonly AssemblyBuilder assemblyBuilder;
private static readonly ModuleBuilder moduleBuilder;
private static readonly object moduleBuilderLock;
static DataRowFactory() {
assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("EffortDataRowTypeLib"), AssemblyBuilderAccess.Run);
moduleBuilder = assemblyBuilder.DefineDynamicModule("EffortDataRowTypeLib");
moduleBuilderLock = new object();
}
public static Type Create(IDictionary<string, Type> properties) {
TypeBuilder typeBuilder;
// Lock to prevent concurrent creation of a TypeBuilder.
lock (moduleBuilderLock) {
typeCount++;
typeBuilder = moduleBuilder.DefineType(string.Format("DataRow{0}", typeCount), TypeAttributes.Public, typeof(DataRow), Type.EmptyTypes);
}
// Use the TypeBuidler outside the lock to generate the type...
return typeBuilder.CreateTypeInfo();
}
}You then call DataRowFactory.Create() from multiple threads concurrently.
Expected behavior
The expected behavior is that the types are generated correctly.
I know that AssemblyBuilder doesn't say that it's thread-safe in this way, but this has worked in .NET 10 since 10.0.1, and had previously worked in .NET 9 and .NET 8 and probably earlier versions.
Only since 10.0.6 has the exception started to occur.
Actual behavior
The actual behavior since 10.0.6 is that you might encounter a TypeLoadException when trying to use the generated type.
Regression?
Yes. This workflow run seems to indicate that it worked from 10.0.1 through to 10.0.5, and then started failing in 10.0.6 and above.
https://github.com/reduckted/EntityFramework-Effort/actions/runs/27271952492/job/80543359245
Known Workarounds
Not really a workaround, but you can change the code so that concurrent use of the TypeBuilder is prevented.
Configuration
| Question | Answer |
|---|---|
| Which version of .NET is the code running on? | 10.0.6+ |
| What OS and version, and what distro if applicable? | Windows and Linux (Ubuntu) |
| What is the architecture? | x64 |
| Do you know whether it is specific to that configuration? | Unlikely to be specific |
| If you're using Blazor, which web browser(s) do you see this issue in? | Not using Blazor |
Other information
No response
Activity
Fix concurrent Reflection.Emit TypeLoadException (#129230) (#130696)
[release/11.0] Fix concurrent Reflection.Emit TypeLoadException (#129230
[release/10.0] Fix concurrent Reflection.Emit TypeLoadException (#129230