When can a dynamic module have a type load exception?
Asked Answered
P

2

3

I have a dynamic module which gets types added to it as my application runs. The module is created via the following code:

var assemblyName = new AssemblyName("MyAssembly");
var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
MyClass.RuntimeBoundDerivedTypesModule = assemblyBuilder.DefineDynamicModule("MainModule");

Other parts of the application also sometimes call GetTypes() on the module's assembly. Occasionally, when this happens I get TypeLoadException for one of the types in the dynamic module. The stack trace is:

   at System.Reflection.RuntimeModule.GetTypes(RuntimeModule module)
   at System.Reflection.RuntimeModule.GetTypes()
   at System.Reflection.Assembly.GetTypes()

My question is: what could cause this exception? Are runtime modules truly thread-safe or can there be race conditions where GetTypes() gets called while a type is partway through being created?

EDIT: here's a small snippet of code that reproduces the bug reliably for me. It now seems that the exception occurs if GetTypes() is called between DefineType() and CreateType():

var assemblyName = new AssemblyName("MyAssembly");
var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
ModuleBuilder m = assemblyBuilder.DefineDynamicModule("foo");

Action doStuff = () => {
    try {
        if (!m.GetTypes().Any() || Guid.NewGuid().GetHashCode() % 2 == 0) {
            var t = m.DefineType(
                "MyType" + Guid.NewGuid().ToString().Replace("-", ""),
                TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.AutoClass | TypeAttributes.AnsiClass | TypeAttributes.BeforeFieldInit | TypeAttributes.AutoLayout,
                typeof(object)
            );
            Thread.Sleep(1);
            t.CreateType();
        }
        else {
            //m.GetTypes(); // interestingly, this always works
            assemblyBuilder.GetTypes();
            "it worked!".Dump();
        }

    } catch (Exception ex) {
        Console.WriteLine(ex);
    }
};

// in synchronous mode, I only see failures if I leave out the call to CreateType(). In that
// case, it never works
//Enumerable.Range(1, 1000)
//  .ToList()
//  .ForEach(_ => doStuff());

// in the async mode, I always see failures with Thread.Sleep() and sometimes
// see them if I take the sleep out. I often see a mix of failures and "It worked!"
var threads = Enumerable.Range(1, 100).Select(t => new Thread(() => doStuff()))
    .ToList();
threads.ForEach(t => t.Start());
threads.ForEach(t => t.Join());
Promulgate answered 22/8, 2013 at 17:26 Comment(0)
U
1

ModuleBuilder and in fact most instance methods are not thread safe in .Net. See here at the bottom: http://msdn.microsoft.com/en-us/library/system.reflection.emit.modulebuilder.aspx

Unpracticed answered 28/8, 2013 at 15:55 Comment(0)
J
1

Yesterday I've encountered a similar problem, except I'm not in a threaded scenario. Apparently TypeLoadExceptions can also be thrown if DefineType is called, but CreateType is not. This can easily happen if an exception is thrown in between these two calls, which is what was happening in my scenario. I had a legitimate case for throwing an exception, as the type I was building was turning out to be impossible to build for the given parameters. The solution is to prepare all required information and validate it, before even trying to DefineType.

Jamarjamb answered 13/7 at 22:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.