110
votes

We have a function which a single thread calls into (we name this the main thread). Within the body of the function we spawn multiple worker threads to do CPU intensive work, wait for all threads to finish, then return the result on the main thread.

The result is that the caller can use the function naively, and internally it'll make use of multiple cores.

All good so far..

The problem we have is dealing with exceptions. We don't want exceptions on the worker threads to crash the application. We want the caller to the function to be able to catch them on the main thread. We must catch exceptions on the worker threads and propagate them across to the main thread to have them continue unwinding from there.

How can we do this?

The best I can think of is:

  1. Catch a whole variety of exceptions on our worker threads (std::exception and a few of our own ones).
  2. Record the type and message of the exception.
  3. Have a corresponding switch statement on the main thread which rethrows exceptions of whatever type was recorded on the worker thread.

This has the obvious disadvantage of only supporting a limited set of exception types, and would need modification whenever new exception types were added.

9

9 Answers

99
votes

C++11 introduced the exception_ptr type that allows to transport exceptions between threads:

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

static std::exception_ptr teptr = nullptr;

void f()
{
    try
    {
        std::this_thread::sleep_for(std::chrono::seconds(1));
        throw std::runtime_error("To be passed between threads");
    }
    catch(...)
    {
        teptr = std::current_exception();
    }
}

int main(int argc, char **argv)
{
    std::thread mythread(f);
    mythread.join();

    if (teptr) {
        try{
            std::rethrow_exception(teptr);
        }
        catch(const std::exception &ex)
        {
            std::cerr << "Thread exited with exception: " << ex.what() << "\n";
        }
    }

    return 0;
}

Because in your case you have multiple worker threads, you will need to keep one exception_ptr for each of them.

Note that exception_ptr is a shared ptr-like pointer, so you will need to keep at least one exception_ptr pointing to each exception or they will be released.

Microsoft specific: if you use SEH Exceptions (/EHa), the example code will also transport SEH exceptions like access violations, which may not be what you want.

75
votes

Currently, the only portable way is to write catch clauses for all the types of exceptions that you might like to transfer between threads, store the information somewhere from that catch clause and then use it later to rethrow an exception. This is the approach taken by Boost.Exception.

In C++0x, you will be able to catch an exception with catch(...) and then store it in an instance of std::exception_ptr using std::current_exception(). You can then rethrow it later from the same or a different thread with std::rethrow_exception().

If you are using Microsoft Visual Studio 2005 or later, then the just::thread C++0x thread library supports std::exception_ptr. (Disclaimer: this is my product).

14
votes

If you're using C++11, then std::future might do exactly what you're looking for: it can automagically trap exceptions that make it to the top of the worker thread, and pass them through to the parent thread at the point that std::future::get is called. (Behind the scenes, this happens exactly as in @AnthonyWilliams' answer; it's just been implemented for you already.)

The down side is that there's no standard way to "stop caring about" a std::future; even its destructor will simply block until the task is done. [EDIT, 2017: The blocking-destructor behavior is a misfeature only of the pseudo-futures returned from std::async, which you should never use anyway. Normal futures don't block in their destructor. But you still can't "cancel" tasks if you're using std::future: the promise-fulfilling task(s) will continue running behind the scenes even if nobody is listening for the answer anymore.] Here's a toy example that might clarify what I mean:

#include <atomic>
#include <chrono>
#include <exception>
#include <future>
#include <thread>
#include <vector>
#include <stdio.h>

bool is_prime(int n)
{
    if (n == 1010) {
        puts("is_prime(1010) throws an exception");
        throw std::logic_error("1010");
    }
    /* We actually want this loop to run slowly, for demonstration purposes. */
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    for (int i=2; i < n; ++i) { if (n % i == 0) return false; }
    return (n >= 2);
}

int worker()
{
    static std::atomic<int> hundreds(0);
    const int start = 100 * hundreds++;
    const int end = start + 100;
    int sum = 0;
    for (int i=start; i < end; ++i) {
        if (is_prime(i)) { printf("%d is prime\n", i); sum += i; }
    }
    return sum;
}

