Search code examples
c++c++11exceptionopenmp

Elegant exception handling in OpenMP


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)?


Solution

  • 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()