What happens when an exception goes unhandled in a multithreaded C++11 program?

C++MultithreadingExceptionC++11C++ Faq

C++ Problem Overview


If I have a C++11 program running two threads, and one of them throws an unhandled exception, what happens? Will the entire program die a fiery death? Will the thread where the exception is thrown die alone (and if so, can I obtain the exception in this case)? Something else entirely?

C++ Solutions


Solution 1 - C++

Nothing has really changed. The wording in n3290 is:

> If no matching handler is found, the function std::terminate() is called

The behavior of terminate can be customized with set_terminate, but:

> Required behavior: A terminate_handler shall terminate execution of the program without returning to the caller.

So the program exits in such a case, other threads cannot continue running.

Solution 2 - C++

Since there seems to be legitimate interest in exception propagation and this is slightly at least somewhat relevant to the question, here's my suggestion: std::thread is to be considered an unsafe primitive to build e.g. higher level abstractions. They're doubly risky exception-wise: if an exception goes off inside the thread we just launched, everything blows up, as we've shown. But if an exception goes off in the thread that launched the std::thread we may potentially be in trouble because std::thread's destructor requires that *this be either joined or detached (or equivalently, be not-a-thread). A violation of those requirements results in... a call to std::terminate!

A code map of the dangers of std::thread:

auto run = []
{
    // if an exception escapes here std::terminate is called
};
std::thread thread(run);

// notice that we do not detach the thread
// if an exception escapes here std::terminate is called

thread.join();
// end of scope

Of course, some may argue that if we simply detached every thread that we launch we're safe on that second point. The problem with that is that in some situations join is the most sensible thing to do. The 'naive' parallelization of quicksort for instance requires to wait until the subtasks have ended. In those situations join serves as a synchronization primitive (a rendez-vous).

Lucky for us, those higher level abstractions I mentioned do exist and come with the Standard Library. They are std::async, std::future as well as std::packaged_task, std::promise and std::exception_ptr. The equivalent, exception-safe version of the above:

auto run = []() -> T // T may be void as above
{
    // may throw
    return /* some T */;
};

auto launched = std::async(run);
// launched has type std::future<T>

// may throw here; nothing bad happens

// expression has type T and may throw
// will throw whatever was originally thrown in run
launched.get();

And in fact instead of calling get in the thread that called async you can instead pass the buck to another thread:

// only one call to get allowed per std::future<T> so
// this replaces the previous call to get
auto handle = [](std::future<T> future)
{
    // get either the value returned by run
    // or the exception it threw
    future.get();
};

// std::future is move-only
std::async(handle, std::move(launched));
// we didn't name and use the return of std::async
// because we don't have to

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionR. Martinho FernandesView Question on Stackoverflow
Solution 1 - C++Ben VoigtView Answer on Stackoverflow
Solution 2 - C++Luc DantonView Answer on Stackoverflow