简体   繁体   中英

How do I use the stack but avoid a stack overflow in C++

I'm presently moving back to C++ from Java. There are some areas of C++ where higher performance can be achieved by doing more computation on the stack.And some recursive algorithms operate more efficiently on the stack than on the heap.

Obviously the stack is a resource, and if I am going to use it, I should ensure that I do not consume too much (to the point of crashing my program).

I'm running Xcode, and wrote the following simple program:

#include <csignal>
static bool interrupted = false;

long stack_test(long limit){
    if((limit>0)&&(interrupted==false))
       return stack_test(limit-1)+1; // program crashes here with EXC_BAD_ACCESS...
    else
        return 0;
}

void signal_handler(int sig){
    interrupted = true;
}

int main(char* args[]){
    signal(SIGSEGV,&signal_handler);
    stack_test(1000000);
    signal(SIGSEGV,SIG_DFL);
}

The documentation states that running on BSD, stack limits can be checked by using getrlimit() and that when the stack limit is being reached, a SIGSEGV event is issued. I tried installing the above event handler for this event, but instead, my program stops at the next iteration with EXT_BAD_ACCESS (code=2, ... ) .

Am I taking the wrong approach here, or is there a better way?

This has the same problem in Java as it does in c++. You are way over-committing to the stack.

And some recursive algorithms operate more efficiently on the stack than on the heap.

Indeed, and they are commonly of the divide and conquer type. The usefulness of recursion is to reduce the computation to a more manageable computation with each call. limit - 1 is not such a candidate.

If your question is only about the signal, I unfortunately can't offer you any advice on your system.

Your signal handler can't do much to fix the stack overflow. Setting your interrupted flag doesn't help. When your signal handler returns, the instruction that tried to write to an address beyond the end of the stack resumes and it's still going to attempt to write beyond the end of the stack. Your code won't get back to the part which checks your interrupted flag.

With great care and a lot of architecture-specific code, your signal handler could potentially change the context of the thread which encountered the signal such that, when it resumes, it will be at a different point in the code.

You could also use setjmp() and longjmp() to accomplish this at a coarser granularity.

A different approach would be to set up a thread to use a stack that your code allocated, using pthread_attr_setstackaddr() and pthread_attr_setstacksize() prior to pthread_create() . You would run your code in that secondary thread and not the main one. You could set the last page or two of the stack you allocated to be non-writable using mprotect() . Then, your signal handler could set the interrupted flag and also set those pages to be writable. That should give you enough headroom that the resumed code can execute without re-raising the signal, get far enough to check the flag, and return gracefully. Note that this is a one-time last resort, unless you can find a good point to set those guard pages non-writable again.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM