14

OpenMP forbids code which leaves the openmp block via exception. Therefore I'm looking for a nice way of getting the exceptions from an openmp block with the purpose of rethrowing it in the main thread and handling at a later point. So far the best I've been able to come up with is the following:

class ThreadException {
    std::exception_ptr Ptr;
    std::mutex         Lock;
public:
    ThreadException(): Ptr(nullptr) {}
    ~ThreadException(){ this->Rethrow(); }  
    void Rethrow(){
        if(this->Ptr) std::rethrow_exception(this->Ptr);
    }
    void CaptureException() { 
        std::unique_lock<std::mutex> guard(this->Lock);
        this->Ptr = std::current_exception(); 
    }   
};
//...
ThreadException except;
#pragma omp parallel
{
    try {
      //some possibly throwing code
    } 
    catch(...) { except.CaptureException(); }
}

While this works nicely, rethrowing possible exceptions from the parallel section as soon as the ThreadException object is destroyed, this construct is still a bit unwieldy to use with putting a try {}catch(...){} around each section and having to manually capture the exception.

So my question is: Does anyone know a more elegant (less verbose) way to do this (and if so, what does it look like)?

Seth Johnson
  • 14,762
  • 6
  • 59
  • 85
Grizzly
  • 19,595
  • 4
  • 60
  • 78
  • How would you handle the case when two or more threads throw exceptions (possibly different ones)? – Hristo Iliev Aug 06 '12 at 13:04
  • 1
    @HristoIliev: By ignoring one of them (since I can't throwmore then one exception anyways) and only rethrowing the last one. – Grizzly Aug 06 '12 at 13:13
  • 1
    Throwing from a destructor is illegal (I've used a library that did this, and it brought me a lot of headaches until I figured out why my application kept aborting without catching the exception). You'd have to call `except.Rethrow()` after the parallel section. This would be better any way if there is sequential code after the parallel section that you don't want to execute if an exception occurred. – Cris Luengo Aug 29 '17 at 22:14
  • 1
    Using `std::mutex` may result in undefined behavior I think. See https://stackoverflow.com/a/53516587/5861244. However, `#pragma omp critical` should work here. – Benjamin Christoffersen Feb 15 '21 at 13:17
  • Or https://stackoverflow.com/a/41316118/5861244. – Benjamin Christoffersen Feb 15 '21 at 13:20
  • You can also use some of the locks provided by openmp if you have multiple different instances of `ThreadException` which should not share the same critical section. See https://www.openmp.org/spec-html/5.0/openmpse31.html. – Benjamin Christoffersen Feb 15 '21 at 13:45

1 Answers1

18

You can utilize a few more C++11 tools to clean up the syntax a bit. Add this variadic member function to your ThreadException class:

class ThreadException {

    // ...

    template <typename Function, typename... Parameters>
    void Run(Function f, Parameters... params)
    {
        try 
        {
            f(params...);
        }
        catch (...)
        {
            CaptureException();
        }
    }
 };

Then when calling inside an OpenMP construct use a lambda function like so:

ThreadException e;

#pragma omp parallel for
for (int i = 0; i < n; i++)
{
    e.Run([=]{
        // code that might throw
        // ...
    });
}
e.Rethrow()
Dave Clemmer
  • 3,741
  • 12
  • 49
  • 72
Kyle Spagnoli
  • 196
  • 2
  • 4