Provide Python hook for asynchronous exception handler
When creating a SyclQueue, or SyclContext one should be able to define an exception handler in Python.
One operation that can through an exception, or asynchronous SYCL exception should be wrapped in try{ } catch(std::exception &e) { }.
Per https://github.com/IntelPython/dpnp/issues/201#issuecomment-719569082 the asynchronous exception can be caught, converted to dpctl.SyclException instances, and provided to a user-defined function for handling.
@oleksandr-pavlyk I was thinking of implementing it as follows:
In the C API, add a functor that is initialized with a Python callback function and invokes the callback inside a async_handler function. In the Cython-layer, we need a function that switches over the error codes passed to the callback function and then
raise a PyErr.
I am actually not totally sure on the Cython/Python side of things, but for the C-API, following is what I had in mind:
#include <iostream>
#include <functional>
#include <CL/sycl.hpp>
class invalid_kernel;
using namespace cl::sycl;
namespace {
void py_exception_handler (int err_code) {
std::cerr << "This is the callback that Cython passed to dpctl C-API."
"Error code (" << err_code << ")\n";
}
struct DPCTL_AsycErrorHandler {
void (*exception_handler)(int err_code) = nullptr;
DPCTL_AsycErrorHandler (void (*py_err_handler)(int err_code))
: exception_handler(py_err_handler)
{ }
void operator() (const exception_list &exceptions) {
for (std::exception_ptr const& e : exceptions) {
try {
std::rethrow_exception(e);
} catch(cl::sycl::exception const& e) {
std::cerr << "ASYNC error handling\n";
std::cerr << "Caught asynchronous SYCL exception:\n"
<< e.what() << std::endl;
if(exception_handler) {
// In the spec but not supported yet
//auto err_code = e.code().value();
auto err_code = -1;
exception_handler(err_code);
}
}
}
}
};
} /* end of anonymous namespace */
int main(int, char**) {
cl::sycl::queue queue(cl::sycl::default_selector{},
DPCTL_AsycErrorHandler(py_exception_handler));
queue.submit([&] (cl::sycl::handler& cgh) {
auto range = cl::sycl::nd_range<1>(cl::sycl::range<1>(1), cl::sycl::range<1>(10));
cgh.parallel_for<class invalid_kernel>(range, [=] (cl::sycl::nd_item<1>) {});
});
try {
queue.wait_and_throw();
} catch (cl::sycl::exception const& e) {
std::cerr << "Caught synchronous SYCL exception:\n" << e.what() << std::endl;
}
return 0;
}
@oleksandr-pavlyk I pushed an as yet untested implementation of DPCTLSyclQueue_Create to my WIP branch that implements the async_handler feature. I expect this function to replace the DPCTLQueueMgr_GetQueueFromContextAndDevice function and to be used everywhere we create queues in C API and Cython.
Will appreciate your feedback.
@diptorupd The sycl::exception currently provides get_cl_code() method.
Configurable Python async error handler is not as useful as I thought it might be.
Instead of allowing a user to write one, dpctl provides a default async error handler in branch featute/queue_manager_v2
dpctl already supports setting of sycl::async_handler in sycl::queue and sycl::context. The default handler implemented in dpctl/_sycl_queue.pyx` is used.
It is not user configurable.
C-API should support custom synchronous error handler to allow information contained in C++ exceptions to cross C-interface.
The handler should take an error code enum and a string (e.what()) as artguments and return void.
The default handler for C user may be to echo to std::cerr, but dpctl should set to translate error code & error message to Python exceptions.
@oleksandr-pavlyk @vlad-perevezentsev Is solving this issue still on the cards? Can we close the issue based on Vlad's error handler redesign?
Closing as no longer relevant. The async exception handler is a C++ callable which takes a C++ argument. Passing such a callable from C (created in Python, but most importantly, passed to queue construction via C interface just does not work).
Moreover, we could not identify sensible use-cases that would become possible should custom async handler be available for Python user to program.