views:

673

answers:

11

Is it possible to catch a stack overflow exception in a recursive C++ function? If so, how?

so what will happen in this case

void doWork()
{

     try() {

     doWork();    
     }


     catch( ... )  {

     doWork();    
     }
}

I am not looking for an answer to specific OS. Just in general

A: 

This is done all the time by most modern operating systems. If you want to do it on your own, you'll have to know the maximum "safe" address for your stack (or likewise do some math to determine how many times you can safely call the function), but this can get very tricky if you aren't managing the call stack yourself, since the OS will usually (for good reason) be hiding this from you.

If you are programming in kernel space, this gets significantly easier, but still something I question why you're doing. If you have a stack overflow, it's probably because of a bad algorithmic decision or else an error in the code.

edit: just realized you want to "catch the exception" that results. I don't think my answer directly answers that at all (does this exception even exist? i would figure instead on a spectacular failure), but I'll leave it up for insight. If you want it removed, please let me know in the comments and I will do so.

San Jacinto
guess i should make mention: i assumed that the stack on your system grows "down." The same logic applies if it is the reverse, though. Just reverse the comparison operator :)
San Jacinto
+1  A: 

I doubt so, when stack got overflow the program will not be able even to handle exception. Normally OS will close such program and report the error.
This happens mostly because of infinite recursions.

Andrejs Cainikovs
"Handle" isn't the same as "catch." If the environment provides an exception, you should be able to catch it. That doesn't mean you can *recover* from it, though. Catch it, log it, and exit.
Rob Kennedy
Right, thank you.
Andrejs Cainikovs
+2  A: 

On what OS? Just for example, you can do it on Windows using Structured Exception Handling (or Vectored Exception Handling). Normally you can't do it with native C++ exception handling though, if that's what you're after.

Edit: Microsoft C++ can turn a structured exception into a C++ exception. That was enabled by default in VC++ 6. It doesn't happen by default with newer compilers, but I'm pretty sure with a bit of spelunking, you could turn it back on.

It's true that when this happens, you're out of stack space. That's part of why I mentioned vectored exception handling. Each thread gets its own stack, and a vectored exception handler can run in a separate thread from where the exception was thrown. Even SEH, however, you can handle a stack overflow exception -- it just has to manually spawn a thread to do most of the work.

Jerry Coffin
"Normally" you can't? When *can* you catch a stack overflow with native C++ exception handling?
Rob Kennedy
Depends on what you mean by "native C++", I guess. Either the C++ that you've got on the machine, or standard C++.
David Thornley
Its not entirely that easy with windows either ... because once you've trapped it you need to remember you don't have any stack space ;)
Goz
Sorry -- in "native C++ exception", "native" was meant to modify "exception", not "C++". As in, "an exception that's native to C++".
Jerry Coffin
@Goz, that depends on how the exception is propagated. If the stack is unwound as the exception is thrown and the "does this handler match?" code is run, then you very well may have enough stack space to actually run your handler. Even in that case, it will depend on how much stack destructors chew up. Of course you said "on windows", so that implies certain things about how this scenario plays out.
Logan Capaldo
+8  A: 

There's really no portable way to do it. An out of control recursive function will usually cause an invalid memory access when it tries to allocate a stack frame beyond the stack address space. This will usually just crash your program with a Segmentation Fault/Access Violation depending on the OS. In other words, it won't throw a c++ exception that can be handled in a standard way by the language.

Charles Salvia
I take two issues with this: 1) it doesn't ALWAYS matter if it's portable. 2) a stack overflow can occur more often than just being "out of control." Neither is worth a downvote, but I don't know that this answer is as helpful as the votes indicate (not that mine is any better).
San Jacinto
+1  A: 

In Windows you can use structured exception handling (SEH), with __try and __except keywords to install your own exception handler routine that can catch stack overflows, access violation, etc etc.

It's pretty neat to avoid Windows' default crash dialog, and replace it with your own, if you need to.

Marcus Lindblom
be careful about using this approach to "handle" the exception by keeping going as if the fault didn't happen. by doing that you invite a world of pain.
asveikau
True. Should've pointed that out. I've always terminated afterwards anyway.
Marcus Lindblom
A: 

You have to know always a level of your recursion and check it if greater than some threshold. Max level (threshold) is calclulated by ratio of stack size divided by the memory required one recursive call.

