1

I have 2 assemblies, A containing the Main method and the Foo class, that uses Bar the class from assembly B:

Bar assembly (assembly B):

public sealed class Bar : IDisposable { 
    /* ... */ 
    public void Dispose() { /* ... */ }
}

Foo class (assembly A):

public class Foo : IDisposable {
    private readonly Bar external;
    private bool disposed;
    public Foo()
    { 
        Console.WriteLine("Foo");
        external = new Bar(); 
    }
    ~Foo()
    { 
        Console.WriteLine("~Foo");
        this.Dispose(false); 
    }
    public void Dispose()
    {
        this.Dispose(true);
        GC.SuppressFinalize(this);
    }
    protected virtual void Dispose(bool disposing)
    {
        if (disposed) return;
        if (disposing) external.Dispose();
        disposed = true;
    }
}

Entry point (in assembly A):

class Program
{
    static void Main(string[] args)
    {
        try
        {
            var foo = new Foo();
            Console.WriteLine(foo);
        }
        catch (FileNotFoundException ex) 
        {
            // handle exception
            Console.WriteLine(ex.ToString());
        }
        Console.ReadLine();
    }
}

One of the requirements for this piece of software is that it must gracefully handle the case when a dll is missing.

So when I delete assembly B, and start the application I would expect that the try catch block in the main method handles the FileNotFoundException thrown when the assembly B is missing. Which it sort of does, but that is where the problems start...

When the application continues (a line is entered in the console), the finalizer of the Foo class is called (?!) although no instance of Foo was created - the constructor hasn't been called. Since there is no instance of the class there is no way for me to call GC.SupressFinalize on the instance externally. The only thing you see in the console output when running the project without the B assembly is ~Foo.

So the questions:

  • Why is the finalizer called even though no instance of the class is created? (to me it makes absolutely no sense! I would love to be enlightened)
  • Is it possible to prevent the application from crashing without a try-catch block in the finalizer? (this would mean refactoring the whole code base...)

Some background: I encountered this problem when writing a plugin enable enterprise application with the requirement that it must continue operation if a dll is missing in the plugin deployment folder and flagging the faulty plugin. I figured that the try-catch block around the external plugin loading procedure would suffice, but obviously it doesn't, since after catching the first exception the finalizer is still invoked (on the GC thread), which finally crashes the application.

Remark The above code is the most minimalistic code I could write to reproduce the exception in the finalizer.

