c++exceptionstackg++stack-unwinding

Why program cannot reach proper return instruction after stack unwinding?


Compiler: g++ 9.2.0
Operating system: Windows 10
g++ call:

g++ -E main.cpp -v -o main.i
g++ -c main.cpp -v -o main.o 
g++ main.o -v -o main.exe
main.exe

main.cpp:

#include <chrono>
#include <iostream>
#include <string>
#include <exception>
#include <iostream>
//#include <thread>
#include "mingw.thread.h"
struct Object{
    struct Exception : public std::exception{
        std::string error_;
        Exception(std::string str){
            this->error_ = str;
        }
        ~Exception() {
        }
        std::string get(){
            return error_;
        }
    };
    void DoSomeWork() {
        try {
        std::thread AnotherTh(&Object::GenerateException ,this);
        AnotherTh.detach ();
        while(true);
    }
        catch (...) {
            throw ;
        }
    }
    void GenerateException(){
        std::this_thread::sleep_for (std::chrono::seconds(5));
        throw Object::Exception ("Some error");
    }
};
int main(){
    try{
        Object instance;
        std::thread th(&Object::DoSomeWork,std::ref(instance));
        th.join ();
    }
    catch (Object::Exception &ex ) {
        std::cout << ex.get ();
    }
    catch (std::exception &ex ){
        std::cout << ex.what ();
    }
    catch (...){
    }
    std::cout << "never reach this";
    return 0;
}

Output:

terminate called after throwing an instance of 'Object::Exception'
  what():  std::exception

I am launching main thread with a new thread (th) and wait for it ,inside of th starts another thread where exception will be throwed. So ,when it appears ,starts stack unwinding (from Object::GenerateException to Object::DoSomeWork as there is no more calls is Object::GenerateException's stack) and management is passed to Object::DoSomeWork's try-catch ,there is the same calls chain to main's try-catch as Object::DoSomeWork "knows" it was called from main.

I cannot understand why it cannot handle exception and pass it to main's try-catch.


Solution

  • Why program cannot reach proper return instruction after stack unwinding c++?

    Because your code creates multiple threads and you are not catching the exception in the thread which is actually throwing the exception. The exceptions won't be propagated across threads even if you call join() member function of std::thread.

    Try blocks are defined as dynamic constructs of the stack. A try block catches exceptions thrown by code reached dynamically, by call, from its contents.

    When you create a new thread, you create a brand-new stack, that is not at all part of the dynamic context of the try block, even if the call to pthread_create or construct join-able std::thread() is inside the try.

    To catch an exception originating in thread X, you have to have the try-catch clause in thread X (for example, around everything in the thread function, similarly to what you already do in main).

    For a related question, see How can I propagate exceptions between threads?.

    An example:

    #include <chrono>
    #include <iostream>
    #include <string>
    #include <exception>
    #include <iostream>
    #include <thread>
    
    
    struct Object {
    
        void DoSomeWork() 
        {
            std::cout << "DoSomeWork Thread ID: " << std::this_thread::get_id() << std::endl;
            try {
                std::thread thread(&Object::GenerateException, this);
                thread.detach();
                while(true);
            }
            catch (...) {
                std::cout << "Caught exception: " << std::this_thread::get_id() << std::endl;
                throw ;
            }
        }
        void GenerateException(void)
        {
            std::cout << "GenerateException Thread ID: " << std::this_thread::get_id() << std::endl;
            try {
                std::this_thread::sleep_for (std::chrono::seconds(5));
                throw std::runtime_error("Some error");
            } catch (...) {
                std::cout << "Caught exception: " << std::this_thread::get_id() << std::endl;
                throw;
            }
        }
    };
    int main()
    {
        std::cout << "Main Thread ID: " << std::this_thread::get_id() << std::endl;
        try {
            Object instance;
            std::thread th(&Object::DoSomeWork,std::ref(instance));
            th.join();
        }
        catch (const std::exception &ex) {
            std::cout << ex.what() << std::endl;
            std::cout << "Exception caught at: " << std::this_thread::get_id() << std::endl;
        }
        std::cout << "never reach this" << std::endl;
        return 0;
    }
    

    Output:

    Main Thread ID: 140596684195648
    DoSomeWork Thread ID: 140596665124608
    GenerateException Thread ID: 140596656670464
    Caught exception: 140596656670464
    terminate called after throwing an instance of 'std::runtime_error'
      what():  Some error
    Aborted (core dumped)