11

Apparently, some exceptions may just get lost while using nested using statement. Consider this simple console app:

using System;

namespace ConsoleApplication
{
    public class Throwing: IDisposable
    {
        int n;

        public Throwing(int n)
        {
            this.n = n;
        }

        public void Dispose()
        {
            var e = new ApplicationException(String.Format("Throwing({0})", this.n));
            Console.WriteLine("Throw: {0}", e.Message);
            throw e;
        }
    }

    class Program
    {
        static void DoWork()
        {
            // ... 
            using (var a = new Throwing(1))
            {
                // ... 
                using (var b = new Throwing(2))
                {
                    // ... 
                    using (var c = new Throwing(3))
                    {
                        // ... 
                    }
                }
            }
        }

        static void Main(string[] args)
        {
            AppDomain.CurrentDomain.UnhandledException += (sender, e) =>
            {
                // this doesn't get called
                Console.WriteLine("UnhandledException:", e.ExceptionObject.ToString());
            };

            try
            {
                DoWork();
            }
            catch (Exception e)
            {
                // this handles Throwing(1) only
                Console.WriteLine("Handle: {0}", e.Message);
            }

            Console.ReadLine();
        }
    }
}

Each instance of Throwing throws when it gets disposed of. AppDomain.CurrentDomain.UnhandledException never gets called.

The output:

Throw: Throwing(3)
Throw: Throwing(2)
Throw: Throwing(1)
Handle: Throwing(1)

I prefer to at least be able to log the missing Throwing(2) and Throwing(3). How do I do this, without resorting to a separate try/catch for each using (which would kinda kill the convenience of using)?

In real life, those objects are often instances of classes over which I have no control. They may or may not be throwing, but in case they do, I'd like to have an option to observe such exceptions.

This question came along while I was looking at reducing the level of nested using. There's a neat answer suggesting aggregating exceptions. It's interesting how this is different from the standard behavior of nested using statements.

[EDITED] This question appears to be closely related: Should you implement IDisposable.Dispose() so that it never throws?

Community
  • 1
  • 1
