4

My question is about throw and exception bubbling. I was searching around about file locking and C# and I tried messing around with someone's code which made me question how much I understood about throw and exception bubbling.

Here is the link to the thread.

public class FileManager
{
private string _fileName;
private int _numberOfTries;

private int _timeIntervalBetweenTries;

private FileStream GetStream(FileAccess fileAccess)
{
    var tries = 0;
    while (true)
    {
        try
        {
            return File.Open(_fileName, FileMode.Open, fileAccess, Fileshare.None); 
        }
        catch (IOException e)
        {
            if (!IsFileLocked(e))
                throw;
            if (++tries > _numberOfTries)
                throw new MyCustomException("The file is locked too long: " + e.Message, e);
            Thread.Sleep(_timeIntervalBetweenTries);
        }
    }
}

private static bool IsFileLocked(IOException exception)
{
    int errorCode = Marshal.GetHRForException(exception) & ((1 << 16) - 1);
    return errorCode == 32 || errorCode == 33;
}

// other code

}

In the code above, if IsFileLocked returns false, throw rethrows an IOException. To my understanding, I thought that now that IOException would bubble up the stack trace. This means, to me at least, that when that throw occurs, it should caught again by the catch(IOException e) again (the one obviously in the GetStream method). After all, the stacktrace would be something like:

GetStream (handling IOExceptions) IsFileLocked throw Exception would be handled in GetStream

However, when I run this, it seems to not bother bubbling up but rather halts my application. Am I missing something here?

Side note: Just so I get the difference between throw and throw ex, if throw ex had been used, the exception would have been shown to have originated from GetStream and then bubble up to no where right? I know there is the same question on here, I just want to reaffirm to myself in this example that this is true.

Thanks

Community
  • 1
  • 1
Ilya
  • 1,215
  • 1
  • 14
  • 25
  • Try to reason how your version of what happens would not cause an endless loop. And what the caller of this method could do to break that loop. – Hans Passant Nov 01 '10 at 00:33
  • Not directly applicable to the question, but a comment pertinent to the code posted in the question: As of .NET 4.5 (released in 2012), the `Exception.HResult` property's getter is now public and can be used directly. In addition, the call to `GetHRForException()` has side-effects that are generally not desirable, so that method should be avoided (see https://blogs.msdn.microsoft.com/yizhang/2013/01/18/marshal-gethrforexception-does-more-than-just-get-hr-for-exception/) – Peter Duniho Mar 13 '19 at 01:07

2 Answers2

10

An exception thrown (or rethrown) from a catch block is not handled by that same catch block, but by next exception handler up the chain. If there is no other handler (and there is no global exception handler in place to gracefully handle the exception), the application will terminate.

Regarding your side note, throw preserves the call stack on the exception, but an exception rethrown by throw ex will appear to have originated in GetStream().

Phil Hunt
  • 8,404
  • 1
  • 30
  • 25
  • In the example above then I could put a catch where I initialized my FileManager object and it would be caught, cool, get that. However, what is the purpose then of the while loop here? So you throw another IOException, it is caught below where you initialize the FileManager object and then what? Aren't you now distanced away from GetStream method so now you don't even have you tries variable on the stack anymore? – Ilya Nov 01 '10 at 01:49
  • Great question. The purpose of the `!IsFileLocked()` call is to throw the exception up the chain if the exception is due to something other than a lock on the file in question. If it is due to the file being locked (but it has not exceeded the predetermined attempt limit), then the exception is swallowed (by virtue of not being rethrown) and thread sleeps. Execution then resumes at the top of the `while`, and it attempts to open the file again. Once it has caught the exception more times than the specified limit, a different exception is thrown indicating this condition. Hope this helps! – Phil Hunt Nov 01 '10 at 01:55
1

A plain throw in a catch will not be caught there again. Only exception raised from the try block will be caught. Your exception will continue up the call stack.

You are right that the stack trace might be different for throw and throw ex, but the stack trace is not what determines where the exception is caught, the throw site is.

Ned Batchelder
  • 364,293
  • 75
  • 561
  • 662