2

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.

Uwe Keim
  • 39,551
  • 56
  • 175
  • 291
  • 2
    On a slightly related note: If you have `catch(...) { throw; }` then what's the point of having the `try catch` at all? That just does what would naturally happen (the exception is passed on upwards in the stack). – Some programmer dude May 02 '20 at 12:55
  • This doesn't address the question, but `std::exception` has a member function `const char *what()` that returns a description of the exception. Derived classes usually override that function, so code that catches `std::exception&` can get a meaningful message. – Pete Becker May 02 '20 at 14:20

2 Answers2

1

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)
abhiarora
  • 9,743
  • 5
  • 32
  • 57
  • what is inside Object::GenerateException calls stack at exception throwing moment? – Gaurav Goswami May 02 '20 at 12:56
  • Didn't get you! – abhiarora May 02 '20 at 13:08
  • posted here screenshot of qt + mingw 7.3.0 debugger's calls stack of Object::GenerateException https://imgur.com/a/FeoJkxu ,I am surprised there is no Object::DoSomeWork at lvl 1. – Gaurav Goswami May 02 '20 at 13:14
  • Because it is showing you the stack which had uncaught exception. But there will be two more stack. I can see in my gdb – abhiarora May 02 '20 at 13:17
  • By default, gdb/debugger shows you the only thread/stack which caught the issue. I can see all of the thread in gdb by `thread all apply bt` – abhiarora May 02 '20 at 13:19
  • See my edited answer, I have posted an example code which shows the thread id's – abhiarora May 02 '20 at 13:20
  • @rueodmk The call stack of a newly create thread is implementation defined. – Some programmer dude May 02 '20 at 13:23
  • Now I got it. I wasn't able to understand what you where saying. As mentioned by @Someprogrammerdude, it is implementation defined. The `??` in your call stack basically means the debugger is unable to get all the required symbols and got confused. – abhiarora May 02 '20 at 13:25
0

From this std::thread reference:

... if it terminates by throwing an exception, std::terminate is called.

If an uncaught exception is thrown in a thread, then the program will be forcibly terminated.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621