I want to create types at Runtime and instantiate it via Activator.CreateInstance. I am using Refletion.Emit to do it and everithing works fine when the method that creates and instantiates the type runs in a single thread. Hoever, when I try to run the same method in more than one thread an ArgumentException is thrown.
The code is similar to:
class TypeBuilder
public IMyType Build() {
Type type = GetDynamicType("MyDynamicType");
IMyType myType = (IMyType) Activator.CreateInstance(type);
return myType;
}
Type GetDynamicType(string typeName) {
// define the module builder...
ModuleBuilder module = ...
Type type = module.GetType(typeName);
if (type == null) {
type = MakeDynamicType(typeName);
}
retyrn type;
}
Type MakeDynamicType(string typeName) {
lock(lock_) { // lock_ is a static variable
// ensure that the type was not already created by another thread.
Type type =
module
.GetType(typeName);
if (type != null) {
return type;
}
// define the type builder...
TypeBuilder builder = ...
// define the type body...
return type.CreateType();
}
}
}
Some observations:
Update 1:
The exception message is : "Type must be a type provided by the runtime."
Update 2:
The full source is hosted on GitHub
Maybe a late answer, but in any way.
In your code, GetDynamicType
is not thread safe, there are two race conditions which aren't handled. The more interesting of them is as follows:
One thread defines a new type with ModuleBuilder.DefineType(...)
. It gets TypeBuilder
object which it then uses to build the type.
At the same time, the other thread calls ModuleBuilder.GetType(...)
and gets the Type
object it looks for... at least it thinks so. It assumes it got the RuntimeType
, but actually, it gets the TypeBuilder
being built by the first thread.
Only when the first thread calls TypeBuilder.CreateType()
, the TypeBuilder
is replaced with the corresponding RuntimeType
in the ModuleBuilder
.
And so you end up with trying to Activator.CreateInstance
of a TypeBuilder
, which throws the "Type must be a type provided by the runtime" exception.
I would suggest rewriting GetDynamicType
function and use a ConcurrentDictionary<string, Type>
with MakeDynamicType
as value factory:
private readonly ConcurrentDictionary<string, Type> _dynamicTypesByName =
new ConcurrentDictionary<string, Type>();
Type GetDynamicType(string typeName) {
// define the module builder...
ModuleBuilder module = ...
return _dynamicTypesByName.GetOrAdd(typeName, MakeDynamicType);
}