What is stack unwinding?
Asked Answered
V

11

262

What is stack unwinding? Searched through but couldn't find enlightening answer!

Versatile answered 25/2, 2010 at 3:4 Comment(3)
If he doesn't know what it is, how can you expect him to know they are not the same for C and for C++?Lorilee
@dreamlax: So, how concept of "stack unwinding" is different in C & C++?Thermomotor
@PravasiMeet: C has no exception handling, so stack unwinding is very straightfoward, however, in C++, if an exception is thrown or a function exits, stack unwinding involves destructing any C++ objects with automatic storage duration.Lorilee
R
195

Stack unwinding is usually talked about in connection with exception handling. Here's an example:

void func( int x )
{
    char* pleak = new char[1024]; // might be lost => memory leak
    std::string s( "hello world" ); // will be properly destructed

    if ( x ) throw std::runtime_error( "boom" );

    delete [] pleak; // will only get here if x == 0. if x!=0, throw exception
}

int main()
{
    try
    {
        func( 10 );
    }
    catch ( const std::exception& e )
    {
        return 1;
    }

    return 0;
}

Here memory allocated for pleak will be lost if an exception is thrown, while memory allocated to s will be properly released by std::string destructor in any case. The objects allocated on the stack are "unwound" when the scope is exited (here the scope is of the function func.) This is done by the compiler inserting calls to destructors of automatic (stack) variables.

Now this is a very powerful concept leading to the technique called RAII, that is Resource Acquisition Is Initialization, that helps us manage resources like memory, database connections, open file descriptors, etc. in C++.

Now that allows us to provide exception safety guarantees.

Regnant answered 25/2, 2010 at 3:27 Comment(7)
That was really enlightening! So I get this: if my process is crashed unexpectedly during leaving ANY block at which time stack was being popped then it might happen that the code after exception handler code, is not going to be executed at all, and it may cause memory leaks, heap corruption etc.Versatile
Hmm, crash is a little bit different, though related, animal (signals, core dumps, etc.) What I'm talking about are C++ tools/facilities to safely manage resources in the presence of exceptions/errors, that might lead to a crash if not handled.Regnant
If the program "crashes" (i.e. terminates due to an error), then any memory leakage or heap corruption is irrelevant since the memory is released at termination.Mcmurry
Exactly. Thanks. I'm just being a bit dyslexic today.Regnant
@TylerMcHenry: The standard does not guarantee that resources or memory are released at termination. Most OS's happen to do so however.Furman
delete [] pleak; is only reached if x == 0.Bombay
@TylerMcHenry, Not completely. The process may leak resources which are system/session global (e.g. ATOMs on Windows).Fanjet
B
101

All this relates to C++:

Definition: As you create objects statically (on the stack as opposed to allocating them in the heap memory) and perform function calls, they are "stacked up".

When a scope (anything delimited by { and }) is exited (by using return XXX;, reaching the end of the scope or throwing an exception) everything within that scope is destroyed (destructors are called for everything). This process of destroying local objects and calling destructors is called stack unwinding.

You have the following issues related to stack unwinding:

  1. avoiding memory leaks (anything dynamically allocated that is not managed by a local object and cleaned up in the destructor will be leaked) - see RAII referred to by Nikolai, and the documentation for boost::scoped_ptr or this example of using boost::mutex::scoped_lock.

  2. program consistency: the C++ specifications state that you should never throw an exception before any existing exception has been handled. This means that the stack unwinding process should never throw an exception (either use only code guaranteed not to throw in destructors, or surround everything in destructors with try { and } catch(...) {}).

If any destructor throws an exception during stack unwinding you end up in the land of undefined behavior which could cause your program to terminate unexpectedly (most common behavior) or the universe to end (theoretically possible but has not been observed in practice yet).

