Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How threadsafe is System.Reflection.Emit?

I'm wetting my feet with dynamic code generation and System.Reflection.Emit. All seems pretty easy and straightforward, but there's one question which I cannot find answered on the web.

When building dynamic assemblies with AssemblyBuilder it's possible to create a type and then start using it immediately. If later you need to add another type to the assembly, you can do that too and it's all fine (as far as I can tell).

But what if there are two threads that are trying to build types for that assembly? Or what if one ready-made type is already being used, while another is in the making? Will there be any conflicts or race conditions?

Added: OK, I think that a bit more information is necessary.

What I'm doing with Reflection.Emit is dynamically implementing interfaces at runtime. Basically I have something like:

class MagicClass
{
    public static T GetImplementation<T>();
}

Where T needs to be an interface (and a few other unrelated requirements).

Each interface will have exactly one implementation and that implementation will have exactly one instance. They will all be thread-safe singletons.

So when a request for a new, hitherto unseen interface comes in, I implement it, make an instance and then cache it for all eternity (which is here defined as "until my program is stopped").

This will however be done in a ASP.NET web application, so we have many threads requesting interfaces all the time. Performance is important and I'm trying to figure out how much multithreading I can afford.

It's pretty clear that a single interface will ever only be implemented by a single thread. But can I implement two different interfaces in two different threads at the same time? I guess the answer is - "better not".

OK, so I can add a lock that only one thread is doing an implementation at the same time. That will take care of builders interfering with each other. But what about those interfaces that were implemented previously? Other threads are using them at the same time while we're making a new one. I guess they're fine, unless they're trying to use some kind of reflection on their own assembly?

I could, of course, make one-assembly-per-interface-implementation policy, but that would spam the "Loaded Modules" debugger window real fast. Also, I don't know what performance effects there will be of having a 100 loaded assemblies (but I doubt that they will be good).

Added 2: Right, there must be something wrong with my language, since people don't seem to get it. Let me try with a code example:

var object1 = MagicClass.GetImplementation<I1>();
DoSomethingInAnotherThread(object1);
var object2 = MagicClass.GetImplementation<I2>();

Can there be a threading-related error between DoSomethingInAnotherThread(object1) and MagicClass.GetImplementation<I2>()?

We can assume that:

  • DoSomethingInAnotherThread(object1) does NOT call MagicClass.GetImplementation<T>()
  • DoSomethingInAnotherThread(object1) does NOT use reflection on object1 and neither does object1 itself.

Basically, the question is - can an innocent call to object1.SomeMethod() blow up because the assembly is being reorganized on another thread.

like image 942
Vilx- Avatar asked Jul 11 '14 10:07

Vilx-


People also ask

What is reflection emit?

Reflection. Emit namespace that allow a compiler or tool to emit metadata and Microsoft intermediate language (MSIL) at run time and optionally generate a portable executable (PE) file on disk. Script engines and compilers are the primary users of this namespace.

Is reflection thread safe C#?

0, reflection is not thread safe.


2 Answers

The only declared comment is "Any public static (Shared in Visual Basic) members of this type are thread safe. Any instance members are not guaranteed to be thread safe.", so we can't assume much.

The following is entirely implementation details via inspection of the IL, and cannot be used for anything except anecdote.

Checking DefineDynamicModule, it uses a lock, as does DefineType - in fact, it uses the same SyncLock as the parent assembly builder.

Some things inside TypeBuilder do the same (DefineEvent, DefineMethod, DefineField, etc); but some things, like DefineCustomAttribute end up in extern code, so we can't tell; however, I can't see a lock on the way through, and it doesn't pass the lock the the extern method.

Overall, it looks as though some thought has been given to thread-safety, but most likely they didn't want to formally guarantee it. It looks like most common stuff should be ok, though, especially if you never have multiple threads working on the same type.

Emphasis: this is all completely implementation specific, and we have no set of "this use-case is guaranteed to work, but this has not been considered".

I tend to keep it simple, personally; one builder (per assembly) is usually fine.

like image 184
Marc Gravell Avatar answered Oct 11 '22 14:10

Marc Gravell


It is not specified.

Whenever you use code that is not explicitly documented to be thread-safe then you'll have to make a choice. Weigh the cost of not locking, getting it wrong and have your code crash in an impossible to diagnose way at utterly random intervals. Against the cost of taking a lock and be sure to never be wrong, just a little slow.

Where "little slow" is something you have to know to make the proper choice. It is an easy one in this case, building a method takes a few handful of microseconds, at most. That lock you put around it is not going to be contested. And if it is anyway then the delay that's incurred is unobservable to anybody. A thread context switch or a page fault, normal things that happen that cause code to run slower, take more time than that.

Take the lock. You'll never have to be sorry.

like image 22
Hans Passant Avatar answered Oct 11 '22 15:10

Hans Passant