noseratio
  • 59,932
  • 34
  • 208
  • 486
  • 1
    I don't suppose it's possible to alter your Dispose so it [doesn't throw an exception](http://msdn.microsoft.com/en-us/library/bb386039.aspx), eh? I definitely see why it's happening, and I wouldn't call them "lost" necessarily (I see it as similar to catching an exception, then throwing a new exception in the catch block). EDIT: Maybe I should ask, must you catch them with your `AppDomain.CurrentDomain.UnhandledException` handler, or is another mechanism acceptable? – Chris Sinclair Oct 08 '13 at 03:45
  • In real life, those objects are often instances of classes over which I have no control. They may or may not be throwing, but in case they do, I'd like to have an option to log those exceptions. And `AppDomain.CurrentDomain.UnhandledException` doesn't catch them. – noseratio Oct 08 '13 at 03:51
  • Then can you wrap your (I'm guessing repeated) usage of nested `using` with a single object that can add the three levels of `try/catch` and thus abstract/simplify its usage? – Chris Sinclair Oct 08 '13 at 03:53
  • 1
    So, decompiling this shows that `leave.s` is placed at the end of the `try` blocks generated by the `using` statement. The documentation states that this operator "unconditionally" transfers control to the label specified AFTER it executes the finally blocks. So, it would appear that the exception is indeed "lost".. because control is "unconditionally" transferred after the finally blocks are executed. (control is transferred to after the finally block). – Simon Whitehead Oct 08 '13 at 03:53
  • @SimonWhitehead: The way I see it, when it disposes `c`, it throws an exception. This causes it to leave the `using` block for `b`, thus calling `b.Dispose`. This in turn throws a new exception causing it to leave the `using` block for `c`, thus calling `c.Dispose` which throws its own new exception. I see this functionally not that much different than `try { throw new Exception(); } catch (Exception ex) { throw new Exception() }` whereby `ex` is not exactly "lost" but thrown away. I guess in this context, it might be considered "lost" only because the programmer has _chosen_ not to catch it. – Chris Sinclair Oct 08 '13 at 03:56
  • 1
    @Noseratio: `UnhandledException` doesn't catch them because they're not unhandled. Rather, they're swallowed by more indirect means. – Chris Sinclair Oct 08 '13 at 03:58
  • 2
    @Noseratio: Are you willing to wrap your disposable usages with a managed disposable wrapper, like `LoggedDisposable`? You might use it like `using(var loggedDisposable = new LoggedDisposable(() => new Throwing(1)) { var myThrowing = loggedDisposable.WrappedDisposable; ... }` wherein your `LoggedDisposable.Dispose` method you have a `try/catch` log block that wraps a `WrappedDisposable.Dispose` call? – Chris Sinclair Oct 08 '13 at 04:04
  • @ChrisSinclair, `LoggedDisposable` certainly looks like an option. The question is, is it any better than `DisposableList` from [here](http://stackoverflow.com/a/19218822/1768303)? – noseratio Oct 08 '13 at 04:14
  • @Noseratio: Not necessarily; probably the list is better just out of more direct typing and less accessing of wrappers. Honestly, I would rather abstract the nested usings away anyway to a single managed object. But that's easy for me to say not having seen your actual usage, code base, and how frequently it's used or modified. – Chris Sinclair Oct 08 '13 at 04:18
  • 1
    (I botched my above comment, I'll fix the significant part of it here for posterity since it's well past the edit window and the comment as-is is pretty incorrect) _The way I see it, when it disposes `c`, it throws an exception. This causes it to leave the using block for `b`, thus calling `b.Dispose`. This in turn throws a new exception causing it to leave the using block for `a`, thus calling `a.Dispose` which throws its own new exception..._ – Chris Sinclair Oct 08 '13 at 04:21
  • @ChrisSinclair, feel free to post your idea about `LoggedDisposable` as an answer, I'd +1 it. It does allow logging those exceptions, at least. Let's see what others may think about it. – noseratio Oct 08 '13 at 04:47

3 Answers3

23

There's a code analyzer warning for this. CA1065, "Do not raise exceptions in unexpected locations". The Dispose() method is on that list. Also a strong warning in the Framework Design Guide, chapter 9.4.1:

AVOID throwing an exception from within Dispose(bool) except under critical situations where the containing process has been corrupted (leaks, inconsistent shared state, etc.).

This goes wrong because the using statement calls Dispose() inside a finally block. An exception raised in a finally block can have an unpleasant side-effect, it replaces an active exception if the finally block was called while the stack is being unwound because of an exception. Exactly what you see happening here.

Repro code:

class Program {
    static void Main(string[] args) {
        try {
            try {
                throw new Exception("You won't see this");
            }
            finally {
                throw new Exception("You'll see this");
            }
        }
        catch (Exception ex) {
            Console.WriteLine(ex.Message);
        }
        Console.ReadLine();
    }
}
Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • Hans: is this because of the `leave.s` operand as I suspected in the comments above? – Simon Whitehead Oct 08 '13 at 04:22
  • The CLR does this. It must choose between a rock and a hard place, only one of these exceptions can be propagated. It picks the rock. That choice is somewhat consistent with the exception you see when a catch block throws, last one counts. – Hans Passant Oct 08 '13 at 04:34
  • @HansPassant, so basically, I should leave that to designers of any 3rd party library I might be using, and just go on with nested `using` (as I always did)? – noseratio Oct 08 '13 at 04:35
  • No idea what you are asking. If you are implying that 3rd party programmers can get this wrong and there isn't anything you can do about it, then no, you can give them a call. – Hans Passant Oct 08 '13 at 04:37
  • 1
    I'll clarify: I may **not even know an exception was thrown by a `Dispose` in a 3rd party library** (or even in the code by my colleagues), because it's swallowed like this (unless I enable 1st chance exceptions in the debugger). I don't like this; I would rather use a wrapper like `LoggedDisposable` by @ChrisSinclair in the comments above, or `DisposableList` from [here](http://stackoverflow.com/a/19218822/1768303), which aggregates such exceptions. – noseratio Oct 08 '13 at 04:45
  • 7
    Hmm, no, you *always* get an exception. That it isn't the one you hoped for doesn't eliminate the fact that two things went drastically wrong in the program. Avoid picking a favorite, crap + crap is still crap. – Hans Passant Oct 08 '13 at 05:05
  • 1
    @HansPassant: Some exceptions represent things that go "drastically" wrong. Others represent things which failed in anticipated fashion. If something goes majorly wrong in `Dispose` (e.g. an attempt to write a file fails), that will *often* be more important than anything which happened in the guarded block, but the information contained in that exception will often be less informative. The proper thing would be to throw a `DisposeFailureException` which encapsulates all the others, but that's unfortunately rather difficult to do. – supercat Oct 08 '13 at 23:43
  • @HansPassant, you've provided a great explanation on why it is happening, but could you elaborate on how to address the problem itself, beyond using the code analyzer? Does aggregating exceptions like [this](http://stackoverflow.com/a/19240514/1768303) make sense? – noseratio Oct 09 '13 at 02:11
  • Use the exception info to get the bug fixed. Rinse and repeat until the program is stable. – Hans Passant Oct 09 '13 at 02:16
  • @HansPassant, so far I know the only stable way of doing "rinse and repeat": wrap each `Dispose` in its own `try/catch` and deal with each possibly thrown exception individually. Which eliminates the usefulness of `using`, unless I'm still missing something. – noseratio Oct 09 '13 at 04:46
  • Sounds to me you are making the classic mistake of thinking that you can actually handle this exception. Of course you can't, it is a bug. You can't handle a bug, you can't rewrite code in your catch block. – Hans Passant Oct 09 '13 at 09:36
  • Pardon my persistence, but what bug are you referring to? E.g., `Dispose` for `Stream` may throw because there is no disk space when it's flushing the buffer as it's closing, is this a bug? I could not put my point better than @supercat did above in the comments. – noseratio Oct 09 '13 at 12:55
  • Bad example, that exception will be raised anyway, you just got it a little sooner. It completely doesn't change the outcome, the user is still going to have to free up disk space and that's not something you can do. I'm getting worn out by this question, why don't you ask supercat to post an answer? Bye. – Hans Passant Oct 09 '13 at 13:03
3

Maybe some helper function that let you write code similar to using:

 void UsingAndLog<T>(Func<T> creator, Action<T> action) where T:IDisposabe
 {  
      T item = creator();
      try 
      {
         action(item);
      }
      finally
      { 
          try { item.Dispose();}
          catch(Exception ex)
          {
             // Log/pick which one to throw.
          } 
      }      
 }

 UsingAndLog(() => new FileStream(...), item => 
 {
     //code that you'd write inside using 
     item.Write(...);
 });

Note that I'd probably not go this route and just let exceptions from Dispose to overwrite my exceptions from code inside normal using. If library throws from Dispose against strong recommendations not to do so there is a very good chance that it is not the only issue and usefulness of such library need to be reconsidered.

Alexei Levenkov
  • 98,904
  • 14
  • 127
  • 179
  • If a class which encapsulates a file or stream buffers data and writes it on `Dispose`, what *should* the `Dispose` method do if the file can't be written? If there were some way to throw on `Dispose` if the application has called `Close` *and there isn't a pending exception*, that might be the most sensible approach, but no standard pattern exists to do that cleanly. I'm not sure if throwing on `Dispose` can be termed a "defect" unless there's some alternative which would be better. – supercat Oct 10 '13 at 01:31
  • @supercat If there was an exception in the using block, the buffer probably does not contain what you think and your file would be corrupt anyway. Why bother trying to write a corrupt file? On the other hand, if the application has closed the file without exception (i.e. this is what you want to happen), then why would the dispose have to throw? It doesn sound so sensible to me... – Kris Vandermotten Oct 11 '13 at 07:37
  • @KrisVandermotten: I meant "throw if the application *hasn't* called `Close`." As for "the file being corrupt anyway", that's hardly a given. Often if a data acquisition and logging program throws an exception while acquiring data, data which wasn't given to the logging code to the exception won't be available, but the logging code should try to finish writing out everything that was available. As for `Dispose` throwing if executed "normally", the idea is that if the only time `Dispose` is called without `Close` is when there's a pending exception, then `Dispose` could figure... – supercat Oct 11 '13 at 15:28
  • ...that even if it stifles exceptions that occur when writing the file, the application would know that "something" is wrong (i.e. whatever exception caused the `Close` to not happen). Such a mechanism won't work, however, if the programmer simply forgets to call `Close` without `Dispose`. If that happens, the class should do something to "set of an alarm" to let the programmer know the problem needs to be fixed. – supercat Oct 11 '13 at 15:33
  • @supercat "Setting of an alarm" can be done easily by implementing a finalizer. But do know that this will significantly impact garbage collection. – Kris Vandermotten Oct 14 '13 at 07:25
  • @KrisVandermotten: The performance impact of the finalizer itself should be slight; a bigger problem is that finding out that something sometime created an object and didn't clean it up isn't nearly as helpful as would be an exception at the point where, in the absence of other exceptions, `Dispose` was called on an object that hadn't been closed. – supercat Oct 14 '13 at 15:14
  • @supercat With a finalizer, you can know if there is a problem that needs investigating or not. That sounds pretty helpfull to me, even if it doesn't do the investigation for you. That being said, I start to have some doubts about your idea of what Dispose should be doing. Take a look at http://blogs.msdn.com/b/kimhamil/archive/2008/03/15/the-often-non-difference-between-close-and-dispose.aspx for more information. – Kris Vandermotten Oct 14 '13 at 15:46
  • @KrisVandermotten: Fundamentally, if an object encapsulates a file or stream, and it buffers data for writing, there's no way to avoid the possibility of the object accepting data (without throwing an exception) but later finding itself unable to actually write it. If code which has written to the file calls `Close`, and `Close` discovers that the data can't be written, then having `Close` throw an exception is almost certainly the best way to indicate the problem since it cannot satisfy its post-condition. Having the application call `Close` before calling `Dispose` is thus a good pattern. – supercat Oct 14 '13 at 16:13
  • @supercat - note that `Close` (or `Flush`) before end of `using` block (or explicit call to `Dispose`) requires 2-3 lines of comments explaining why it is so important in that particular case because it against default pattern of `using`. Note that I'm not arguing if it is good or bad pattern - just your suggestions will **look** wrong in the code (which likely leads to someone "fixing code" by removing the calls later). – Alexei Levenkov Oct 14 '13 at 16:21
  • @KrisVandermotten: The problem with such a pattern is that there's no nice way to enforce it. If calling `Dispose` on an open file with no exception pending could throw an exception with a `Proper usage requires Close() before Disposal` message, anyone who forgot to call `Close` would quickly find out and know exactly where the problem was. Getting a message `Someone, somewhere, disposed a file without calling Close()` would be far less helpful. What's really needed is to have a `finally(Exception ex)` block and have `using` check for and use an `IDisposableEx` interface. – supercat Oct 14 '13 at 16:21
  • @AlexeiLevenkov: I think if the `Close` was wrapped within a `try` block, it would be pretty clear why it was done separately. I agree with your point; as I said, the fundamental problem would best be fixed by having language designers agree to an alternate interface for `using` which would let the `IDisposable` object know about pending exceptions. Among other things, it would make it easier for objects to enforce certain invariants even in the presence of exceptions. If an unexpected exception occurs while a write lock is thrown, the proper behavior is often not to release the lock, but... – supercat Oct 14 '13 at 16:29
  • ...to expressly invalidate it. If a language makes it more convenient to release the lock in such cases, programmers are apt to do so whether it's safe or not. By contrast, if the language makes it easy for programmers can say whether the lock should be released or invalidated, programmers are more apt to do the right thing. – supercat Oct 14 '13 at 16:33
3

What you are noticing is a fundamental problem in the design of Dispose and using, for which no nice solution as yet exists. IMHO the best design would be to have a version of Dispose which receives as an argument any exception which may be pending (or null, if none is pending), and can either log or encapsulate that exception if it needs to throw one of its own. Otherwise, if you have control of both the code which could cause an exception within the using as well as within the Dispose, you may be able to use some sort of outside data channel to let the Dispose know about the inner exception, but that's rather hokey.

It's too bad there's no proper language support for code associated with a finally block (either explicitly, or implicitly via using) to know whether the associated try completed properly and if not, what went wrong. The notion that Dispose should silently fail is IMHO very dangerous and wrongheaded. If an object encapsulates a file which is open for writing, and Dispose closes the file (a common pattern) and the data cannot be written, having the Dispose call return normally would lead the calling code to believe the data was written correctly, potentially allowing it to overwrite the only good backup. Further, if files are supposed to be closed explicitly and calling Dispose without closing a file should be considered an error, that would imply that Dispose should throw an exception if the guarded block would otherwise complete normally, but if the guarded block fails to call Close because an exception occurred first, having Dispose throw an exception would be very unhelpful.

If performance isn't critical, you could write a wrapper method in VB.NET which would accept two delegates (of types Action and an Action<Exception>), call the first within a try block, and then call the second in a finally block with the exception that occurred in the try block (if any). If the wrapper method was written in VB.NET, it could discover and report the exception that occurred without having to catch and rethrow it. Other patterns would be possible as well. Most usages of the wrapper would involve closures, which are icky, but the wrapper could at least achieve proper semantics.

An alternative wrapper design which would avoid closures, but would require that clients use it correctly and would provide little protection against incorrect usage would have a usage batter like:

var dispRes = new DisposeResult();
... 
try
{
  .. the following could be in some nested routine which took dispRes as a parameter
  using (dispWrap = new DisposeWrap(dispRes, ... other disposable resources)
  {
    ...
  }
}
catch (...)
{
}
finally
{
}
if (dispRes.Exception != null)
  ... handle cleanup failures here

The problem with this approach is that there's no way to ensure that anyone will ever evaluate dispRes.Exception. One could use a finalizer to log cases where dispRes gets abandoned without ever having been examined, but there would be no way to distinguish cases where that occurred because an exception kicked code out beyond the if test, or because the programmer simply forgot the check.

PS--Another case where Dispose really should know whether exceptions occur is when IDisposable objects are used to wrap locks or other scopes where an object's invariants may temporarily be invalidated but are expected to be restored before code leaves the scope. If an exception occurs, code should often have no expectation of resolving the exception, but should nonetheless take action based upon it, leaving the lock neither held nor released but rather invalidated, so that any present or future attempt to acquire it will throw an exception. If there are no future attempts to acquire the lock or other resource, the fact that it is invalid should not disrupt system operation. If the resource is critically necessary to some part of the program, invalidating it will cause that part of the program to die while minimizing the damage it does to anything else. The only way I know to really implement this case with nice semantics is to use icky closures. Otherwise, the only alternative is to require explicit invalidate/validate calls and hope that any return statements within the part of the code where the resource is invalid are preceded by calls to validate.

supercat
  • 77,689
  • 9
  • 166
  • 211
  • I think, this is the most flexible way of dealing with exceptions, possibly thrown within `Dispose` when it is automatically called as part of `using` pattern. – noseratio Oct 10 '13 at 10:41
  • 1
    @Noseratio: My biggest complaint with this approach is that programming languages offer no assistance to ensure that the code which checks for cleanup failures gets invoked even if a `return` statement is executed within the `try` block. A `try`/`finally` would cause the code to be executed in that case, but also if an exception occurs (which is *not* wanted), but no other construct will execute even if there's a return. – supercat Oct 10 '13 at 14:58