Brittney answered 25/2, 2010 at 9:21 Comment(5)
On the contrary. While gotos should not be abused, they do cause stack unwinding in MSVC (not in GCC, so it's probably an extension). setjmp and longjmp do this in a cross platform way, with somewhat less flexibility.Pyne
I've just tested this with gcc and it does correctly call the destructors when you goto out of a code block. See #335280 - as mentioned in that link, this is part of the standard as well.Wrangle
reading Nikolai's, jrista's and your answer in this order, now it makes sense!Orography
@sashoalm Do you really think it's necessary to edit a post seven years later?Bernie
@DavidHoelzer I agree, David!! I was thinking that too when I saw the edit date and the posting date.Willyt
F
49

In a general sense, a stack "unwind" is pretty much synonymous with the end of a function call and the subsequent popping of the stack.

However, specifically in the case of C++, stack unwinding has to do with how C++ calls the destructors for the objects allocated since the started of any code block. Objects that were created within the block are deallocated in reverse order of their allocation.

Foundry answered 25/2, 2010 at 3:10 Comment(3)
There is nothing special about try blocks. Stack objects allocated in any block (whether try or not) is subject to unwinding when the block exits.Strangle
Its been a while since I have done much C++ coding. I had to dig that answer out of the rusty depths. ;PFoundry
don't worry. Everyone has "their bad" occasionally.Tarkington
D
19

I don't know if you read this yet, but Wikipedia's article on the call stack has a decent explanation.

Unwinding:

Returning from the called function will pop the top frame off of the stack, perhaps leaving a return value. The more general act of popping one or more frames off the stack to resume execution elsewhere in the program is called stack unwinding and must be performed when non-local control structures are used, such as those used for exception handling. In this case, the stack frame of a function contains one or more entries specifying exception handlers. When an exception is thrown, the stack is unwound until a handler is found that is prepared to handle (catch) the type of the thrown exception.

Some languages have other control structures that require general unwinding. Pascal allows a global goto statement to transfer control out of a nested function and into a previously invoked outer function. This operation requires the stack to be unwound, removing as many stack frames as necessary to restore the proper context to transfer control to the target statement within the enclosing outer function. Similarly, C has the setjmp and longjmp functions that act as non-local gotos. Common Lisp allows control of what happens when the stack is unwound by using the unwind-protect special operator.

When applying a continuation, the stack is (logically) unwound and then rewound with the stack of the continuation. This is not the only way to implement continuations; for example, using multiple, explicit stacks, application of a continuation can simply activate its stack and wind a value to be passed. The Scheme programming language allows arbitrary thunks to be executed in specified points on "unwinding" or "rewinding" of the control stack when a continuation is invoked.

Inspection[edit]

Divulsion answered 25/2, 2010 at 3:7 Comment(0)
S
15

Stack unwinding is a mostly C++ concept, dealing with how stack-allocated objects are destroyed when its scope is exited (either normally, or through an exception).

Say you have this fragment of code:

void hw() {
    string hello("Hello, ");
    string world("world!\n");
    cout << hello << world;
} // at this point, "world" is destroyed, followed by "hello"
Strangle answered 25/2, 2010 at 3:7 Comment(2)
Does this apply to any block? I mean if there is only { // some local objects }Versatile
@Rajendra: Yes, an anonymous block defines an area of scope, so it counts too.Biebel
U
14

IMO, the given below diagram in this article beautifully explains the effect of stack unwinding on the route of next instruction (to be executed once an exception is thrown which is uncaught):

enter image description here

In the pic:

  • Top one is a normal call execution (with no exception thrown).
  • Bottom one when an exception is thrown.

In the second case, when an exception occurs, the function call stack is linearly searched for the exception handler. The search ends at the function with exception handler i.e. main() with enclosing try-catch block, but not before removing all the entries before it from the function call stack.

Unstained answered 1/9, 2017 at 9:42 Comment(1)
Diagrams are good but explanation is bit confusing viz. ...with enclosing try-catch block, but not before removing all the entries before it from the function call stack...Thyme
N
12

I read a blog post that helped me understand.

What is stack unwinding?

In any language that supports recursive functions (ie. pretty much everything except Fortran 77 and Brainf*ck) the language runtime keeps a stack of what functions are currently executing. Stack unwinding is a way of inspecting, and possibly modifying, that stack.

Why would you want to do that?

The answer may seem obvious, but there are several related, yet subtly different, situations where unwinding is useful or necessary:

  1. As a runtime control-flow mechanism (C++ exceptions, C longjmp(), etc).
  2. In a debugger, to show the user the stack.
  3. In a profiler, to take a sample of the stack.
  4. From the program itself (like from a crash handler to show the stack).

These have subtly different requirements. Some of these are performance-critical, some are not. Some require the ability to reconstruct registers from outer frame, some do not. But we'll get into all that in a second.

You can find the full post here.

Nissensohn answered 8/5, 2014 at 15:45 Comment(0)
P
11

Everyone has talked about the exception handling in C++. But,I think there is another connotation for stack unwinding and that is related to debugging. A debugger has to do stack unwinding whenever it is supposed to go to a frame previous to the current frame. However, this is sort of virtual unwinding as it needs to rewind when it comes back to current frame. The example for this could be up/down/bt commands in gdb.

Palawan answered 8/3, 2011 at 6:1 Comment(3)
The debugger action is typically called "Stack Walking" which is simply parsing the stack. "Stack Unwinding" implies not only "Stack Walking" but also calling the destructors of objects that exist on the stack.Animalism
@Animalism I didn't know it is also called "stack walking". I have always been seeing "stack unwinding" in the context of all debugger articles and also even inside gdb code. I felt "stack unwinding" more appropriate as it is not just about peeking into stack information for every function, but involves unwinding of frame information (c.f. CFI in dwarf).This is processed in-order one function by one.Palawan
I guess the "stack walking" is made more famous by Windows. Also, I found as an example code.google.com/p/google-breakpad/wiki/StackWalking apart from dwarf standard's doc itself uses term unwinding few times. Though agree, it is virtual unwinding. Moreover, the question seems to be asking for every possible meaning "stack unwinding" can suggest.Palawan
E
5

C++ runtime destructs all automatic variables created between between throw & catch. In this simple example below f1() throws and main() catches, in between objects of type B and A are created on the stack in that order. When f1() throws, B and A's destructors are called.

#include <iostream>
using namespace std;

class A
{
    public:
       ~A() { cout << "A's dtor" << endl; }
};

class B
{
    public:
       ~B() { cout << "B's dtor" << endl; }
};

void f1()
{
    B b;
    throw (100);
}

void f()
{
    A a;
    f1();
}

int main()
{
    try
    {
        f();
    }
    catch (int num)
    {
        cout << "Caught exception: " << num << endl;
    }

    return 0;
}

The output of this program will be

B's dtor
A's dtor

This is because the program's callstack when f1() throws looks like

f1()
f()
main()

So, when f1() is popped, automatic variable b gets destroyed, and then when f() is popped automatic variable a gets destroyed.

Hope this helps, happy coding!

Eugeniaeugenics answered 3/3, 2014 at 8:16 Comment(0)
A
1

Stack unwinding is the process of removing function entries from the function call stack at runtime. It generally related to exception handling. In C++ when an exception occurs , the function call stack is linearly searched for the exception handler all the entries before the function with exception handlers are removed from the function call stack .

Afloat answered 29/6, 2021 at 4:16 Comment(0)
E
0

In Java stack unwiding or unwounding isn't very important (with garbage collector). In many exception handling papers I saw this concept (stack unwinding), in special those writters deals with exception handling in C or C++. with try catch blocks we shouln't forget: free stack from all objects after local blocks.

Escape answered 10/7, 2013 at 11:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.