Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ uncaught exception in worker thread

Uncaught exception behaves differently for main thread and another std::thread.

here is the test program

#include <thread>

class XXX{
public:
  XXX(){std::fprintf(stderr, "XXX ctor\n");}
  ~XXX(){std::fprintf(stderr, "XXX dtor\n");}
};

void mytest(int i)
{
    XXX xtemp;
    throw std::runtime_error("Hello, world!");
}
int main(int argc, char *argv[])
{
    if(argc == 1) {
        mytest(0);
    }else{
        std::thread th([&]{mytest(0);});
        th.join();
    }
}

above code (C++11), compiled by GCC 5.4 run without args

XXX ctor
terminate called after throwing an instance of 'std::runtime_error'
   what():  Hello, world!
Aborted (core dumped)

run 1 arg:

XXX ctor
XXX dtor
terminate called after throwing an instance of 'std::runtime_error'
  what():  Hello, world!
Aborted (core dumped)

So the stack unwind is performed in worker thread but not in main thread, WHY?

I'm asking because I'd like core dump to give useful stack backtrace information in both cases (for uncaught exception).

Thanks in advance!!!


Further trials reveals that add noexcept keyword to thread function body mytest() can partially solves my problem because unwind will fail,but its not a good solution because unwind will still partially happen if mytest() is calling another function without noexcept guarantee and actually thrown the uncaught exception.


Update: Thanks to all comment providers, now I understand C++ exception is not backtrace friendly, and GCC, as a C++ implementation,has the freedom to choose to not unwind when uncaught exception is thrown from main thread, and unwind when from worker thread.


Update: Special thanks to Sid S & Jive Dadson, I must mixed up some concepts: 1) exception/error handling; 2) runtime assertion 3) Segment fault, 2&3 are similar, they are UN-recoverable errors, abort immediately is the only choice, they are also backtrace friendly in debugger because stack unwind is not involved. they should not be implemented using exception concept at all. exception is supposed to be caught always, let uncaught exception leaving main() is not a recommended usage.

like image 608
TingQian LI Avatar asked Jan 31 '18 05:01

TingQian LI


1 Answers

Why? That's just the way it is. Since c++11, there is some support for dealing with exceptions thrown in threads other than main, but you will need to instrument the threads to catch exceptions and re-throw them. Here's how.

#include <thread>
#include <iostream>

class XXX {
public:
    XXX() { std::fprintf(stderr, "XXX ctor\n"); }
    ~XXX() { std::fprintf(stderr, "XXX dtor\n"); }
};

void mytest(int i)
{
    XXX xtemp;
    throw std::runtime_error("Hello, world!");
}

int main(int argc, char *argv[])
{
    std::exception_ptr exception_ptr = nullptr;
    if (argc == 1) {
        mytest(0);
    }
    else {
        std::thread th([&exception_ptr]() {
            try {
                mytest(0);
            }
            catch (...) {
                exception_ptr = std::current_exception();
            }
        });
        th.join();
        if (exception_ptr) {
            try {
                std::rethrow_exception(exception_ptr);
            }
            catch (const std::exception &ex)
            {
                std::cerr << "Thread exited with exception: " << ex.what() << "\n";
            }
        }
    }
}
like image 138
Jive Dadson Avatar answered Sep 21 '22 23:09

Jive Dadson