6

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.

TingQian LI
  • 660
  • 8
  • 13
  • Related question: https://stackoverflow.com/questions/7730502/will-main-catch-exceptions-thrown-from-threads – veefu Jan 31 '18 at 06:20
  • More Related question: [stackoverflow.com/questions/2443135/how-do-i-find-where-an-exception-was-thrown-in-c](https://stackoverflow.com/questions/2443135/how-do-i-find-where-an-exception-was-thrown-in-c) – TingQian LI Jan 31 '18 at 07:04

2 Answers2

4

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";
            }
        }
    }
}
Jive Dadson
  • 16,680
  • 9
  • 52
  • 65
  • Thanks for your code, it illustrated good way to use exception, But when we run this code inside a debugger like GDB, exception happens, we will lost the context (which piece of code does the throw) by the time GDB stops, right? – TingQian LI Jan 31 '18 at 06:24
  • So put a breakpoint on the line containing exception_ptr = std::current_exception(). – Jive Dadson Jan 31 '18 at 06:28
  • I'm saying that catch every exception and display a message about it is less helpful in identifying the cause of the exception, and that's why uncaught exception is more favorable to me, and good news is its working in main thread, bad news is its not working in worker thread. OK, maybe I'm expecting the wrong thing out of C++ exception – TingQian LI Jan 31 '18 at 06:30
  • put a breakpoint on the line containing exception_ptr = std::current_exception() also do not tell us which line inside mytest(0) throws the exception---assume mytest() is far more complicated than here – TingQian LI Jan 31 '18 at 06:33
  • I think you just need to learn to use the debugger better. I am not familiar with GDB, but with Visual Studio, you can inspect the stack when a thread hits a break point. – Jive Dadson Jan 31 '18 at 06:34
  • 3
    Check the debugger options. I don't know about GDB, but a few Windows debuggers I've worked with had the option to pause execution at the throw of any/selected exception. That would give you all the context you want. – veefu Jan 31 '18 at 06:34
  • Thanks for your comment, I found this command in GDB (gdb) catch throw, works, but it will catch all throw, not just uncaught throw – TingQian LI Jan 31 '18 at 06:42
2

You should catch the exception in the thread where it occurs. The default handler will call terminate() wherever it is at, unwinding or not depending on implementation.

Sid S
  • 6,037
  • 2
  • 18
  • 24
  • My logic is that catching all possible exceptions are not helpful for debugging purpose, so I allow un-caught exceptions to acting as some kind of run-time assertion, but better than assert, if call site does handle some exceptions, those exceptions will not acting as assert automatically. – TingQian LI Jan 31 '18 at 06:03
  • 1
    That's just not a good way to deal with exceptions. If you throw them, you should catch them - in the same thread of execution. – Sid S Jan 31 '18 at 06:06
  • OK, I must be expecting too much from C++ exception mechanism, :) – TingQian LI Jan 31 '18 at 06:20
  • so you are saying that exception is not a debug mechanism, at least not designed for that purpose? so I should catch all possible exceptions (at least in outer most function) if I decided to use it? @Sid S – TingQian LI Jan 31 '18 at 07:17
  • 1
    @TingQianLI, Yes, that is correct. If you throw an exception, you should catch it somewhere whether you're debugging your program or not. If you try to open a file, and that file isn't there, this would be an exception to the normal flow of your program. Shirley, you would want your program to handle this situation ? – Sid S Jan 31 '18 at 07:28
  • Thanks for correcting me, 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. they should not be implemented using exception concept at all. – TingQian LI Jan 31 '18 at 08:00
  • Thanks a lot, @Sid S – TingQian LI Jan 31 '18 at 08:09