4

I am having an issue handling exceptions in multithreaded c++ code. The following program exits with terminate called without an active exception Aborted (core dumped).

#include <thread>
#include <iostream>
#include <stdexcept>

struct Thing {

    void runComponent()
    {
       throw std::runtime_error("oh no!\n");
    }

    void runController()
    {
        while (true) {}
    }

    void run()
    {
        auto control_thread = std::thread([this] { runController(); });

        runComponent();

        if (control_thread.joinable())
            control_thread.join();
    }
};

int main() 
{
    try {
        Thing thing;
        thing.run();
    } catch (std::runtime_error &ex) {
        std::cerr << ex.what();
    }
}

Instead, I would like to handle the exception in the try catch block in main(). I understand that exceptions are not (normally) passed between threads since threads each have there own stack. The issue here (it seems to me) is that the exception is not being handled even though it is being generated on the non-forked thread. If I comment out the lines in run() having to do with control_thread, everything works fine.

Compiled with clang-3.8 and -std=c++11 main.cpp -lpthread.

jonnew
  • 194
  • 1
  • 3
  • 12
  • @Curious Maybe. I'm aware of `exception_ptr`. But the issue here (I think..) is that thrower (runComponent) is actually on the main thread. Do I need to use the exception pointer even in this case? – jonnew Dec 29 '16 at 04:19
  • Answered in the answer below! I misunderstood the question – Curious Dec 29 '16 at 04:24

1 Answers1

4

The issue here is that you are neither calling detach() or join() on the thread you create in the run() method. That is an error in C++ threads. See When should I use std::thread::detach?

If you change your code to either of the following then everything works fine

#include <thread>
#include <iostream>
#include <stdexcept>

struct Thing {

    void runComponent()
    {
       throw std::runtime_error("oh no!\n");
    }

    void runController()
    {
        return;
    }

    void run()
    {
        auto control_thread = std::thread([this] { runController(); });
        // either of the following two will work
        // control_thread.join();
        control_thread.detach();

        runComponent();
    }
};

int main()
{
    try {
        Thing thing;
        thing.run();
    } catch (std::runtime_error &ex) {
        std::cerr << ex.what();
    }
}

It looked like what you wanted was a detach, thats why I detached the thread in the example above. If that is not what you want, you likely would need some other way to handle the join()ing of the thread. If you want the main thread and the other thread to function independently then you will need some other synchronization.

Community
  • 1
  • 1
Curious
  • 20,870
  • 8
  • 61
  • 146
  • Yes, this works. I think I do want detach, but I note the point raised in the answer to the SO link you provide "Unless you need to have more flexibility AND are willing to provide a synchronization mechanism to wait for the thread completion on your own, in which case you may use detach". In this particular case, what does this mean? Will the resources allocated by `runController()` be freed if I use detach? – jonnew Dec 29 '16 at 04:33
  • Also, by "error in c++ threads" do you mean in the standard library implementation? – jonnew Dec 29 '16 at 04:34
  • @jonnew I think what that means is that if you want some custom way to handle exceptions then you will have to implement it manually with locks. The simplest thing would be to make a list of threads which has thread ids for the threads in the order in which exceptions have occurred. I don't think it's that relevant – Curious Dec 29 '16 at 04:35
  • @jonnew, the `std::thread` destructor checks to make sure that the user has either called `detach` or `join` and if neither have been called, it terminates the program. So I guess yes, the standard library implementation will cause your program to exit with an error – Curious Dec 29 '16 at 04:36
  • Ah, I see. That makes sense. Thank you, very much. – jonnew Dec 29 '16 at 04:37