% -*- latex -*-

\chapter{Try Execute}
\label{chap:TryExecute}

\index{try execute|(}
\index{device adapter!try execute|(}

Throughout this chapter and elsewhere in this book we have seen examples that require specifying the device on which to run using a device adapter tag.
This is an important aspect when writing portable parallel algorithms.
However, it is often the case that users of these algorithms are agnostic about what device \VTKm algorithms run so long as they complete correctly and as fast as possible.
Thus, rather than directly specify a device adapter, you would like \VTKm to try using the best available device, and if that does not work try a different device.
Because of this, there are many features in \VTKm that behave this way.
For example, you may have noticed that running filters, as in the examples of Chapter~\ref{chap:RunningFilters}, you do not need to specify a device; they choose a device for you.

Internally, the filter superclasses have a mechanism to automatically select a device, try it, and fall back to other devices if the first one fails.
We saw this at work in the implementation of filters in Chapter~\ref{chap:CreatingFilters}.
Most of the outward facing interfaces of parallel algorithms in \VTKm are through these filter classes.
For everything else, there is the \vtkmcont{TryExecute} function.

\textidentifier{TryExecute} is a simple, generic mechanism to run an algorithm that requires a device adapter without directly specifying a device adapter.
\vtkmcont{TryExecute} is a templated function with two arguments.
The first argument is a functor object whose parenthesis operator takes a device adapter tag and returns a \textcode{bool} that is true if the call succeeds on the given device.
\index{runtime device tracker|}\index{device adapter!runtime tracker|}The second argument is a \vtkmcont{RuntimeDeviceTracker} that specifies what devices to try.
\textidentifier{RuntimeDeviceTracker} is documented in Section~\ref{sec:RuntimeDeviceTracker}.

To demonstrate the operation of \textidentifier{TryExecute}, consider an operation to find the average value of an array.
Doing so with a given device adapter is a straightforward use of the reduction operator.

\vtkmlisting[ex:ArrayAverageImpl]{A function to find the average value of an array in parallel.}{ArrayAverageImpl.cxx}

The function in Example~\ref{ex:ArrayAverageImpl} requires a device adapter.
We want to make an alternate version of this function that does not need a specific device adapter but rather finds one to use.
To do this, we first make a functor as described earlier.
It takes a device adapter tag as an argument, calls the version of the function shown in Example~\ref{ex:ArrayAverageImpl}, and returns true when the operation succeeds.
We then create a new version of the array average function that does not need a specific device adapter tag and calls \textidentifier{TryExecute} with the aforementioned functor.

\vtkmlisting[ex:ArrayAverageTryExecute]{Using \textidentifier{TryExecute}.}{ArrayAverageTryExecute.cxx}

\begin{didyouknow}
  \textidentifier{TryExecute} calls \vtkmcont{TryExecuteOnDevice} internally.
  This means that \vtkmcont{GetRuntimeDeviceTracker} is automatically called and ensures execution on devices that have been enabled.
  It is up to the application to enable/disable devices before calling \textidentifier{TryExecute} using the methods provided in Section~\ref{sec:RuntimeDeviceTracker}.
\end{didyouknow}

\begin{commonerrors}
  When \textidentifier{TryExecute} calls the operation of your functor, it will catch any exceptions that the functor might throw.
  \textidentifier{TryExecute} will interpret any thrown exception as a failure on that device and try another device.
  If all devices fail, \textidentifier{TryExecute} will return a false value rather than throw its own exception.
  This means if you want to have an exception thrown from a call to \textidentifier{TryExecute}, you will need to check the return value and throw the exception yourself.
\end{commonerrors}

\index{device adapter!try execute|)}
\index{try execute|)}
