How to throw exceptions between threads?

We have a function that is accessed by a single thread (we call this the main thread). Inside the function, we create several workflows for intensive work with the CPU, wait for the completion of all threads, and then return the result to the main thread.

As a result, the caller can naively use the function, and internally it will use multiple cores.

Everything is fine so far ..

The problem we are dealing with is exceptions. We do not want workflow exceptions to cause the application to crash. We want the calling function to catch them in the main thread. We need to catch exceptions in worker threads and distribute them to the main thread so that they continue to unwind from there.

How can we do this?

The best I can think of:

  • Grab the many exceptions from our worker threads (std :: exception and a few of our own).
  • Record the type and message of the exception.
  • Have an appropriate switch statement in the main thread that throws exceptions of any type written to the workflow.

This has an obvious drawback only in supporting a limited set of exception types and will need to be modified whenever new exception types are added.

+78
c ++ multithreading exception
Oct 24 '08 at 11:17
source share
9 answers

In C ++ 11, the exception_ptr type was introduced, which allows you to carry 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; } 

Since in your case you have several worker threads, you need to save one exception_ptr for each of them.

Note that exception_ptr is a generic pointer of type ptr, so you need to keep at least one exception_ptr pointing to each exception, or they will be thrown.

Microsoft's specificity: if you use SEH exceptions (/ EHa), the example code also suffers SEH exceptions, such as access violations, which may not be what you want.

+36
Sep 06 '15 at 21:22
source share

Currently, the only portable way is to record statements for all types of exceptions that you could pass between threads, store information somewhere from this catch condition, and then use it later to throw an exception. This is the approach used by Boost.Exception .

In C ++ 0x, you can catch an exception with catch(...) and then save it in an instance of std::exception_ptr with std::current_exception() . Then you can throw it from the same or different thread with std::rethrow_exception() .

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

+68
Oct 24 '08 at 13:47
source share

The problem is that you can get multiple exceptions from multiple threads, as each of them may fail, possibly for various reasons.

I assume that the main thread is somehow waiting for the threads to complete in order to get results or regularly monitor the progress of other threads and provide access to shared data.

A simple solution

A simple solution would be to catch all the exceptions in each thread, write them to a common variable (in the main thread).

Once all threads have run out, decide what to do with the exceptions. This means that all other threads have continued processing, which may not be what you want.

Complete solution

A more complicated solution is that each of your threads checks at strategic points of their execution if an exception was thrown from another thread.

If a stream throws an exception, it is captured before exiting the stream, the exception object is copied to some container in the main stream (as in a simple solution), and for some common logical variable it is set to true.

And when another thread checks this boolean, it sees that execution should be aborted and aborted in an elegant way.

When all threads are canceled, the main thread can handle the exception as needed.

+5
Oct 24 '08 at 11:48
source share

An exception thrown from a thread will not be catching in the parent thread. Topics have different contexts and stacks, and as a rule, the parent thread should not stay there and wait until the children run out so that they can catch their exceptions. There is no place in this code in the code:

 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 the exceptions within each thread and interpret the exit status of the threads in the main thread to re-throw any exceptions you may need.

BTW, in the absence of a trick in the stream, this is a specific implementation, if the expansion of the stack will be performed at all, i.e. the destructors of your automatic variables may not even be called before terminate is called. Some compilers do this, but this is not required.

+4
Oct 24 '08 at 11:54
source share

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

+3
Oct 24 '08 at 11:45
source share

If you use C ++ 11, then std::future can do exactly what you are looking for: it can automatically catch exceptions that fall at the top of the workflow and pass them to the parent element, the thread at the point at which std::future::get is called std::future::get . (Behind the scenes, this happens exactly the same as in @AnthonyWilliams answer, it has just been implemented for you already.)

On the other hand, there is no standard way to “stop caring” about std::future ; even its destructor will simply block until the task is completed. [EDIT, 2017: The behavior of a blocking destructor is an error only for pseudo-releases returned from std::async , which you should never use in any case. Normal futures are not blocked in their destructor. But you still can't “cancel” tasks if you use std::future : fulfilled tasks promises will be kept behind the scenes, even if no one else is responsible for the answer.] Here's a toy example that can 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 an example that looked like work using std::thread and std::exception_ptr , but something was wrong with std::exception_ptr (using libC ++), so I haven't actually got it 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 don’t know what I did wrong in 2013, but I’m sure it was my mistake.]

+3
Jan 25 '13 at 1:18
source share

There really is no good and general way to pass exceptions from one thread to the next.

If, as it should be, all of your exceptions come from std :: exception, then you can have a general round-trip of the top-level exceptions, which will somehow send the exception to the main thread, where it will be thrown again. The problem is that you are losing the point of throwing an exception. You can probably write compiler dependent code to get this information and pass it though.

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

+1
Oct 24 '08 at 11:39
source share

You will need to make a general catch for all exceptions in the workplace (including exceptions for non-std, such as access violations) and send a message from the workflow (suppose you have some kind of messaging in place?) To the control thread, containing a live pointer to the exception, and reconstruct there by creating a copy of the exception. Then the worker can free the source object and exit.

+1
Oct 24 '08 at 11:47
source share

See http://www.boost.org/doc/libs/release/libs/exception/doc/tutorial_exception_ptr.html . You can also write a wrapper function of any function that you call to join the child thread, which automatically throws (using boost :: rethrow_exception) any exception thrown by the child thread.

+1
Jan 14 '10 at 7:55
source share



All Articles