The memory required one recursive call is the memory for all arguments of the function plus the memory for all local variables plus the memory for return address + some bytes (about 4-8).

Alexey Malistov
A: 

Of course, you could avoid the recursion problem by converting it to a loop.

Not sure if you're aware of this but any recursive solution can be translated to a loop-based solution, and vice-versa. It is usually desirable to use a loop based solution because it is easier to read and understand.

Regardless of use of recursion or loop, you need to make sure the exit-condition is well defined and will always be hit.

SteveV
I strongly disagree with your second paragraph. Very frequently, the recursive solution is easier to read and understand, while the loop-based solution needs stack handling that complicates things. Traversing a binary tree is simple recursively (process left_child; process right_child;), and is harder to express iteratively. (Unfortunately, recursion is usually demonstrated on toy problems, like calculating factorials, that are better off done in a loop.)
David Thornley
While it's true that any recursive formula can be converted into a series of loops, that does *not* mean it's an easy thing to do. Take a look at the Ackermann function, for example.
Loadmaster
+1  A: 

There isn't a portable way. However, there are a few nonportable solutions.

First, as others have mentioned, Windows provides a nonstandard __try and __except framework called Structured Exeption Handling (your specific answer is in the Knowledge Base).

Second, alloca -- if implemented correctly -- can tell you if the stack is about to overflow:

bool probe_stack(size_t needed_stack_frame_size)
{
    return NULL != alloca(needed_stack_frame_size);
};

I like this approach, because at the end of probe_stack, the memory alloca allocated is released and available for your use. Unfortunately only a few operating systems implement alloca correctly. alloca never returns NULL on most operating systems, letting you discover that the stack has overflowed the old fashioned way: with a spectacular crash.

Third, UNIX-like systems often have a header called ucontext.h with functions to set the size of the stack (or, actually, to chain several stacks together). You can keep track of where you are on the stack, and determine if you're about to overflow. Windows comes with similar abilities a la CreateFiber.

Max Lybbert
I assume that shutting down the program isn't accomplished through a signal that you could catch and recover from gracefully, but rather to be safe is more of a: "dah!! what are you doing!? stop! stop!" This is probably something I can look up later, but since you're here I can ask (in a SO question, if you wish): what happens to the open resources I have when such an event happens to my process?
San Jacinto
I really don't know what happens, although I doubt destructors are run. There is a __finally construct that you can use, a la Java.
Max Lybbert
+5  A: 

Even if you can do this non-portably, as you can in Windows, it's still a very bad idea. The best strategy is to not overflow the stack in the first place. If you need isolation from some code you don't control, run that code in a different process and you can detect when it crashes. But you don't want to do that sort of thing in your own process, because you don't know what sort of nasty corruption of state the offending code is going to do, and that will make you unstable.

There's an interesting, somewhat related blog post by Microsoft's Raymond Chen about why you shouldn't try to check for valid pointers in a user mode application on Windows.

asveikau
awesome point/idea about running stack-instensive stuff in a different process. this is probably the best post i've seen so far on this topic.
San Jacinto
+3  A: 

It's not an exception per se, but if you just want to be able to limit your stack usage to a fixed amount, you could do something like this:

#include <stdio.h>

// These will be set at the top of main()
static char * _topOfStack;
static int _maxAllowedStackUsage;

int GetCurrentStackSize()
{
   char localVar;
   int curStackSize = (&localVar)-_topOfStack;
   if (curStackSize < 0) curStackSize = -curStackSize;  // in case the stack is growing down
   return curStackSize;
}

void MyRecursiveFunction()
{
   int curStackSize = GetCurrentStackSize();
   printf("MyRecursiveFunction:  curStackSize=%i\n", curStackSize);

   if (curStackSize < _maxAllowedStackUsage) MyRecursiveFunction();
   else
   {
      printf("    Can't recurse any more, the stack is too big!\n");
   }
}

int main(int, char **)
{
   char topOfStack;
   _topOfStack = &topOfStack;
   _maxAllowedStackUsage = 4096;  // or whatever amount you feel comfortable allowing

   MyRecursiveFunction();
   return 0;
}
Jeremy Friesner
Clever :) (15 chars)
cwap
A: 
If you use Visual C++
Goto C/C++ , Code Generation
Choose "Both..." in "Basic Runtime Checks"

Then, run your application...

Malkocoglu