What is stack unwinding? Searched through but couldn't find enlightening answer!
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.
delete [] pleak;
is only reached if x == 0. –
Bombay 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:
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.
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).
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.
try
blocks. Stack objects allocated in any block (whether try
or not) is subject to unwinding when the block exits. –
Strangle 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]
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"
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):
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.
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:
- As a runtime control-flow mechanism (C++ exceptions, C longjmp(), etc).
- In a debugger, to show the user the stack.
- In a profiler, to take a sample of the stack.
- 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.
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.
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!
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 .
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.
© 2022 - 2024 — McMap. All rights reserved.