How can I propagate exceptions between threads?

pauldoo picture pauldoo · Oct 24, 2008 · Viewed 46.8k times · Source

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.

Answer

Gerardo Hernandez picture Gerardo Hernandez · Sep 6, 2015

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.