How to overcome StackOverflowException bypassing unhandled exception handling in .NET
Asked Answered
H

4

14

After hitting a few StackOverflowExceptions in .NET I noticed they completely bypass the unhandled exception handlers that .NET offers (Application.ThreadException / AppDomain.UnhandledException). This is very disturbing since we have critical cleanup code in those exception handlers.

Is there any way to overcome this?

Heckelphone answered 20/9, 2008 at 9:33 Comment(0)
Q
26

There are three kind of so-called "asynchronous exceptions". That are the ThreadAbortException, the OutOfMemoryException and the mentioned StackOverflowException. Those excepions are allowed to occur at any instruction in your code.

And, there's also a way to overcome them:

The easiest is the ThreadAbortException. When the current code executes in a finally-block. ThreadAbortExceptions are kind of "moved" to the end of the finally-block. So everything in a finally-block can't be aborted by a ThreadAbortException.

To avoid an OutOfMemoryException, you have only one possibility: Do not allocate anything on the Heap. This means that you're not allowed to create any new reference-types.

To overcome the StackOverflowException, you need some help from the Framework. This help manifests in Constrained Execution Regions. The required stack is allocated before the actual code is executed and additionally also ensures that the code is already JIT-Compiled and therefor is available for execution.

There are three forms to execute code in Constrained Execution Regions (copied from the BCL Team Blog):

  • ExecuteCodeWithGuaranteedCleanup, a stack-overflow safe form of a try/finally.
  • A try/finally block preceded immediately by a call to RuntimeHelpers.PrepareConstrainedRegions. The try block is not constrained, but all catch, finally, and fault blocks for that try are.
  • As a critical finalizer - any subclass of CriticalFinalizerObject has a finalizer that is eagerly prepared before an instance of the object is allocated.
    • A special case is SafeHandle's ReleaseHandle method, a virtual method that is eagerly prepared before the subclass is allocated, and called from SafeHandle's critical finalizer.

You can find more at these blog posts:

Constrained Execution Regions and other errata [Brian Grunkemeyer] at the BCL Team Blog.

Joe Duffy's Weblog about Atomicity and asynchronous exception failures where he gives a very good overview over asynchronous exceptions and robustness in the .net Framework.

Qualmish answered 20/9, 2008 at 12:33 Comment(1)
Note that not all the necessary stack space if pre-allocated, only something like 12 pages (48K or 96K). If the critical code needs more space it will fail and the CER won't be of any help. A CER only raises a little the guarantee of execution, it does not provide a 100% guarantee.Bumgarner
Y
18

Not really; a stack overflow (or "out of memory") exception happens within the CLR itself means something has gone critically wrong (I usually get it when I've been dumb and created a recursive property).

When this state occurs there is no way for the CLR to allocate new function calls or memory to enable it to call into the exception handlers; it's a "we must halt now" scenario.

If, however, you throw the exception yourself, your exception handlers will be called.

York answered 20/9, 2008 at 9:37 Comment(3)
I find it somewhat surprising, after all the clr is microsoft owned code, they could have spared a few stack frames to allow exception handling.Heckelphone
How would it know how much to reserve? In a stack overflow situation the stack itself has grown to such a size that it's not handleable. By reserving some space you'd need to reserve the maximum stack size, which would in turn half the stack available to software.York
Ok, I do get this idea... Kind of... In a single-threaded environment. But hey, we could create more than one thread, can't we? And every thread has a separate stack, doesn't it? Then why terminate the whole process when just one thread ran out of stack?Incompetence
H
1

A stackoverflow isn't something you can just recover from, since it can't allocate more stack memory to even call your exception handler.

The only thing you can really do is track down the cause and prevent it from happening at all (eg becareful with recursion, and don't allocate large objects on the stack).

Harrington answered 20/9, 2008 at 9:41 Comment(0)
E
0

blowdart nailed it. Really just a problem with typing code too quickly.

private Thing _myThing = null;

Public Thing MyThing
{
   get{
        return this.MyThing;}
   set{
        this.MyThing = value;}
}
Evocative answered 1/6, 2010 at 19:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.