4.5. Worklet Error Handling

It is sometimes the case during the execution of an algorithm that an error condition can occur that causes the computation to become invalid. At such a time, it is important to raise an error to alert the calling code of the problem. Since VTK‑m uses an exception mechanism to raise errors, we want an error in the execution environment to throw an exception.

However, throwing exceptions in a parallel algorithm is problematic. Some accelerator architectures, like CUDA, do not even support throwing exceptions. Even on architectures that do support exceptions, throwing them in a thread block can cause problems. An exception raised in one thread may or may not be thrown in another, which increases the potential for deadlocks, and it is unclear how uncaught exceptions progress through thread blocks.

VTK‑m handles this problem by using a flag and check mechanism. When a worklet (or other subclass of vtkm::exec::FunctorBase) encounters an error, it can call its vtkm::exec::FunctorBase::RaiseError() method to flag the problem and record a message for the error. Once all the threads terminate, the scheduler checks for the error, and if one exists it throws a vtkmcont{ErrorExecution} exception in the control environment. Thus, calling vtkm::exec::FunctorBase::RaiseError() looks like an exception was thrown from the perspective of the control environment code that invoked it.

Example 4.63 Raising an error in the execution environment.
 1struct SquareRoot : vtkm::worklet::WorkletMapField
 2{
 3public:
 4  using ControlSignature = void(FieldIn, FieldOut);
 5  using ExecutionSignature = _2(_1);
 6
 7  template<typename T>
 8  VTKM_EXEC T operator()(T x) const
 9  {
10    if (x < 0)
11    {
12      this->RaiseError("Cannot take the square root of a negative number.");
13      return vtkm::Nan<T>();
14    }
15    return vtkm::Sqrt(x);
16  }
17};

It is also worth noting that the VTKM_ASSERT macro described in Section 2.9.2 (Asserting Conditions) also works within worklets and other code running in the execution environment. Of course, a failed assert will terminate execution rather than just raise an error so is best for checking invalid conditions for debugging purposes.