How to: Convert an OpenMP Loop that Uses Exception Handling to Use the Concurrency Runtime

This example demonstrates how to convert an OpenMP parallelfor loop that performs exception handling to use the Concurrency Runtime exception handling mechanism.

In OpenMP, an exception that is thrown in a parallel region must be caught and handled in the same region by the same thread. An exception that escapes the parallel region is caught by the unhandled exception handler, which terminates the process by default.

In the Concurrency Runtime, when you throw an exception in the body of a work function that you pass to a task group such as a concurrency::task_group or concurrency::structured_task_group object, or to a parallel algorithm such as concurrency::parallel_for, the runtime stores that exception and marshals it to the context that waits for the task group or algorithm to finish. For task groups, the waiting context is the context that calls concurrency::task_group::wait, concurrency::structured_task_group::wait, concurrency::task_group::run_and_wait, or concurrency::structured_task_group::run_and_wait. For a parallel algorithm, the waiting context is the context that called that algorithm. The runtime also stops all active tasks that are in the task group, including those in child task groups, and discards any tasks that have not yet started.

Example

This example demonstrates how to handle exceptions in an OpenMP parallel region and in a call to parallel_for. The do_work function performs a memory allocation request that does not succeed and therefore throws an exception of type std::bad_alloc. In the version that uses OpenMP, the thread that throws the exception must also catch it. In other words, each iteration of an OpenMP parallel loop must handle the exception. In the version that uses the Concurrency Runtime, the main thread catches an exception that is thrown by another thread.

// concrt-omp-exceptions.cpp 
// compile with: /EHsc /openmp
#include <ppl.h>
#include <new>
#include <iostream>
#include <sstream>

using namespace concurrency;
using namespace std;

// Demonstrates a function that performs a memory allocation request  
// that does not succeed. 
void do_work(int)
{
   // The following memory allocation causes this function to  
   // throw std::bad_alloc. 
   char* ptr = new char[(~unsigned int((int)0)/2) - 1];

   // TODO: Assuming that the allocation succeeds, perform some work  
   // and free the allocated memory. 

   delete[] ptr;
}

// Demonstrates an OpenMP parallel loop that performs exception handling. 
void omp_exception_handling()
{
   #pragma omp parallel for  
      for(int i = 0; i < 10; i++) 
      {
         try {
            // Perform a unit of work.
            do_work(i);
         }
         catch (exception const& e) {
            // Print the error to the console.
            wstringstream ss;
            ss << L"An error of type '" << typeid(e).name() 
               << L"' occurred." << endl;
            wcout << ss.str();
         }
      }
}

// Demonstrates an Concurrency Runtime parallel loop that performs exception handling. 
void concrt_exception_handling()
{
   try {
      parallel_for(0, 10, [](int i) 
      {
         // Perform a unit of work.
         do_work(i);
      });
   }
   catch (exception const& e) {
      // Print the error to the console.
      wcout << L"An error of type '" << typeid(e).name() 
            << L"' occurred." << endl;
   }
}

int wmain()
{
   wcout << L"Using OpenMP..." << endl;
   omp_exception_handling();

   wcout << L"Using the Concurrency Runtime..." << endl;
   concrt_exception_handling();
}

This example produces the following output.

Using OpenMP...
An error of type 'class std::bad_alloc' occurred.
An error of type 'class std::bad_alloc' occurred.
An error of type 'class std::bad_alloc' occurred.
An error of type 'class std::bad_alloc' occurred.
An error of type 'class std::bad_alloc' occurred.
An error of type 'class std::bad_alloc' occurred.
An error of type 'class std::bad_alloc' occurred.
An error of type 'class std::bad_alloc' occurred.
An error of type 'class std::bad_alloc' occurred.
An error of type 'class std::bad_alloc' occurred.
Using the Concurrency Runtime...
An error of type 'class std::bad_alloc' occurred.

In the version of this example that uses OpenMP, the exception occurs in and is handled by each loop iteration. In the version that uses the Concurrency Runtime, the runtime stores the exception, stops all active tasks, discards any tasks that have not yet started, and marshals the exception to the context that calls parallel_for.

If you require that the version that uses OpenMP terminates after the exception occurs, you could use a Boolean flag to signal to other loop iterations that the error occurred. As in the example in the topic How to: Convert an OpenMP Loop that Uses Cancellation to Use the Concurrency Runtime, subsequent loop iterations would do nothing if the flag is set. Conversely, if you require that the loop that uses the Concurrency Runtime continues after the exception occurs, handle the exception in the parallel loop body itself.

Other components of the Concurrency Runtime, such as asynchronous agents and lightweight tasks, do not transport exceptions. Instead, unhandled exceptions are caught by the unhandled exception handler, which terminates the process by default. For more information about exception handling, see Exception Handling in the Concurrency Runtime.

For more information about parallel_for and other parallel algorithms, see Parallel Algorithms.

Compiling the Code

Copy the example code and paste it in a Visual Studio project, or paste it in a file that is named concrt-omp-exceptions.cpp and then run the following command in a Visual Studio Command Prompt window.

cl.exe /EHsc /openmp concrt-omp-exceptions.cpp

See Also

Concepts

Migrating from OpenMP to the Concurrency Runtime

Exception Handling in the Concurrency Runtime

Parallel Algorithms