Showing posts with label cpp_exception. Show all posts
Showing posts with label cpp_exception. Show all posts

Oct 22, 2022

[C++] throw exception in the handling exception state will lead to std::terminate()

Reference:
What happens if my C++ exception handler itself raises an exception?

Throwing exception in the handling exception state will lead to
std::terminate()

#include <iostream>
using namespace std;

struct Object1 {

  ~Object1() noexcept(false) {
    cout << "Object1 Destructor"
         << "\n";
    throw int(10);
  }
};

struct Object2 {
  ~Object2() noexcept(false) {
    cout << "Object2 Destructor"
         << "\n";
    throw double(10);
  }
};

void test() {
  try {
    try {
      Object1 obj1; // In exception state of obj2 but still throws exception
                    // during destruction, std::terminate called.
      Object2 obj2;
    } catch (int &) {
      cout << "catch int"
           << "\n";
    }
  } catch (double &) {
    cout << "catch double"
         << "\n";
  } catch (int &) {
    cout << "catch int"
         << "\n";
  }
}

int main() {
  //
  //
  test();
}
Print:
Object2 Destructor
Object1 Destructor
libc++abi: terminating with uncaught exception of type int
[1]    18250 abort      ./a.out

Dec 12, 2017

[C++] Exception in detail.


Reference:

  1. When we write a throw statement,
    the compiler will translate it into a pair of calls into libstdc++ functions
    that allocate the exception and then start the stack unwinding process by calling libstdc.
  2. For each catch statement, the compiler will write some special information after the method's body, a table of exceptions this method can catch and a cleanup table (more on the cleanup table later).
  3. As the unwinder goes through the stack it will call a special function provided by libstdc++(called personality routine) that checks for each function in the stack which exceptions can be caught.
  4. If no matching catch is found for the exception, std::terminate is called.
    (No RAII object clean up happend. Same as calling std::exit() )
  5. If a matching catch is found, the unwinder now starts again on the top of the stack.
  6. As the unwinder goes through the stack a second time it will
    ask the personality routine to perform a cleanup for this method.
  7. The personality routine will check the cleanup table on the current method.
    If there are any cleanup actions to be run, it will "jump" into the current stack
    frame and run the cleanup code.
    This will run the destructor for each object allocated at the *current* scope.
  8. Once the unwinder reaches the frame in the stack that can handle the exception it will jump into the proper catch statement.
  9. Upon finishing the execution of the catch statement, a cleanup function will be called to release the memory held for the exception. 

Runtime functions involved:

__cxa_allocate_exception
Most implementations seem to allocate memory on a local thread storage (heap)
but resort to an emergency storage (presumably static) if out of memory.
We, of course, don't want to worry about the ugly details
so we can just have a static buffer if we want to.

__cxa_throw
Once the exception has been created __cxa_throw will be called.
This function will be responsible of starting the stack unwinding.
__cxa_throw is never supposed to return.
It either delegates execution to the correct catch block to handle
the exception or calls (by default) std::terminate, but it never ever returns.

