2

According to MSDN, AppDomain.Unload causes all threads inside the unloading AppDomain to throw a thread abort exception.

The threads in domain are terminated using the Abort method, which throws a ThreadAbortException in the thread. Although the thread should terminate promptly, it can continue executing for an unpredictable amount of time in a finally clause. -- from MSDN

So my understanding then is that everytime i'm writing code anywhere that is expected to run in this AppDomain, I have to expect that a thread abort could happen on any thread at any time. Is this true? Should all code everywhere assume that a ThreadAbortException can be thrown at any time?

Practically this virtually elimates catch(Exception ex) because that would catch the ThreadAbortException and try to handle it, usually by logging an error that really shouldn't be logged (since unloading of AppDomain isn't really an exception).

Are there any other considerations that need to be taken to avoid unecessary exception handling / error logging?

Mark
  • 5,223
  • 11
  • 51
  • 81
  • I've gone through *lots* of code explicitly "not-caching" ThreadAbort (and even OutOfMemory..). It should be nice if there was a separation of 'system critical' exceptions and otherwise, but there is not :} – user2864740 Jul 15 '18 at 23:48
  • Thankfully, at least ThreadAbort requires an explicit reset to not re-throw (so having it 'caught' is mostly an issue of incorrect logging / invalid recovery / false assumptions of program remaining in a valid state); An OOM can sometimes be smothered until the .NET process 'fatally' crashes :} – user2864740 Jul 15 '18 at 23:55

2 Answers2

5

You pretty much have the expectation correct regarding the possibility of TAEs at any time. The only point I'd make is that your code should probably already be written this way- when handling data that has reliability requirements, you should be using transactions or other compensation mechanisms in cases of hardware failure, operator error, OOMs, etc - any of which are not unique to the scenario of AppDomain shutdown.

Finally, you should know that you can catch TAEs and execute compensatory code in the catch block. The only thing special about them is that they will be immediately rethrown after the catch block, so you can't "swallow" them. You can suppress them by using Thread.ResetAbort(), but in this case that probably isn't the desired effect.

We've all written code like this:

public void Foo() {
    try {
        Do.Some.Stuff();
    } catch (Exception ex) {
       Console.Out.WriteLine("Oh noes!");
    }
}

And yes, the catch block will catch everything1, including TAEs and OOMs.

The thing to keep in mind here is that for all of the above exceptions, the world is basically ending. All you should care about is that whatever transactionally sensitive data that you are dealing with doesn't get lost or left in a bad state, and that's why we generally leave things like transactionally-safe I/O to the smart folks at Microsoft and Oracle who write databases. If your code is doing, for instance, flat file I/O that you need to be really sure never leaves the file in a bad state, then you should be thinking about failure modes in a very holistic way- like "what happens if the power goes out when this file is half-written?"

1 The sole exception is that StackOverflow exceptions generally cannot be caught. This was a change in .NET 2.0.

user2864740
  • 60,010
  • 15
  • 145
  • 220
Chris Shain
  • 50,833
  • 6
  • 93
  • 125
  • 2
    Good answer, probably you should add that: you should implement correctly the IDisposable pattern, including the object finalizer part for **every unmanaged resource** you are using. This is the only way to avoid a system wide lock if, say, you are using a mutex. When a ThreadAbortException is thrown, final blocks and object finalizers run anyway, this is what saves you. Do I sound scared? **I AM** – Maghis Feb 23 '12 at 21:18
  • 1
    One small correction. As far as I understand, you cannot catch `StackOverflowException` on .Net 2 or higher. Try it yourself and check out [MSDN](http://msdn.microsoft.com/en-us/library/system.stackoverflowexception(v=vs.110).aspx) – Pavel Gatilov Aug 25 '14 at 03:40
1

You cannot handle ThreadAbortException so no need to catch the exception. More technically

is a special exception that can be caught, but it will automatically be raised again at the end of the catch block

From here.

Aliostad
  • 80,612
  • 21
  • 160
  • 208
  • Should all code everywhere assume that a ThreadAbortException can be thrown at any time? Also, does this virtually elimates catch(Exception ex) because that will handle the ThreadAbortException? By Handle in my question i didn't mean handle in a try catch but just in general how should we code knowing that threadabortexception can be thrown at any time on any thread – Mark Feb 23 '12 at 15:37
  • 1
    Any work that you do and needs to cleanup after itself must use dispose pattern and possibly transactions. Most codes however would not need that. – Aliostad Feb 23 '12 at 15:43
  • 2
    You can, in effect, catch a `ThreadAbortException`, by issuing a `Thread.ResetAbort()` in the catch block. – Rob Levine Feb 23 '12 at 15:57
  • 1) code *can* catch (even *without* suppressing propagation of a) a thread-abort 2) sometimes [user] thread-aborts are fired outside of an Assembly Unload.. so this answer is patently incorrect, even if one often "shouldn't" from application-level code. In lower-level code (of which there are many examples in the .NET library), there are notable cases of handling async ThreadAbort / OOM exceptions. – user2864740 Jul 15 '18 at 23:49