int spawn_workers(int N)
{
    std::vector<std::future<int>> waitables;
    for (int i=0; i < N; ++i) {
        std::future<int> f = std::async(std::launch::async, worker);
        waitables.emplace_back(std::move(f));
    }

    int sum = 0;
    for (std::future<int> &f : waitables) {
        sum += f.get();  /* may throw an exception */
    }
    return sum;
    /* But watch out! When f.get() throws an exception, we still need
     * to unwind the stack, which means destructing "waitables" and each
     * of its elements. The destructor of each std::future will block
     * as if calling this->wait(). So in fact this may not do what you
     * really want. */
}

int main()
{
    try {
        int sum = spawn_workers(100);
        printf("sum is %d\n", sum);
    } catch (std::exception &e) {
        /* This line will be printed after all the prime-number output. */
        printf("Caught %s\n", e.what());
    }
}

I just tried to write a work-alike example using std::thread and std::exception_ptr, but something's going wrong with std::exception_ptr (using libc++) so I haven't gotten it to actually work yet. :(

[EDIT, 2017:

int main() {
    std::exception_ptr e;
    std::thread t1([&e](){
        try {
            ::operator new(-1);
        } catch (...) {
            e = std::current_exception();
        }
    });
    t1.join();
    try {
        std::rethrow_exception(e);
    } catch (const std::bad_alloc&) {
        puts("Success!");
    }
}

I have no idea what I was doing wrong in 2013, but I'm sure it was my fault.]

6
votes

You problem is that you could receive multiple exceptions, from multiple threads, as each could fail, perhaps from different reasons.

I am assuming the main thread is somehow waiting for the threads to end to retrieve the results, or checking regularly the other threads' progress, and that access to shared data is synchronized.

Simple solution

The simple solution would be to catch all exceptions in each thread, record them in a shared variable (in the main thread).

Once all threads finished, decide what to do with the exceptions. This means that all other threads continued their processing, which perhaps, is not what you want.

Complex solution

The more complex solution is have each of your threads check at strategic points of their execution, if an exception was thrown from another thread.

If a thread throws an exception, it is caught before exiting the thread, the exception object is copied into some container in the main thread (as in the simple solution), and some shared boolean variable is set to true.

And when another thread tests this boolean, it sees the execution is to be aborted, and aborts in a graceful way.

When all thread did abort, the main thread can handle the exception as needed.

4
votes

An exception thrown from a thread will not be catchable in the parent thread. Threads have different contexts and stacks, and generally the parent thread is not required to stay there and wait for the children to finish, so that it could catch their exceptions. There is simply no place in code for that catch:

try
{
  start thread();
  wait_finish( thread );
}
catch(...)
{
  // will catch exceptions generated within start and wait, 
  // but not from the thread itself
}

You will need to catch exceptions inside each thread and interpret exit status from threads in the main thread to re-throw any exceptions you might need.

BTW, in the absents of a catch in a thread it is implementation specific if stack unwinding will be done at all, i.e. your automatic variables' destructors may not even be called before terminate is called. Some compilers do that, but it's not required.

3
votes

Could you serialize the exception in the worker thread, transmit that back to the main thread, deserialize, and throw it again? I expect that for this to work the exceptions would all have to derive from the same class (or at least a small set of classes with the switch statement thing again). Also, I'm not sure that they would be serializable, I'm just thinking out loud.

2
votes

There is, indeed, no good and generic way to transmit exceptions from one thread to the next.

If, as it should, all your exceptions derive from std::exception, then you can have a top-level general exception catch that will somehow send the exception to the main thread where it will be thrown again. The problem being you loose the throwing point of the exception. You can probably write compiler-dependent code to get this information and transmit it though.

If not all your exception inherit std::exception, then you are in trouble and have to write a lot of top-level catch in your thread ... but the solution still hold.

1
votes

You will need to do a generic catch for all exceptions in the worker (including non-std exceptions, like access violations), and send a message from the worker thread (i suppose you have some kind of messaging in place?) to the controlling thread, containing a live pointer to the exception, and rethrow there by creating a copy of the exception. Then the worker can free the original object and exit.

1
votes

See http://www.boost.org/doc/libs/release/libs/exception/doc/tutorial_exception_ptr.html. It is also possible to write a wrapper function of whatever function you call to join a child thread, which automatically re-throws (using boost::rethrow_exception) any exception emitted by a child thread.