Thread: exception handling, function call and memory

  1. #1
    Registered User
    Join Date
    May 2006
    Posts
    1,579

    exception handling, function call and memory

    Hello everyone,


    Such code segment is used to check whether function call or exception-handling mechanism runs out of memory first (written by Bjarne),

    Code:
    void perverted()
    {
        try{
            throw exception();
        }
        catch (exception& e)
        {
            perverted();
            cout << e.what() << endl;
        }
    }
    1.

    My question is when the exception is thrown, and goes to exception handler in catch block, it will do operations in the following sequences,

    (1) execute the exception handler code (since there will be recursive function call -- stack will ever increasing);
    (2) unwind stack.

    Runs out of memory because function call will make stack ever-increasing, right?

    If it does (2) before (1), I think stack will always be unwinded before exception handling is called -- then stack will never grow, so there will be no out-of-memory caused by an ever increasing stack.

    Is that correct?

    2.

    I am confused about why exception handling mechanism will run out of memory because in exception handle implementation, we just insert exception handler registration block of the function onto the beginning of the new function call stack each time there is a function call, so the memory should be the memory consumed by function call stuff, I do not think exception handling mechanism itself will consume additional memory. How do you think of it and how do you think of the what are the memory consumed by exception handling mechanism?


    thanks in advance,
    George

  2. #2
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    It will run out of stack. I'm 99.9% sure of that - the stack can't unwind the whole way down to the level where perverted was first called from, so it will continue to grow the stack until it runs out (at which point the application will not be able to deal with exceptions any longer, since the exception handling in itself requires stack-space).

    Would you expect this:
    Code:
    int count = 0;
    void perverted()
    {
        try{
            throw exception();
        }
        catch (exception& e)
        {
            if (count < 100)
                perverted();
            cout << count << endl;
            cout << e.what() << endl;
        }
    }
    to work out correctly?

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  3. #3
    Registered User
    Join Date
    May 2006
    Posts
    1,579
    Thanks Mats,


    I am interested to your point, "the exception handling in itself requires stack-space".

    In the original purpose of this sample, Bjarne is to show us whether (1) function call or (2) exception handling mechanism will run out of memory.

    About (2), the memory consumed by exception handling mechanism, you mean the exception registration data structure (the linked list) of the called function being pushed on stack? Anything else you think is memory consumed by exception handling mechanism?

    Quote Originally Posted by matsp View Post
    It will run out of stack. I'm 99.9% sure of that - the stack can't unwind the whole way down to the level where perverted was first called from, so it will continue to grow the stack until it runs out (at which point the application will not be able to deal with exceptions any longer, since the exception handling in itself requires stack-space).

    Would you expect this:
    Code:
    int count = 0;
    void perverted()
    {
        try{
            throw exception();
        }
        catch (exception& e)
        {
            if (count < 100)
                perverted();
            cout << count << endl;
            cout << e.what() << endl;
        }
    }
    to work out correctly?

    --
    Mats

    regards,
    George

  4. #4
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Obviously, which you run out of first, memory or stack, depends on several factors:
    1. How large the exception object is.
    2. How large the stack is.
    3. How large the heap is.

    But in some respect, the exception handling itself will (almost certainly) use SOME stack-space in some way or another - it's almost inconceivable to think of how to implement something like this without using up SOME amount of stack-space [1].

    Certainly if the heap is large enough (e.g. you have a Windows system with 2-4GB), and the exception object isn't VERY large, then stack-space is going to run out first, and the exception mechanism will most likely fail at this point.

    1. The stack unwinding mechanism is highly likely to consist of function calls at some point or another, and that will use stack-space in itself. This is of course dependent on how the actual implementation is made, but it is fairly common that the data structure to track the stack-content is non-trivial, and thus the exception handling requires calls to functions to "sort it out".

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  5. #5
    Registered User
    Join Date
    May 2006
    Posts
    1,579
    Thanks Mats,


    Your reply is great! And we are closer to the answer. :-)

    Two more comments,

    1.

    Quote Originally Posted by matsp View Post
    Certainly if the heap is large enough (e.g. you have a Windows system with 2-4GB), and the exception object isn't VERY large, then stack-space is going to run out first, and the exception mechanism will most likely fail at this point.
    I agree and understand all of your analysis that exception handling mechanism is based on stack. Why you suddenly mentioned "heap"? Why "Certainly if the heap is large enough, and the exception object isn't VERY large, then stack-space is going to run out first"?

    From your whole analysis, I can not see any relationship between heap and exception handling mechanism. And it is appreciated if you could provide more information. :-)

    2.

    Quote Originally Posted by matsp View Post
    1. The stack unwinding mechanism is highly likely to consist of function calls at some point or another, and that will use stack-space in itself. This is of course dependent on how the actual implementation is made, but it is fairly common that the data structure to track the stack-content is non-trivial, and thus the exception handling requires calls to functions to "sort it out".

    --
    Mats
    "sort it out" you mean find the exception handler or call the exception handler or something else? A little lost in your last sentence.


    regards,
    George

  6. #6
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    1) The operation sequence will be:
    1. Find an appropriate catch handler.
    2. Unwind the stack to this point.
    3. Execute the catch handler.

    Note that Bjarne's function will call itself recursively from within itself. The function itself is not unwound, because it contains the catch handler. Thus, it's an infinite recursion and will eventually run out of stack space.

    BUT

    2) There is a lot of leeway in the way exceptions are implemented. Matsp's assumption that the exception handling mechanism has the entire heap for its objects is incorrect. In fact, the exception handling mechanism absolutely must reserve a bit of space for allocating an exception at the very beginning of the program, because otherwise it couldn't throw a bad_alloc if new fails - it would have to allocate the exception object on the heap, but the heap is full. (Potentially. Of course, a bad_alloc being very small, there's a good chance you actually can allocate it. But then, maybe not.)
    Now, the exception handling implementation might go further and say that it only allocates from this limited area. It could reserve, say, 4k on program startup and allocate all exception objects from there. This is a perfectly valid implementation. The standard says:

    15.1/4:
    "The memory for the temporary copy of the exception being thrown is
    allocated in an unspecified way, except as noted in
    _basic.stc.dynamic.allocation_."

    The reference is to 3.7.3.1/4:
    "[...] in particular, a global allocation function is not called to allocate storage [...] for the copy of an object thrown by a throw expression."

    This doesn't mean it doesn't allocate from the heap. It merely means it doesn't go through any user-defined operator new for doing so.

    3) The exception handling mechanism will require a small and finite amount of space to run the code which unwinds the stack. In addition, it will need an unspecified amount of memory during destructor calls in unwinding, but that's not really the fault of the exception handling.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  7. #7
    Registered User
    Join Date
    May 2006
    Posts
    1,579
    Great CornedBee!


    Two more comments.

    1.

    Quote Originally Posted by CornedBee View Post
    1) The operation sequence will be:
    1. Find an appropriate catch handler.
    2. Unwind the stack to this point.
    3. Execute the catch handler.
    Sorry I do not agree with the sequence and I think it should be 1, 3, 2. :-)

    Reason is, if the sequence is 1, 2 and 3. Then suppose in my sample when there is an exception thrown, then step 2 will unwind the stack (I think you mean unwind stack is something like clear up local objects by invocation of destructor, and reassign register like EBP/ESP). If stack is unwound perfectly before execute (3), you can see the stack is clear-up during unwinding stack size is shrinked (by moving EBP/ESP to higher address) and it is the same size of what it is before. Then stack is never grow to make recursive out of memory...

    This is what I read these days, and stack unwind is executed after 3 indicated in this article.

    http://www.microsoft.com/msj/0197/Ex...Exception.aspx

    2.

    Quote Originally Posted by CornedBee View Post
    2) There is a lot of leeway in the way exceptions are implemented. Matsp's assumption that the exception handling mechanism has the entire heap for its objects is incorrect. In fact, the exception handling mechanism absolutely must reserve a bit of space for allocating an exception at the very beginning of the program, because otherwise it couldn't throw a bad_alloc if new fails - it would have to allocate the exception object on the heap, but the heap is full. (Potentially. Of course, a bad_alloc being very small, there's a good chance you actually can allocate it. But then, maybe not.)
    Now, the exception handling implementation might go further and say that it only allocates from this limited area. It could reserve, say, 4k on program startup and allocate all exception objects from there. This is a perfectly valid implementation. The standard says:

    15.1/4:
    "The memory for the temporary copy of the exception being thrown is
    allocated in an unspecified way, except as noted in
    _basic.stc.dynamic.allocation_."

    The reference is to 3.7.3.1/4:
    "[...] in particular, a global allocation function is not called to allocate storage [...] for the copy of an object thrown by a throw expression."

    This doesn't mean it doesn't allocate from the heap. It merely means it doesn't go through any user-defined operator new for doing so.

    3) The exception handling mechanism will require a small and finite amount of space to run the code which unwinds the stack. In addition, it will need an unspecified amount of memory during destructor calls in unwinding, but that's not really the fault of the exception handling.
    What do you mean the exception object? Exception registration entry (contains try level, handler information, previous entry to make a linked list) or something else?

    http://www.microsoft.com/msj/0197/Ex...Exception.aspx

    If you mean other things, it is appreciated if you could clarify. If you mean exception registration entry, it should be on stack, not on heap.

    (So, I still confused what objects in exception handling mechanism will consume heap other than stack?)


    regards,
    George

  8. #8
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    I think you will find that the of the stack pointer (EBP/ESP in x86-32) is done to get back to the catch-handler - which may not be in the same stack. Of course, we'd also, in the process, undo any objects on the stack.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  9. #9
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    Quote Originally Posted by George2 View Post
    Reason is, if the sequence is 1, 2 and 3. Then suppose in my sample when there is an exception thrown, then step 2 will unwind the stack (I think you mean unwind stack is something like clear up local objects by invocation of destructor, and reassign register like EBP/ESP). If stack is unwound perfectly before execute (3), you can see the stack is clear-up during unwinding stack size is shrinked (by moving EBP/ESP to higher address) and it is the same size of what it is before. Then stack is never grow to make recursive out of memory...
    That's complete nonsense. There's no such thing as "perfect stack unwinding". Stack unwinding means returning the stack to the state it was in the function that contains the catch block that was selected for handling the exception.
    In the example, this state includes the call to the function because the catch is within the function. Then the function calls itself. It establishes a try block and throws. The EH mechanism finds the inner try and its fitting catch block. It then unwinds to the state the catch block expects (that's no unwinding at all, actually, because the try block contains nothing local and there's no function call). This new state includes both function calls. The catch block calls the function again, and so on.

    That's an article about SEH. What the hell has it got to do with C++ exceptions? Have 4 threads not been enough to make it clear that they are different things?

    What do you mean the exception object?
    The exception object. The exception itself. Dude, what do you think you're throwing? Hot air?
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  10. #10
    Registered User
    Join Date
    May 2006
    Posts
    1,579
    Hi Mats,


    Your reply #8 is reply to which # of post of this question? I lost the context. :-)

    BTW: from technical point of view as a standalone, I agree with your point below, just lost the context.

    Quote Originally Posted by matsp View Post
    I think you will find that the of the stack pointer (EBP/ESP in x86-32) is done to get back to the catch-handler - which may not be in the same stack. Of course, we'd also, in the process, undo any objects on the stack.

    --
    Mats

    regards,
    George

  11. #11
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    My comment was to clarify your question on CornedBee's 1,2,3 ordering, and I agree with CornedBee that it happens in the order described.

    But the stack will possibly, temporarily, grow when performing exception handling BEFORE it shrinks. In the example given in the #1 post here, there will be no (large) stack reduction, because the throw does not lead to leaving the function, so there is no other stack-frame to remove.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  12. #12
    Registered User
    Join Date
    May 2006
    Posts
    1,579
    Thanks CornedBee,


    Great reply! Two more comments,

    1.

    Quote Originally Posted by CornedBee View Post
    That's complete nonsense. There's no such thing as "perfect stack unwinding". Stack unwinding means returning the stack to the state it was in the function that contains the catch block that was selected for handling the exception.
    In the example, this state includes the call to the function because the catch is within the function. Then the function calls itself. It establishes a try block and throws. The EH mechanism finds the inner try and its fitting catch block. It then unwinds to the state the catch block expects (that's no unwinding at all, actually, because the try block contains nothing local and there's no function call). This new state includes both function calls. The catch block calls the function again, and so on.
    You mean local stack unwinding is to invoke local object destructor before invoking exception handler? Does it do anything else?

    Unwind in your mind does not clear-up and function call stack itself? (if it does, the recursive function call will never make stack ever-increasing to make memory out of run?)

    2.

    Quote Originally Posted by CornedBee View Post
    The exception object. The exception itself. Dude, what do you think you're throwing? Hot air?
    Now I understand the exception object you mean is the object binded to exception reference e (in catch statement exception& e). Right?


    regards,
    George

  13. #13
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Quote Originally Posted by George2 View Post
    Unwind in your mind does not clear-up and function call stack itself? (if it does, the recursive function call will never make stack ever-increasing to make memory out of run?)
    The stack unwinding that is done for any exception will be "Whatever is necessary to get it to the catch-block", so in this example, there will still be a return address for the call to perverted on the stack when the catch handler gets executed. So yes, the above code WILL absolutely run out of stack [unless there is some other resource it runs out of first, e.g. exception object space].

    2.

    Now I understand the exception object you mean is the object binded to exception reference e (in catch statement exception& e). Right?
    That is how I see it, yes.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  14. #14
    Registered User
    Join Date
    Apr 2006
    Posts
    2,149
    Quote Originally Posted by George2 View Post
    You mean local stack unwinding is to invoke local object destructor before invoking exception handler? Does it do anything else?
    It will pop the local objects off the stack, thereby moving the stack pointer to where it was before the try block.

    Unwind in your mind does not clear-up and function call stack itself? (if it does, the recursive function call will never make stack ever-increasing to make memory out of run?)
    There is no function call inside the catch block to clean up. If there were, then yes, that call would be cleaned up. But here perverted is always called outside the try block.
    It is too clear and so it is hard to see.
    A dunce once searched for fire with a lighted lantern.
    Had he known what fire was,
    He could have cooked his rice much sooner.

  15. #15
    Registered User
    Join Date
    May 2006
    Posts
    1,579
    Thanks Mats and King!


    Both your replies are very practical. :-)

    Quote Originally Posted by matsp View Post
    The stack unwinding that is done for any exception will be "Whatever is necessary to get it to the catch-block", so in this example, there will still be a return address for the call to perverted on the stack when the catch handler gets executed.

    --
    Mats
    Quote Originally Posted by King Mir View Post
    It will pop the local objects off the stack, thereby moving the stack pointer to where it was before the try block.

    There is no function call inside the catch block to clean up. If there were, then yes, that call would be cleaned up. But here perverted is always called outside the try block.
    1.

    I think your above comments mean the same thing -- which I also agree -- during stack unwinding, not all of the stack of the current function which throw happens will disappear, but a part of them will be on the stack -- the part before the try block and in my sample may include return address of the client of the function, right?

    2.

    Another interesting question is, if in try block we invoked another function? How do we restore the state?

    Sample code,

    Code:
    void foo()
    {
        FooClass function_local_object;
        manipulate_data (&function_local_object);
        try{
        goo (&function_local_object); // modify function_local_object
        throw(something);
        } catch (...)
        {
        }
    }
    How to restore the status of function_local_object to the exact state after calling manipulate_data (&function_local_object)?

    (saving a copy is memory storage expensive solution.)


    thanks in advance,
    George

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. To find the memory leaks without using any tools
    By asadullah in forum C Programming
    Replies: 2
    Last Post: 05-12-2008, 07:54 AM
  2. Game Pointer Trouble?
    By Drahcir in forum C Programming
    Replies: 8
    Last Post: 02-04-2006, 02:53 AM
  3. pointers
    By InvariantLoop in forum C Programming
    Replies: 13
    Last Post: 02-04-2005, 09:32 AM
  4. Manipulating the Windows Clipboard
    By Johno in forum Windows Programming
    Replies: 2
    Last Post: 10-01-2002, 09:37 AM
  5. Is it necessary to write a specific memory manager ?
    By Morglum in forum Game Programming
    Replies: 18
    Last Post: 07-01-2002, 01:41 PM