vtable for __cxxabiv1::__class_type_info
This is the entry point the ABI defines to know (in runtime)
whether two types are the same or not.
This is the function that gets called to determine whether a
catch(Parent) can handle a throw Child.
We need to give it an address for the linker
(ie defining it won't be enough, we need to instantiate it)
and it has to have a vtable (that is, it must have a virtual method).

Detail:

The throw keyword is compiled into two function calls to libstdc++ :
  1. __cxa_allocate_exception
  2. __cxa_throw 
seppuku:

.LFB3:

    [...]

    call    __cxa_allocate_exception

    movl    $0, 8(%esp)

    movl    $_ZTI9Exception, 4(%esp)

    movl    %eax, (%esp)

    call    __cxa_throw

    [...]

__cxa_allocate_exception and __cxa_throw "live" on libstdc++

__cxa_allocate_exception will allocate memory for the new exception

__cxa_throw will prepare a bunch of stuff and forward this exception to _Unwind_ ,
a set of functions that live in libstdc and perform the real stack unwinding
(the ABI defines the interface for these functions)

Jul 16, 2014

[C++] Exception design / reasoning

Reference:
Vexing exceptions

Exceptions, error codes, and assertions - the various error reporting mechanisms of C++

Null Object pattern

Quote from Vexing exceptions:


  • Fatal exceptions
    are not your fault, you cannot prevent them, and you cannot sensibly clean up from them. They almost always happen because the process is deeply diseased and is about to be put out of its misery. Out of memory, thread aborted, and so on. There is absolutely no point in catching these because nothing your puny user code can do will fix the problem. Just let your "finally" blocks run and hope for the best. (Or, if you're really worried, fail fast and do not let the finally blocks run; at this point, they might just make things worse. But that's a topic for another day.
  • Boneheaded exceptions
    are your own darn fault, you could have prevented them and therefore they are bugs in your code. You should not catch them; doing so is hiding a bug in your code. Rather, you should write your code so that the exception cannot possibly happen in the first place, and therefore does not need to be caught. That argument is null, that typecast is bad, that index is out of range, you're trying to divide by zero – these are all problems that you could have prevented very easily in the first place, so prevent the mess in the first place rather than trying to clean it up.
  • Vexing exceptions
    are the result of unfortunate design decisions. Vexing exceptions are thrown in a completely non-exceptional circumstance, and therefore must be caught and handled all the time.

    sum up:
  • Don’t catch fatal exceptions; nothing you can do about them anyway, and trying to generally makes it worse.
  • Fix your code so that it never triggers a boneheaded exception – an "index out of range" exception should never happen in production code.
  • Avoid vexing exceptions whenever possible by calling the “Try” versions of those vexing methods that throw in non-exceptional circumstances. If you cannot avoid calling a vexing method, catch its vexing exceptions.
  • Always handle exceptions that indicate unexpected exogenous conditions; generally it is not worthwhile or practical to anticipate every possible failure. Just try the operation and be prepared to handle the exception.
  • Jun 8, 2014

    [C++] Uncaught Exceptions

    gotw : Uncaught Exceptions
    cppref : std::uncaught_exception
     
    inline int UncaughtException() noexcept
    {
    *(reinterpret_cast<int*>(
     static_cast<char*>(
      static_cast<void*>(
       __cxxabivl::__cxa_get_globals()))
        + sizeof(void*)));
    }
    
    

    http://vimeo.com/channels/ndc2014/97329153

    Feb 24, 2014

    [C++][Note]function-try-blocks

    Rarely known C++ constructs (part 1): Function try blocks
    excerpt:

    • Any exceptions caught in constructors or destructors are rethrown implicitly.
    • The currently handled exception is rethrown if control reaches the end of a handler
      of the function-try-block of a constructor or destructor.
      (§ 15.3.15 C++ International Standard/n3337)
    • Return statement in the catch block of a function acts as
      if it was a return statement in the function.
    • The function returns when the control flow reaches
      the end of the catch block.
      If there is no return statement there and
       the function return type is non-void, the behavior is undefined.
    • Function try block of main has some non-intuitive behavior:
      • Exceptions from constructors of objects defined on the namespace scope are not caught.
      • Exceptions from destructors of objects with static duration are not caught.
    This totally make sense since main function isn't the _start after all.
    Object obj; //Will throw
    
    int main()
    try
    {
    
    }
    catch(...)
    {
    //Won't catch a thing.
    }
    
    

    Dec 2, 2013

    [C++][NOTE][ORIGINAL] Exception & RAII object destructor

    Destructors of RAII objects are not invoked if a thrown exception will never be caught,
    because in this scenario no exception handler is installed.

    Also, if a destructor leaks out an exception while another exception is in flight,
    then all bets are off.

     
    #include <iostream>                                                                       
    using namespace std;                                                                      
                                                                                              
    struct Test                                                                               
    {                                                                                         
       ~Test(){                                                                               
          cout << "ha" << endl;                                                               
       }                                                                                      
    };                                                                                        
                                                                                              
    void ha()                                                                                 
    {                                                                                         
       Test t;                                                                                
       throw 0;                                                                               
    }                                                                                         
                                                                                              
    int main(){                                                                               
       try{                                                                                   
          ha();                                                                               
       }                                                                                      
       catch(...){                                                                            
          cout << "catch!" << endl;                                                           
       }                                                                                      
                                                                                              
    }   
    

    Aug 31, 2012

    [C++][NOTE] C++ Exception Handling and Performance

    Concept :
    In TLPI
    
    Ch. 6.8 setjmp()  longjmp() 
    
    Ch. 21.2.1 sigsetjmp() and siglongjmp()
    
    
    C++ Exception Handling and Performance

    excerpt :
    Using exceptions will make binary size bigger, no matter what mechanism 
    is used to implement exception handling. So if program size is more 
    important than execution speed, exceptions should not be used. And in 
    those cases where you want to gain maximum execution speed with minimal 
    impact on binary size, you have to do additional testing in order to 
    find the best combination of C-style error checking and exception 
    handling code that achieves your result.

    reference :
    Exception handling implementation
    
    [boost] Error and Exception Handling
    Exception-Safety in Generic Components by David Abrahams
    reference:
    Structured Exception Handling
    Exception handling in linux / structured exception handling (SEH)