## Handling Errors and Debugging
## Learning Objectives * Learn about how SYCL handles errors * Learn about the difference between synchronous and asynchronous exceptions * Learn how to handle exceptions and retrieve further information * Learn about the host device and how to use it
## SYCL exceptions * In SYCL errors are handled by throwing exceptions. * It is crucial that these errors are handled, otherwise your application could fail in unpredictable ways. * In SYCL there are two kinds of error: * Synchronous errors (thrown in user thread). * Asynchronous errors (thrown by the SYCL scheduler).
#### SYCL exceptions
![SYCL](../../Static/images/sycl-exceptions.png "SYCL")
#### Handling errors

#include <sycl/sycl.hpp>
using namespace sycl;

int main() {
  queue q;

  /* Synchronous code */ 
  q.single_task([=]() {
    /* Asynchronous code */
  });
  /* Synchronous code */
  q.wait();
}
						
* Code on the device runs asynchronously * If errors are not handled, the application can fail: * A default async handler that will call `std::terminate` when an asynchronous error is thrown.

#include <sycl/sycl.hpp>
using namespace sycl;

int main() {
  try {
    queue q; 
    int* shared = malloc_shared<int>(1024, q);   
    q.parallel_for(1024, [=](id<1> i) {
      shared[i] = i;
    });
    q.wait_and_throw();
    free(shared, q);
  } catch (...) { /* handle errors */ }
}
            
* Synchronous errors are typically thrown by SYCL API functions. * In order to handle all SYCL errors you must wrap everything in a try-catch block.

#include <sycl/sycl.hpp>
using namespace sycl;

int main() {
  try {
    queue q(async_handler{});
    int* shared = malloc_shared<int>(1024, q);   
    q.parallel_for(1024, [=](id<1> i) {
      shared[i] = i;
    });
    q.wait_and_throw();
    free(shared, q);
  } catch (...) { /* handle errors */ }
}
              
* Asynchronous errors that may have occurred will be thrown after a command has been submitted to a `queue`. * To handle these errors you must provide an async handler when constructing the queue object. * Then you must also call the `throw_asynchronous` or `wait_and_throw` member functions of the `queue` class. * This will pass the exceptions to the async handler in the user thread so they can be thrown.

#include<sycl/sycl.hpp>
using namespace sycl;

int main() {
  try {
    queue q([=](exception_list eL) {
        for (auto e : eL) { std::rethrow_exception(e); 
      }
    });
    int* shared = malloc_shared<int>(1024, q);
    q.parallel_for(1024, [=](id<1> i) {
      shared[i] = i;
    });
    q.wait_and_throw();
    free(shared, q);
  } catch (...) { /* handle errors */ }
}
						
* The async handler is a C++ lambda or function object that takes as a parameter an `exception_list` * The exception_list class is a wrapper around a list of `exception_ptrs` which can be iterated over * The exception_ptrs can be rethrown by passing them to `std::rethrow_exception`

#include<sycl/sycl.hpp>
#include<iostream>
using namespace sycl;

int main() {
  try {
    queue q([=](exception_list eL) {
      for (auto e : eL) { std::rethrow_exception(e); }
    });
    /* ... */
    q.wait_and_throw();
  } catch (const exception& e) {
  std::cout << "Exception caught: " << e.what() 
     << std::endl;
  }
}
							
* Once rethrown and caught, a SYCL exception can provide information about the error * The `what` member function will return a string with more details

#include<sycl/sycl.hpp>
#include<iostream>

using namespace sycl;

int main() {
  try {
    queue q([=](exception_list eL) {
      for (auto e : eL) { std::rethrow_exception(e); }
    });

    /* ... */
    q.wait_and_throw();
  } catch (const exception& e) {
    std::cout << "Exception caught: " << e.what();
  }
}
							
* SYCL 2020 provides the `error_category_for` templated free function that allows checking for the category of the exception depending on the backend used.

#include<sycl/sycl.hpp>
using namespace sycl;

int main() {
  queue q([=](exception_list eL) {
    for (auto e : eL) { std::rethrow_exception(e); }
  });
  try {
    /* ... */
    q.wait_and_throw();
  } catch (const sycl::exception& e) {
    if (e.has_context()) {
      if (e.get_context() == q.get_context()) {
       /* handle error */
      }
    }
  }
}
							
* The `has_context` exception member function will tell you if there is a SYCL context associated with the error * If that returns true then the `get_context` member function will return the associated SYCL context object
## Exception Types
* SYCL 2020 only has a single `sycl::exception` type which provides different error codes * E.g. `errc::runtime`, `errc::kernel`
## Debugging SYCL Kernel Functions
* SYCL 2020 only guarantees that a device will always be available, and users can query the `host_debuggable` device aspect to check whether they can use the same functionality as the SYCL 1.2.1 host device

#include<sycl/sycl.hpp>
using namespace sycl;

int main() {
  try {
    queue q(cpu_selector_v, async_handler{}); 
    int* shared = malloc_shared<int>(1024, q);
    q.parallel_for(1024, [=](id<1> i) {
      shared[i] = i;
    });
    q.wait_and_throw();
    free(shared, q);
  } catch (...) { /* handle errors */ }
}

						
* Any SYCL application can be debugged on the host device by switching the queue for a host queue * Replacing the device selector for the `aspect_selector` will ensure that the queue submits all work to the device with the requested aspects, in this case a host debuggable device
## Questions
#### Exercise
Code_Exercises/Handling_Errors/source
Add error handling to a SYCL application for both synchronous and asynchronous errors.