views:

372

answers:

4

I have a .NET C# / C++ app which uses a call to exit(0) (from <stdlib.h>) in a thread in order to terminate.

The strange part is, under some circumstances, the finalizers of the managed objects are called right after the call to exit, and in other circumstances, they are not called at all.

The circumstances are pretty deterministic - the app calls some methods from an external plugin dll (written in unmanaged C) during its lifetime.
If I use dll A, the finalizers are always called.
If I use dll B, the finalizers are never called.

What's the expected behaviour of finalizers in case of an exit(0) call? (if there is any expected -and documented- behaviour)

Can the calls to the external dlls change some global setting that may impact the way the process is terminated?

+2  A: 

Chris Brumme talked about how finalizers are handled during process shutdown:

The bottom line is that there seems to be very little in the way of guarantees for finalizers running at shutdown, but I'm not sure what the DLLs may be doing to causing things to act differently (maybe it 's that one DLL is doing something in DLL_PROCESS_DETACH processing that's giving .NET the opportunity to process finalizers.

The article is for .NET 1.x - I'm not sure how much of this has changed in .NET 2.0 or later.

Michael Burr
+3  A: 

According to Jeff Richter's book, the system does attempt to call finalizers at process shutdown, but there is both a per-finalizer (2s) and a total-finalization (40s) timeout covering this process, after which the process is aborted. (Of course, the exact times might have changed by now, that was right for 2.0)

Could you be seeing a finalizer which takes more than 2 seconds to run? That will cause finalisation to cease.

Will Dean
+1  A: 

Ultimately this is an "arms race" problem. Someone logs a bug at Microsoft complaining that their finalizers aren't running when some nasty code makes the process exit - so that problem gets fixed. Then someone else logs a bug about how there doesn't seem to be a way to force the process to exit instantly so that finalizers don't run, so Microsoft adds a new API to allow that again. So another person demands a new kind of "critical" finalizer that always runs even in response to that new kind of exit... and so on.

So it would probably be easier to change the C++ code than to try and rely on who is currently winning the arms race.

Daniel Earwicker
TerminateProcess(GetCurrentProcess()) is guaranteed to not run finalizers or rewind stack (these are native methods -- P/Invoke them both).
Joshua
+1  A: 

If the code you put in finalizers is rather critical, move it to Dispose and put a call to Dispose in your finalizers with if( !disposed ) pattern in Dispose.

In my book, finalizers shouldn't be relied upon to ever be called by runtime. Dispose is explicit and you have much finer, and deterministic control over it.

Tanveer Badar