Remark 2 If I set the breakpoint in the Foo constructor (after deleting Bar's dll) it is not hit. This means if I would set have a statement in the constructor that creates a critical resource (before newing up Bar) it wouldn't be executed, hence no need for the finalizer to be called:

// in class Foo
public Foo() {
    // ...
    other = new OtherResource(); // this is not called when Bar's dll is missing
    external = new Bar();        // runtime throws before entering the constructor
}

protected virtual void Dispose(bool disposing) {
    // ...
    other.Dispose();    // doesn't get called either, since I am
    external.Dispose(); // invoking a method on external
    // ...
}

Remark 3 An obvious solution would be to implement the IDisposable like below, but that means breaking the reference pattern implementation (even FxCop would complain).

public abstract class DisposableBase : IDisposable {
    private readonly bool constructed;
    protected DisposableBase() {
        constructed = true;
    }
    ~DisposableBase() {
        if(!constructed) return;
        this.Dispose(false);
    } 
    /* ... */
}   
m0sa
  • 10,712
  • 4
  • 44
  • 91
  • 4
    You posted a bad example. This crashes before Main() even is entered. The jit compiler is going to barf when it can't generate code for the constructor. The catch clause can't catch it. Post code that actually reproduces the problem. – Hans Passant Sep 24 '11 at 16:59
  • @Hans Passant - Works for me ... m0sa - This is just an excerpt, but do you need your finalizer? In the code above you're not doing anything (relevant) from that codepath. – ordag Sep 24 '11 at 17:07
  • 2
    I'd rather use a tool that compiles your dlls into a single managed dll. This also prevents users and faulty deployment tools to mix different versions of your dlls. –  Sep 24 '11 at 17:10
  • @ordag: It works when the assembly with Bar is missing? – H H Sep 24 '11 at 17:11
  • I fixed the example. @ordag The finalizer is part of the IDisposable pattern. FXCop complains if its not there. – m0sa Sep 24 '11 at 17:12
  • @m0sa, You'll just need the finalizer with the IDisposable pattern if your class needs to hold (pointers to) unmanaged data, if its just other IDisposables it is fine to just do that from the Dispose() path. – ordag Sep 24 '11 at 17:15
  • @Henk Holterman - If I compile both, then delete B.dll, then run A.exe it throws an Exception inside the try block (and again after that in the finalizer) – ordag Sep 24 '11 at 17:18
  • @ordag I'm fully avare of that. But that is how IDisposable types are normally implemented. Furthermore the case is that I have a base class implementing IDisposable like this, and there the pattern must be followed since a subclass can use native resources, and I cant asume otherwise, since the subclass has only Dispose(bool) to override. – m0sa Sep 24 '11 at 17:20
  • @jdv-JandeVaan which tools can I use to accomplish this? – m0sa Sep 24 '11 at 17:22
  • 1
    @m0sa: We use dotfuscator from http://www.preemptive.com/, but there is als also a free tool called ILmerge: http://www.codeproject.com/KB/dotnet/mergingassemblies.aspx –  Sep 24 '11 at 19:03

5 Answers5

10

Why is the finalizer called even though no instance of the class is created?

The question makes no sense. Obviously an instance is created; what would the finalizer be finalizing if there wasn't an instance created? Are you trying to tell us that there's no "this" reference in that finalizer?

the constructor hasn't been called

The constructor can't be called because jitting the constructor references a field whose type is missing. How could a constructor body that can't even be jitted be called?

You seem to think that just because a constructor cannot be called, that an instance cannot be created. That doesn't follow logically at all. Clearly there must be an instance before the ctor is called because a reference to that instance is passed to it as "this". So the memory manager creates an instance - and the garbage collector knows that there's memory allocated - and then it calls the constructor. If calling the constructor throws an exception - or is interupted by an asynchronous exception such as a thread abort - there's still an instance there, known to the garbage collector, and therefore in need of finalization when it is dead.

Since the object will never be assigned to any live variable -- it cannot be, since the assignment happens after the ctor, and the ctor threw when the jitter tried to jit it -- it will be determined to be dead on the next gen zero collection. It will then be put onto the finalizer queue, which will make it alive.

the finalizer is still invoked (on the GC thread), which finally crashes the application.

Then fix the finalizer so that it does not do that.

Remember, the ctor can be interrupted at any time by an asynchronous exception such as a thread abort. You cannot rely on any invariant of an object being maintained in the finalizer. Finalizers are deeply weird code; you should assume that they can run in arbitrary order on arbitrary threads with the object in an arbitrarily bad state. You are required to write extremely defensive code inside a finalizer.

If I set the breakpoint in the Foo constructor (after deleting Bar's dll) it is not hit.

Correct. As I said, the constructor body cannot even be jitted. How could you hit a breakpoint in a method that cannot even be jitted?

This means if I would set have a statement in the constructor that creates a critical resource (before newing up Bar) it wouldn't be executed, hence no need for the finalizer to be called.

Whether or not you think a finalizer needs to be called is completely irrelevant to the garbage collector. The finalizer might have other semantics than merely cleaning up resources. The garbage collector does not attempt to psychically determine the developer's intentions and make decisions about whether it needs to call the finalizer or not. The object was allocated and has a finalizer and you did not suppress finalization on it, so it's gonna be finalized. If you don't like that then don't make a finalizer. You made a finalizer because presumably you wanted all instances of the object to be finalized, and so they're going to be.

Frankly, I would revisit your basic scenario. The idea that you can safely recover and continue to execute code in an appdomain where required DLLs are missing seems like an extremely bad idea to me. Getting this right is going to be very difficult.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • I totally missed the point that this is already available in the ctor. When you put it like this (no pun intended), it absolutely makes sense. – m0sa Oct 06 '11 at 07:19
  • 1
    @m0sa: Glad I could help. Remember, a ctor is *just an instance method*. It's an instance method that is a bit weird -- it is not inherited, has no name, and cannot be called directly by user code -- but it is still ultimately just an instance method. – Eric Lippert Oct 06 '11 at 07:36
  • How come the finalizer can be jitted without Bar's metadata but the constructor can't? – configurator Oct 06 '11 at 14:16
  • @configurator: Isn't the reported issue that the finalizer also throws? I haven't actually tried out the OP's code. To answer the more general question: the jitter typically goes method-by-method. If a method does not reference a field of type X then type X is not loaded by the jitter. – Eric Lippert Oct 06 '11 at 14:36
2

I think the complaint here is that the object is failing to construct, but the finalizer is still invoked and because the finalizer thread is catching the exception, you are prevented from catching it yourself?

This is perfectly legitimate behavior. Suppose the class partially constructed, and had opened some critical resource before it threw? What would happen if the finalizer didn't run? The cases for this are simplified in C#, but in C++ it was the subject of many posts and books (Sutter: Exceptional C++).

SO: Is Finalizer Called if constructor throws (C++/c#)

The answer to the implicit question, how do I handle binding failures for missing/optional assemblies at run time?, is that you don't. The best solution is to poll the directory, load the assemblies manually, and retrieve the contained types based on interfaces exposed by the assembly and your convention.

Screen shot of binding failure from inside the finalizer thread. If you comment out disposing line for Bar, the exception goes away, but the binding failure does not.

enter image description here

Community
  • 1
  • 1
Ritch Melton
  • 11,498
  • 4
  • 41
  • 54
  • But the problem here is that the constructor isn't even called. If I set the breakpoint in the constructor it is not hit. This means if I would set have a statement in the constructor that creates a critical resource (befor newing up Bar) it wouldn't be executed, hence no need for the finalizer to be called. – m0sa Sep 25 '11 at 06:45
  • m0sa - It has nothing to do with Bar's constructor, it's the binding to Bar inside of Foo that is the problem. Foo can't construct because Bar can't be bound by the .net runtime. The finalizer runs and the runtime still can't bind to Bar, hence the exception. Your logic is flawed as you are mucking with assembly binding failures and not normal program flow. – Ritch Melton Sep 25 '11 at 13:40
  • But why does the runtime still call the finalizer after it can't construct an instance? There is no instance that needs finalization... – m0sa Sep 25 '11 at 14:43
  • mOsa _ Its not. Its Foo's finalizer causing the binding lookup for Bar. If you take out the call to Bar in Foo's dispose, the exception goes away, and the binding failure happens in the application thread when Foo fails to construct. – Ritch Melton Sep 25 '11 at 15:06
  • Sure, it's not calling Bar's finalizer, nor it's constructor. But it's not even calling Foo's constructor (set a breakpoint there to see for yourself), but it's still calling Foo's finalizer – m0sa Sep 25 '11 at 15:20
  • m0sa - It is calling Foo's constructor, but it can't bind to Bar. The finalizer runs because Foo tried to construct. The finalizer wants to bind to Bar also, but it fails to do so. No matter what, you will not be able to catch the binding failure in your code and handle it like an exceptional condition. – Ritch Melton Sep 25 '11 at 15:34
  • m0sa - Just for clarity, I'm talking about fusion loading/binding. There are other techniques for loading dlls into your AppDomain. – Ritch Melton Sep 25 '11 at 15:43
  • Yes. But why call the finalizer after failing to bind in a constructor call?? To me it's not logical. Since the runtime knows there was no way for the instance to be created (because the runtime threw the exception on the constructor it should know that), if it needs to clean up some class creation stuff, let it cleanup internally, but don't invoke my finalizer. Sure it tried to invoke the constructor, but it failed, it didn't enter (no "Foo" in console). – m0sa Sep 25 '11 at 15:51
  • It is logical, see my answer. Throwing during object construction is a complicated issue, and the .net runtime doesn't know/keep track of how far Foo's construction went, or what objects were created. If a critical resource was created, and the constructor threw after it was created what would happen? The approach in .net is to run Foo's finalizer to ensure that any critical resources get cleaned up. Get rid of the finalizers and all you will see is binding failures. – Ritch Melton Sep 25 '11 at 16:04
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/3768/discussion-between-ritch-melton-and-m0sa) – Ritch Melton Sep 25 '11 at 16:05
1

You might want to put your Foo class in a third assembly AssemblyC, and then setup an handler for the AppDomain.CurrentDomain.AssemblyResolve event first in your main function. Then attempt to load and execute the Foo class through Reflection; this way, you can check whether the file exists and react properly. After that, the event handler will fire whenever an assembly is missing (starting from the direct dependencies of AssemblyC, which would be the Bar class in this example).

Andrea
  • 884
  • 1
  • 10
  • 23
  • In the real implementation the instance is created via reflection, but the Finalizer is still invoked. – m0sa Sep 24 '11 at 17:16
  • That's a *very* important detail, also missing in the question. – Hans Passant Sep 24 '11 at 17:32
  • 1
    @HansPassant the question is why the finalizer is called in the first place - the example in the code is the most minimalist example that reproduces the exception. – m0sa Sep 24 '11 at 17:35
  • I must admit that I have no clue on why the finalizer is still called - are you already using an AssemblyResolve delegate too? I would expect it to fire when Boo is not found – Andrea Sep 24 '11 at 17:58
  • @Andrea I have not tried yet, but most I certainly will. I would expect the AssemblyResolve handler to be triggered twice - once in the try catch block, and once when the finalizer call happens. – m0sa Sep 24 '11 at 18:37
  • The AssemblyResolve handler is also triggered on the finalizer thread, so graceful exception handling is not possible due to the duration limitation of the finalizer execution time. – m0sa Oct 04 '11 at 11:14
1

It is a guess, but I tried something like this:

public class Foo : IDisposable {
private Bar external;
private bool disposed;

public static Foo CreateFoo() {
    Foo foo = new Foo();
    foo.external = new Bar();
    return foo;
}

private Foo() {
}

~Foo() {
    Console.WriteLine("~Foo");
    this.Dispose(false);
}
public void Dispose() {
    this.Dispose(true);
    GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing) {
    if (disposed) return;
    if (disposing) external.Dispose();
    disposed = true;
}

}

That sample will work as you expect.

And now my guess:

I think that the constructor is not the first method that is called, when an object will be created. It looks like that something out of our control is allocating the space for the created object before constructor is called, or something similar. This is the point where I suppose GC to start working. It is getting the ClassInfo and knows that a finalizer is available. So it starts the Finalizer-Thread and creates a handle to that allocated memory. Know the constructor would be called, and the object would be created from that handle. But before calling a constructor (even a method) something checks for availability of all referenced types within this code block. This is the point where the FileNotFoundException is thrown. Note, that is before you see debugger entering the constructor. Now we move into the Finalizer-Thread, it looks at it's handle of Foo and sees that this handle is not used anymore (actually it was never used). It starts finalizing. This is the point where your finalizer is called. You access the Dispose(bool) method within, and this is the point where the second FileNotFoundException will be thrown, because you are accessing Bar class within that method. That exception will throw on that obscure method check before calling it. I assume that it has something to do with some optimizations, maybe referenced types are lazy loaded.

The strange thing, when the constructor won't throw an exception directly, like in my example, the GC will check this and will not call the finalizer for that object. Maybe I'm wrong and the object is still used and not collected by GC.

Edit:

The best solution would be rather simple. Remove the finalizer from your class. You don't have unmanaged resources in use, so you won't need a finalizer or Dispose(bool) at all.

dwonisch
  • 5,595
  • 2
  • 30
  • 43
  • sure, but what if Foo is an abstract base class (see my remark #3), and part of an API. There a full implementation of the IDisposable pattern is required, since you cannot make any assumption about unmanaged resources usage. – m0sa Oct 04 '11 at 14:25
1

I think you will have to refactor some things. It's difficult for us to know where it will be the most appropriate without being at your seat :-) For example, adding a try catch to all finalizers does not seem a so big deal to me (you can search finalizers using a regex for example), but it may be to you.

The .NET Framework really assumes that the assemblies you reference and the types you use are there at runtime. If you want a more dynamic system, a plugin-type architecture, you need to architect your assembly and your types differently for example using things like the System.Addin namespace or other libraries such as MEF (see this on SO: Choosing between MEF and MAF (System.AddIn))

So, in your case, you could solve the issue in Foo like this:

public class Foo : IDisposable
{
    // use another specific interface here, like some IBar,
    // this is a sample, so I use IDisposable which I know is implemented by Bar
    private readonly IDisposable external;
    public Foo()
    {
        Console.WriteLine("Foo");
        external = Activator.CreateInstance(Type.GetType("AssemblyB.Bar, AssemblyB")) as IDisposable;
    }

    ... same code    
}

But that means refactoring too...

Community
  • 1
  • 1
Simon Mourier
  • 132,049
  • 21
  • 248
  • 298
  • The real system already uses MEF etc. No problem there. As already said, the above example is the smallest example that can reproduce the exception. What bothers me, is that I would expect the runtime to have a check in place, something like the code I have in remark #3. Since the runtime must receive feedback (from fusion) that the constructor cannot be executed, why is it then calling the finalizer when there is no possibility for unmanaged resources to be created?? – m0sa Oct 05 '11 at 06:23
  • @m0sa - You can perfectly create uninitialized (using FormatterServices.GetUninitializedObject for example) objects that still needs to be finalized. – Simon Mourier Oct 05 '11 at 07:13
  • That I did not know. Could you shed some more light onto uninitialized objects? Why are they valid? Where are they needed (except in serialization obviously)? – m0sa Oct 05 '11 at 11:34
  • @m0sa - Absolutely, they are used mostly in serialization: standard .NET serialization (formatters), or WCF serialization (DataContractSerializer). In this case, initialization is done using methods with specific names ("OnDeserializing" for example) if they are defined on the class. – Simon Mourier Oct 05 '11 at 15:43