From 68757a4535dc2225e94fd96a033378c4b244bde8 Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner Date: Sat, 11 Aug 2018 23:58:25 -0500 Subject: [PATCH] [pybind11] Implement Event.set_callback --- src/wrap_cl.hpp | 103 +++++++++++++++++++++++++++++++++++++++++ src/wrap_cl_part_1.cpp | 3 ++ test/test_wrapper.py | 2 +- 3 files changed, 107 insertions(+), 1 deletion(-) diff --git a/src/wrap_cl.hpp b/src/wrap_cl.hpp index 36881b9f..6c004171 100644 --- a/src/wrap_cl.hpp +++ b/src/wrap_cl.hpp @@ -46,6 +46,10 @@ #endif +#include +#include +#include + #include #include #include @@ -1345,6 +1349,105 @@ namespace pyopencl { PYOPENCL_CALL_GUARDED_THREADED(clWaitForEvents, (1, &m_event)); } + +#if PYOPENCL_CL_VERSION >= 0x1010 + // {{{ set_callback, by way of a a thread-based construction + + private: + struct event_callback_info_t + { + std::mutex m_mutex; + std::condition_variable m_condvar; + + py::object m_py_event; + py::object m_py_callback; + + bool m_set_callback_suceeded; + + cl_event m_event; + cl_int m_command_exec_status; + + event_callback_info_t(py::object py_event, py::object py_callback) + : m_set_callback_suceeded(true), m_py_event(py_event), m_py_callback(py_callback) + {} + }; + + static void evt_callback(cl_event evt, cl_int command_exec_status, void *user_data) + { + event_callback_info_t *cb_info = reinterpret_cast(user_data); + { + std::lock_guard lg(cb_info->m_mutex); + cb_info->m_event = evt; + cb_info->m_command_exec_status = command_exec_status; + } + cb_info->m_condvar.notify_one(); + } + + public: + void set_callback(cl_int command_exec_callback_type, py::object pfn_event_notify) + { + // The reason for doing this via a thread is that we're able to wait on + // acquiring the GIL. (which we can't in the callback) + + std::unique_ptr cb_info_holder( + new event_callback_info_t( + handle_from_new_ptr(new event(*this)), + pfn_event_notify)); + event_callback_info_t *cb_info = cb_info_holder.get(); + + std::thread notif_thread([cb_info]() + { + std::unique_lock ulk(cb_info->m_mutex); + cb_info->m_condvar.wait(ulk); + + { + py::gil_scoped_acquire acquire; + + if (cb_info->m_set_callback_suceeded) + { + try { + cb_info->m_py_callback( + // cb_info->m_py_event, + cb_info->m_command_exec_status); + } + catch (std::exception &exc) + { + std::cerr + << "[pyopencl] event callback handler threw an exception, ignoring: " + << exc.what() + << std::endl; + } + } + + // Need to hold GIL to delete py::object instances in + // event_callback_info_t + delete cb_info; + } + }); + // Thread is away--it is now its responsibility to free cb_info. + cb_info_holder.release(); + + // notif_thread should no longer be coupled to the lifetime of the thread. + notif_thread.detach(); + + try + { + PYOPENCL_CALL_GUARDED(clSetEventCallback, ( + data(), command_exec_callback_type, &event::evt_callback, cb_info)); + } + catch (...) { + // Setting the callback did not succeed. The thread would never + // be woken up. Wake it up to let it know that it can stop. + { + std::lock_guard lg(cb_info->m_mutex); + cb_info->m_set_callback_suceeded = false; + } + cb_info->m_condvar.notify_one(); + throw; + } + } + // }}} +#endif }; #ifdef PYOPENCL_USE_NEW_BUFFER_INTERFACE diff --git a/src/wrap_cl_part_1.cpp b/src/wrap_cl_part_1.cpp index 785427ee..a88dc756 100644 --- a/src/wrap_cl_part_1.cpp +++ b/src/wrap_cl_part_1.cpp @@ -111,6 +111,9 @@ void pyopencl_expose_part_1(py::module &m) .def(py::self != py::self) .def("__hash__", &cls::hash) PYOPENCL_EXPOSE_TO_FROM_INT_PTR(cl_event) +#if PYOPENCL_CL_VERSION >= 0x1010 + .DEF_SIMPLE_METHOD(set_callback) +#endif ; } { diff --git a/test/test_wrapper.py b/test/test_wrapper.py index bba4ca6a..45d04b54 100644 --- a/test/test_wrapper.py +++ b/test/test_wrapper.py @@ -793,7 +793,7 @@ def test_event_set_callback(ctx_factory): queue = cl.CommandQueue(ctx) if ctx._get_cl_version() < (1, 1): - pytest.skip("OpenCL 1.1 or newer required fro set_callback") + pytest.skip("OpenCL 1.1 or newer required for set_callback") a_np = np.random.rand(50000).astype(np.float32) b_np = np.random.rand(50000).astype(np.float32) -- GitLab