[llvm] [libsycl] extend sycl::event API with get_wait_list, wait_and_throw and default ctor (PR #205860)
Kseniya Tikhomirova via llvm-commits
llvm-commits at lists.llvm.org
Mon Jun 29 05:37:26 PDT 2026
https://github.com/KseniyaTikhomirova updated https://github.com/llvm/llvm-project/pull/205860
>From 240751af904e96940a6350c151df6071c07bff8a Mon Sep 17 00:00:00 2001
From: "Tikhomirova, Kseniya" <kseniya.tikhomirova at intel.com>
Date: Thu, 18 Jun 2026 07:53:51 -0700
Subject: [PATCH] [libsycl] extend sycl::event API
Signed-off-by: Tikhomirova, Kseniya <kseniya.tikhomirova at intel.com>
---
libsycl/docs/index.rst | 6 +-
.../__impl/detail/default_async_handler.hpp | 55 -------
libsycl/include/sycl/__impl/event.hpp | 30 ++++
libsycl/include/sycl/__impl/exception.hpp | 13 ++
libsycl/include/sycl/__impl/info/event.hpp | 53 ++++++
libsycl/include/sycl/__impl/queue.hpp | 46 ++++--
libsycl/src/detail/event_impl.cpp | 12 ++
libsycl/src/detail/event_impl.hpp | 36 ++++-
libsycl/src/detail/global_objects.cpp | 38 +++++
libsycl/src/detail/global_objects.hpp | 32 ++++
libsycl/src/detail/queue_impl.cpp | 52 +++---
libsycl/src/detail/queue_impl.hpp | 17 +-
libsycl/src/detail/spinlock.hpp | 47 ++++++
libsycl/src/event.cpp | 29 ++++
libsycl/src/exception_list.cpp | 23 ++-
libsycl/src/queue.cpp | 4 +
libsycl/unittests/CMakeLists.txt | 1 +
libsycl/unittests/common/device_images.hpp | 6 +-
.../common/scoped_binary_registration.hpp | 64 ++++++++
libsycl/unittests/common/unittests_helper.hpp | 1 -
.../device_selector/get_device_preference.cpp | 23 +--
libsycl/unittests/event/CMakeLists.txt | 4 +
libsycl/unittests/event/async_handler.cpp | 152 ++++++++++++++++++
libsycl/unittests/event/event.cpp | 139 ++++++++++++++++
libsycl/unittests/mock/helpers.cpp | 7 +
libsycl/unittests/mock/helpers.hpp | 1 +
libsycl/unittests/mock/mock.cpp | 4 +
.../register_and_unregister.cpp | 14 +-
.../unittests/queue/sycl_kernel_launch.cpp | 32 +---
29 files changed, 781 insertions(+), 160 deletions(-)
delete mode 100644 libsycl/include/sycl/__impl/detail/default_async_handler.hpp
create mode 100644 libsycl/include/sycl/__impl/info/event.hpp
create mode 100644 libsycl/src/detail/spinlock.hpp
create mode 100644 libsycl/unittests/common/scoped_binary_registration.hpp
create mode 100644 libsycl/unittests/event/CMakeLists.txt
create mode 100644 libsycl/unittests/event/async_handler.cpp
create mode 100644 libsycl/unittests/event/event.cpp
diff --git a/libsycl/docs/index.rst b/libsycl/docs/index.rst
index a5d657f3e6404..21120ead9fa15 100644
--- a/libsycl/docs/index.rst
+++ b/libsycl/docs/index.rst
@@ -125,7 +125,11 @@ TODO for added SYCL classes
* forward templated funcs to alignment methods (rewrite current impl)
* handle sub devices once they are implemented (blocked by liboffload support)
-* ``event``: get_wait_list, get_info, get_profiling_info, wait_and_throw & default ctor are not implemented
+* ``event``:
+
+ * get_info, get_profiling_info (no liboffload support) are not implemented
+ * get_wait_list should be aligned with the results of this discussion: https://github.com/KhronosGroup/SYCL-Docs/issues/1017
+
* ``range``, ``id`` - __SYCL_DISABLE_ID_TO_INT_CONV__ and __SYCL_ASSUME_ID_RANGE optimizations are not implemented
* general opens:
diff --git a/libsycl/include/sycl/__impl/detail/default_async_handler.hpp b/libsycl/include/sycl/__impl/detail/default_async_handler.hpp
deleted file mode 100644
index 807c8a597f074..0000000000000
--- a/libsycl/include/sycl/__impl/detail/default_async_handler.hpp
+++ /dev/null
@@ -1,55 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-///
-/// \file
-/// This file contains the definition of an implementation-defined default
-/// async_handler, which is invoked when an asynchronous error occurs in a queue
-/// or context that has no user-supplied asynchronous error handler object (see
-/// SYCL 2020 4.13.1.2).
-///
-//===----------------------------------------------------------------------===//
-
-#ifndef _LIBSYCL___IMPL_DETAIL_DEFAULT_ASYNC_HANDLER_HPP
-#define _LIBSYCL___IMPL_DETAIL_DEFAULT_ASYNC_HANDLER_HPP
-
-#include <sycl/__impl/exception.hpp>
-
-#include <iostream>
-
-_LIBSYCL_BEGIN_NAMESPACE_SYCL
-
-namespace detail {
-
-// SYCL 2020 4.13.1.2. Behavior without an async handler.
-// If an asynchronous error occurs in a queue or context that has no
-// user-supplied asynchronous error handler object async_handler, then an
-// implementation-defined default async_handler is called to handle the error in
-// the same situations that a user-supplied async_handler would be. The default
-// async_handler must in some way report all errors passed to it, when possible,
-// and must then invoke std::terminate or equivalent.
-inline void defaultAsyncHandler(exception_list ExceptionList) {
- std::cerr
- << "Implementation-defined default async_handler caught exceptions:";
- for (auto &Exception : ExceptionList) {
- try {
- if (Exception) {
- std::rethrow_exception(Exception);
- }
- } catch (const std::exception &E) {
- std::cerr << "\n\t" << E.what();
- }
- }
- std::cerr << std::endl;
- std::terminate();
-}
-
-} // namespace detail
-
-_LIBSYCL_END_NAMESPACE_SYCL
-
-#endif // _LIBSYCL___IMPL_DETAIL_DEFAULT_ASYNC_HANDLER_HPP
diff --git a/libsycl/include/sycl/__impl/event.hpp b/libsycl/include/sycl/__impl/event.hpp
index 141b804621f22..7006ffed0fbb0 100644
--- a/libsycl/include/sycl/__impl/event.hpp
+++ b/libsycl/include/sycl/__impl/event.hpp
@@ -20,6 +20,7 @@
#include <sycl/__impl/detail/config.hpp>
#include <sycl/__impl/detail/obj_utils.hpp>
#include <sycl/__impl/info/desc_base.hpp>
+#include <sycl/__impl/info/event.hpp>
#include <memory>
#include <vector>
@@ -32,11 +33,19 @@ namespace detail {
class EventImpl;
template <typename T>
using is_event_info_desc_t = typename is_info_desc<T, event>::return_type;
+template <typename T>
+using is_event_profiling_info_desc_t =
+ typename is_info_desc<T, event_profiling_tag>::return_type;
} // namespace detail
/// SYCL 2020 4.6.6. Event class.
class _LIBSYCL_EXPORT event {
public:
+ /// Constructs an event that is immediately ready.
+ /// The event has no dependencies and no associated commands. Waiting on this
+ /// event will return immediately.
+ event();
+
event(const event &rhs) = default;
event(event &&rhs) = default;
event &operator=(const event &rhs) = default;
@@ -54,6 +63,10 @@ class _LIBSYCL_EXPORT event {
/// \return the backend associated with this event.
backend get_backend() const noexcept;
+ /// \return the list of events that this event waits for in the dependence
+ /// graph.
+ std::vector<event> get_wait_list();
+
/// Blocks until all commands associated with this event and any dependent
/// events have completed.
void wait();
@@ -61,6 +74,16 @@ class _LIBSYCL_EXPORT event {
/// Behaves as if calling event::wait on each event in eventList.
static void wait(const std::vector<event> &eventList);
+ /// Blocks until all commands associated with this event and any dependent
+ /// events have completed. Passes at least all unconsumed asynchronous errors
+ /// held by queues (or their associated contexts) which were used to enqueue
+ /// commands associated with this event and any dependent events, to the
+ /// appropriate async_handler.
+ void wait_and_throw();
+
+ /// Behaves as if calling event::wait_and_throw on each event in eventList.
+ static void wait_and_throw(const std::vector<event> &eventList);
+
/// Queries this SYCL event for information.
///
/// \return depends on the information being requested.
@@ -73,6 +96,13 @@ class _LIBSYCL_EXPORT event {
template <typename Param>
typename Param::return_type get_backend_info() const;
+ /// Queries this SYCL event for profiling data.
+ ///
+ /// \return depends on the information being queried.
+ template <typename Param>
+ typename detail::is_event_profiling_info_desc_t<Param>
+ get_profiling_info() const;
+
private:
event(std::shared_ptr<detail::EventImpl> Impl) : impl(Impl) {}
std::shared_ptr<detail::EventImpl> impl;
diff --git a/libsycl/include/sycl/__impl/exception.hpp b/libsycl/include/sycl/__impl/exception.hpp
index add00ebccaa49..bcc535cb0cf60 100644
--- a/libsycl/include/sycl/__impl/exception.hpp
+++ b/libsycl/include/sycl/__impl/exception.hpp
@@ -26,6 +26,11 @@
_LIBSYCL_BEGIN_NAMESPACE_SYCL
+class exception_list;
+namespace detail {
+void addAsyncException(exception_list &, const std::exception_ptr &);
+}
+
// int is used as the underlying type for consistency with std::error_code.
enum class errc : int {
success = 0,
@@ -142,8 +147,16 @@ class _LIBSYCL_EXPORT exception_list {
private:
std::vector<std::exception_ptr> MList;
+
+ friend void detail::addAsyncException(exception_list &, const_reference);
};
+namespace detail {
+// Default implementation of async_handler used by queue and context when no
+// user-defined async_handler is specified.
+_LIBSYCL_EXPORT void defaultAsyncHandler(exception_list Exceptions);
+} // namespace detail
+
_LIBSYCL_END_NAMESPACE_SYCL
namespace std {
diff --git a/libsycl/include/sycl/__impl/info/event.hpp b/libsycl/include/sycl/__impl/info/event.hpp
new file mode 100644
index 0000000000000..d57bf5ec7d099
--- /dev/null
+++ b/libsycl/include/sycl/__impl/info/event.hpp
@@ -0,0 +1,53 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file contains SYCL 2020 event and event profiling info descriptors.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBSYCL___IMPL_INFO_EVENT_HPP
+#define _LIBSYCL___IMPL_INFO_EVENT_HPP
+
+#include <sycl/__impl/detail/config.hpp>
+#include <sycl/__impl/info/desc_base.hpp>
+
+#include <cstdint>
+
+_LIBSYCL_BEGIN_NAMESPACE_SYCL
+
+class event;
+
+namespace detail {
+/// Sentinel type used as the DescOf tag for event profiling info descriptors,
+/// so they are distinct from regular event info descriptors.
+struct event_profiling_tag {};
+} // namespace detail
+
+namespace info {
+namespace event_profiling {
+struct command_submit
+ : detail::info_desc_tag<command_submit, detail::event_profiling_tag> {
+ using return_type = std::uint64_t;
+};
+
+struct command_start
+ : detail::info_desc_tag<command_start, detail::event_profiling_tag> {
+ using return_type = std::uint64_t;
+};
+
+struct command_end
+ : detail::info_desc_tag<command_end, detail::event_profiling_tag> {
+ using return_type = std::uint64_t;
+};
+} // namespace event_profiling
+} // namespace info
+
+_LIBSYCL_END_NAMESPACE_SYCL
+
+#endif // _LIBSYCL___IMPL_INFO_EVENT_HPP
diff --git a/libsycl/include/sycl/__impl/queue.hpp b/libsycl/include/sycl/__impl/queue.hpp
index d05592e5fa3fd..5b92aff49bb9e 100644
--- a/libsycl/include/sycl/__impl/queue.hpp
+++ b/libsycl/include/sycl/__impl/queue.hpp
@@ -21,11 +21,11 @@
#include <sycl/__impl/property_list.hpp>
#include <sycl/__impl/detail/config.hpp>
-#include <sycl/__impl/detail/default_async_handler.hpp>
#include <sycl/__impl/detail/get_device_kernel_info.hpp>
#include <sycl/__impl/detail/kernel_arg_helpers.hpp>
#include <sycl/__impl/detail/obj_utils.hpp>
#include <sycl/__impl/detail/unified_range_view.hpp>
+#include <sycl/__impl/exception.hpp>
_LIBSYCL_BEGIN_NAMESPACE_SYCL
@@ -166,6 +166,17 @@ class _LIBSYCL_EXPORT queue {
/// exceptions.
void wait();
+ /// Blocks the calling thread until all commands previously submitted to this
+ /// queue have completed. Synchronous errors are reported through SYCL
+ /// exceptions. At least all unconsumed asynchronous errors held by this queue
+ /// are passed to the appropriate async_handler.
+ void wait_and_throw();
+
+ /// Checks to see if any unconsumed asynchronous errors have been produced by
+ /// the queue and if so reports them by passing them to the async_handler
+ /// associated with the queue.
+ void throw_asynchronous();
+
/// Defines and invokes a SYCL kernel function as a lambda expression or a
/// named function object type.
///
@@ -173,7 +184,8 @@ class _LIBSYCL_EXPORT queue {
/// \return an event that represents the status of the submitted kernel.
template <typename KernelName = detail::AutoName, typename KernelType>
event single_task(const KernelType &kernelFunc) {
- return single_task<KernelName, KernelType>({}, kernelFunc);
+ return single_task<KernelName, KernelType>(std::vector<event>{},
+ kernelFunc);
}
/// Defines and invokes a SYCL kernel function as a lambda expression or a
@@ -184,7 +196,8 @@ class _LIBSYCL_EXPORT queue {
/// \return an event that represents the status of the submitted kernel.
template <typename KernelName = detail::AutoName, typename KernelType>
event single_task(event depEvent, const KernelType &kernelFunc) {
- return single_task<KernelName, KernelType>({depEvent}, kernelFunc);
+ return single_task<KernelName, KernelType>(std::vector<event>{depEvent},
+ kernelFunc);
}
/// Defines and invokes a SYCL kernel function as a lambda expression or a
@@ -217,8 +230,8 @@ class _LIBSYCL_EXPORT queue {
// TODO: Rest will represent reduction types once it is supported.
template <typename KernelName = detail::AutoName, typename... Rest>
event parallel_for(range<1> numWorkItems, Rest &&...rest) {
- return parallel_for<KernelName>(numWorkItems, {},
- std::forward<Rest>(rest)...);
+ return parallel_for<KernelName, Rest...>(numWorkItems, std::vector<event>{},
+ std::forward<Rest>(rest)...);
}
/// Defines and invokes a SYCL kernel function as a lambda expression or a
@@ -229,8 +242,8 @@ class _LIBSYCL_EXPORT queue {
// TODO: Rest will represent reduction types once it is supported.
template <typename KernelName = detail::AutoName, typename... Rest>
event parallel_for(range<2> numWorkItems, Rest &&...rest) {
- return parallel_for<KernelName>(numWorkItems, {},
- std::forward<Rest>(rest)...);
+ return parallel_for<KernelName, Rest...>(numWorkItems, std::vector<event>{},
+ std::forward<Rest>(rest)...);
}
/// Defines and invokes a SYCL kernel function as a lambda expression or a
@@ -241,8 +254,8 @@ class _LIBSYCL_EXPORT queue {
// TODO: Rest will represent reduction types once it is supported.
template <typename KernelName = detail::AutoName, typename... Rest>
event parallel_for(range<3> numWorkItems, Rest &&...rest) {
- return parallel_for<KernelName>(numWorkItems, {},
- std::forward<Rest>(rest)...);
+ return parallel_for<KernelName, Rest...>(numWorkItems, std::vector<event>{},
+ std::forward<Rest>(rest)...);
}
/// Defines and invokes a SYCL kernel function as a lambda expression or a
@@ -255,8 +268,9 @@ class _LIBSYCL_EXPORT queue {
// TODO: Rest will represent reduction types once it is supported.
template <typename KernelName = detail::AutoName, typename... Rest>
event parallel_for(range<1> numWorkItems, event depEvent, Rest &&...rest) {
- return parallel_for<KernelName>(numWorkItems, {depEvent},
- std::forward<Rest>(rest)...);
+ return parallel_for<KernelName, Rest...>(numWorkItems,
+ std::vector<event>{depEvent},
+ std::forward<Rest>(rest)...);
}
/// Defines and invokes a SYCL kernel function as a lambda expression or a
@@ -269,8 +283,9 @@ class _LIBSYCL_EXPORT queue {
// TODO: Rest will represent reduction types once it is supported.
template <typename KernelName = detail::AutoName, typename... Rest>
event parallel_for(range<2> numWorkItems, event depEvent, Rest &&...rest) {
- return parallel_for<KernelName>(numWorkItems, {depEvent},
- std::forward<Rest>(rest)...);
+ return parallel_for<KernelName, Rest...>(numWorkItems,
+ std::vector<event>{depEvent},
+ std::forward<Rest>(rest)...);
}
/// Defines and invokes a SYCL kernel function as a lambda expression or a
@@ -283,8 +298,9 @@ class _LIBSYCL_EXPORT queue {
// TODO: Rest will represent reduction types once it is supported.
template <typename KernelName = detail::AutoName, typename... Rest>
event parallel_for(range<3> numWorkItems, event depEvent, Rest &&...rest) {
- return parallel_for<KernelName>(numWorkItems, {depEvent},
- std::forward<Rest>(rest)...);
+ return parallel_for<KernelName, Rest...>(numWorkItems,
+ std::vector<event>{depEvent},
+ std::forward<Rest>(rest)...);
}
/// Defines and invokes a SYCL kernel function as a lambda expression or a
diff --git a/libsycl/src/detail/event_impl.cpp b/libsycl/src/detail/event_impl.cpp
index f4cee5675359b..11d920a7536ab 100644
--- a/libsycl/src/detail/event_impl.cpp
+++ b/libsycl/src/detail/event_impl.cpp
@@ -6,13 +6,20 @@
//
//===----------------------------------------------------------------------===//
+#include <detail/device_impl.hpp>
#include <detail/event_impl.hpp>
+#include <detail/global_objects.hpp>
#include <detail/platform_impl.hpp>
+#include <detail/queue_impl.hpp>
_LIBSYCL_BEGIN_NAMESPACE_SYCL
namespace detail {
+EventImpl::EventImpl(PrivateTag)
+ : MPlatform(detail::getSyclObjImpl(device(default_selector_v))
+ ->getPlatformImpl()) {}
+
EventImpl::~EventImpl() {
if (MOffloadEvent)
std::ignore = olDestroyEvent(MOffloadEvent);
@@ -32,5 +39,10 @@ void EventImpl::wait() {
callAndThrow(olSyncEvent, MOffloadEvent);
}
+void EventImpl::waitAndThrow() {
+ wait();
+ flushAsyncExceptions();
+}
+
} // namespace detail
_LIBSYCL_END_NAMESPACE_SYCL
diff --git a/libsycl/src/detail/event_impl.hpp b/libsycl/src/detail/event_impl.hpp
index 235bdb83f0686..3a2ca7316faf1 100644
--- a/libsycl/src/detail/event_impl.hpp
+++ b/libsycl/src/detail/event_impl.hpp
@@ -21,6 +21,7 @@
#include <OffloadAPI.h>
#include <memory>
+#include <vector>
_LIBSYCL_BEGIN_NAMESPACE_SYCL
namespace detail {
@@ -42,9 +43,25 @@ class EventImpl {
EventImpl(ol_event_handle_t Event, PlatformImpl &Platform, PrivateTag)
: MOffloadEvent(Event), MPlatform(Platform) {}
+ /// Constructs a default, immediately ready event.
+ /// The event is constructed as though it were created from a
+ /// default-constructed queue. Therefore, its backend is the same as the
+ /// backend of the device selected by default_selector_v.
+ /// \throw sycl::exception with errc::runtime if no default device is
+ /// available.
+ EventImpl(PrivateTag);
+
static std::shared_ptr<EventImpl>
- createEventWithHandle(ol_event_handle_t Event, PlatformImpl &Platform) {
- return std::make_shared<EventImpl>(Event, Platform, PrivateTag{});
+ createEventWithHandle(ol_event_handle_t Event, PlatformImpl &Platform,
+ std::vector<std::shared_ptr<EventImpl>> &&WaitList) {
+ auto E = std::make_shared<EventImpl>(Event, Platform, PrivateTag{});
+ E->MWaitList =
+ std::forward<std::vector<std::shared_ptr<EventImpl>>>(WaitList);
+ return E;
+ }
+
+ static std::shared_ptr<EventImpl> createDefaultEvent() {
+ return std::make_shared<EventImpl>(PrivateTag{});
}
/// Releases the handle to the corresponding liboffload event.
@@ -62,9 +79,24 @@ class EventImpl {
/// \return the platform implementation object this event belongs to.
const PlatformImpl &getPlatformImpl() const { return MPlatform; }
+ /// Blocks until all commands associated with this event and any dependent
+ /// events have completed. Passes at least all unconsumed asynchronous errors
+ /// held by queues (or their associated contexts) which were used to enqueue
+ /// commands associated with this event and any dependent events, to the
+ /// appropriate async_handler.
+ void waitAndThrow();
+
+ /// \return the list of event implementation objects that this event waits for
+ /// in the dependence graph.
+ const std::vector<std::shared_ptr<EventImpl>> &getWaitList() const {
+ return MWaitList;
+ }
+
private:
ol_event_handle_t MOffloadEvent{};
PlatformImpl &MPlatform;
+
+ std::vector<std::shared_ptr<EventImpl>> MWaitList;
};
} // namespace detail
diff --git a/libsycl/src/detail/global_objects.cpp b/libsycl/src/detail/global_objects.cpp
index 095912ffcd171..68199567c2706 100644
--- a/libsycl/src/detail/global_objects.cpp
+++ b/libsycl/src/detail/global_objects.cpp
@@ -9,6 +9,7 @@
#include <detail/global_objects.hpp>
#include <detail/platform_impl.hpp>
#include <detail/program_manager.hpp>
+#include <detail/queue_impl.hpp>
#ifdef _WIN32
# include <windows.h>
@@ -54,5 +55,42 @@ std::vector<PlatformImplUPtr> &getPlatformCache() {
return PlatformCache;
}
+InstanceWithLock<AsyncExceptionsContainer> &getAsyncExceptionList() {
+ static InstanceWithLock<AsyncExceptionsContainer> AsyncExceptionList;
+ return AsyncExceptionList;
+}
+
+void reportAsyncException(const std::shared_ptr<QueueImpl> &QueuePtr,
+ const std::exception_ptr &ExceptionPtr) {
+ auto &[AsyncExceptions, AsyncExceptionsMutex] = getAsyncExceptionList();
+ std::lock_guard<SpinLock> Lock(AsyncExceptionsMutex);
+ addAsyncException(AsyncExceptions[QueuePtr], ExceptionPtr);
+}
+
+void flushAsyncExceptions() {
+ auto &[AsyncExceptions, AsyncExceptionsMutex] = getAsyncExceptionList();
+ AsyncExceptionsContainer AsyncExceptionsSwap;
+ {
+ std::lock_guard<SpinLock> Lock(AsyncExceptionsMutex);
+ std::swap(AsyncExceptions, AsyncExceptionsSwap);
+ }
+
+ for (auto &[EntryKey, ExceptionList] : AsyncExceptionsSwap) {
+ exception_list Exceptions = std::move(ExceptionList);
+
+ if (Exceptions.size() == 0)
+ continue;
+
+ if (std::shared_ptr<QueueImpl> Queue = EntryKey.lock();
+ Queue && Queue->getAsyncHandler()) {
+ Queue->getAsyncHandler()(std::move(Exceptions));
+ continue;
+ }
+
+ // If the queue is dead, use the default handler.
+ defaultAsyncHandler(std::move(Exceptions));
+ }
+}
+
} // namespace detail
_LIBSYCL_END_NAMESPACE_SYCL
diff --git a/libsycl/src/detail/global_objects.hpp b/libsycl/src/detail/global_objects.hpp
index d10b352941a8f..f9b12a9ffe2a5 100644
--- a/libsycl/src/detail/global_objects.hpp
+++ b/libsycl/src/detail/global_objects.hpp
@@ -15,16 +15,23 @@
#define _LIBSYCL_GLOBAL_OBJECTS
#include <detail/offload/offload_topology.hpp>
+#include <detail/spinlock.hpp>
#include <sycl/__impl/detail/config.hpp>
+#include <sycl/__impl/exception.hpp>
+#include <map>
#include <memory>
#include <mutex>
+#include <utility>
#include <vector>
_LIBSYCL_BEGIN_NAMESPACE_SYCL
namespace detail {
class PlatformImpl;
+class QueueImpl;
+
+template <typename T> using InstanceWithLock = std::pair<T, SpinLock>;
/// Returns offload topologies (one per backend) discovered from liboffload.
///
@@ -46,6 +53,31 @@ std::vector<std::unique_ptr<PlatformImpl>> &getPlatformCache();
// the SYCL shared library is first being unloaded.
void registerStaticVarShutdownHandler();
+// TODO: extend with context
+using AsyncExceptionKey = std::weak_ptr<QueueImpl>;
+
+struct AsyncExceptionKeyOwnerLess {
+ bool operator()(const AsyncExceptionKey &LHS,
+ const AsyncExceptionKey &RHS) const noexcept {
+ return std::owner_less<std::weak_ptr<QueueImpl>>{}(LHS, RHS);
+ }
+};
+
+using AsyncExceptionsContainer =
+ std::map<AsyncExceptionKey, exception_list, AsyncExceptionKeyOwnerLess>;
+
+/// \return pair of container with unreported exceptions and an unlocked
+/// SpinLock.
+InstanceWithLock<AsyncExceptionsContainer> &getAsyncExceptionList();
+
+/// Adds an exception to the list of unreported asynchronous exceptions.
+void reportAsyncException(const std::shared_ptr<QueueImpl> &QueuePtr,
+ const std::exception_ptr &ExceptionPtr);
+
+/// Reports all unreported asynchronous exceptions to available async_handler
+/// and clears the list.
+void flushAsyncExceptions();
+
} // namespace detail
_LIBSYCL_END_NAMESPACE_SYCL
diff --git a/libsycl/src/detail/queue_impl.cpp b/libsycl/src/detail/queue_impl.cpp
index beba3082f06e2..19adc1b83ebf3 100644
--- a/libsycl/src/detail/queue_impl.cpp
+++ b/libsycl/src/detail/queue_impl.cpp
@@ -10,6 +10,7 @@
#include <detail/device_impl.hpp>
#include <detail/event_impl.hpp>
+#include <detail/global_objects.hpp>
#include <detail/program_manager.hpp>
#include <algorithm>
@@ -66,6 +67,13 @@ backend QueueImpl::getBackend() const noexcept { return MDevice.getBackend(); }
void QueueImpl::wait() { callAndThrow(olSyncQueue, MOffloadQueue); }
+void QueueImpl::waitAndThrow() {
+ wait();
+ throwAsynchronous();
+}
+
+void QueueImpl::throwAsynchronous() { flushAsyncExceptions(); }
+
static bool checkEventsPlatformMatch(std::vector<EventImplPtr> &Events,
const PlatformImpl &QueuePlatform) {
// liboffload limitation to olWaitEvents. We can't do any extra handling for
@@ -86,18 +94,14 @@ void QueueImpl::setKernelParameters(std::vector<EventImplPtr> &&Events,
"libsycl doesn't support cross-context/platform event dependencies "
"now.");
- // TODO: this conversion and storing of only offload events is possible only
- // while we don't have host tasks (or features based on host tasks, like
- // streams). With them - it is very likely we should copy EventImplPtr
- // (shared_ptr) and keep it here. Although it may differ if host tasks will be
- // implemented on offload level (no data now).
- assert(MCurrentSubmitInfo.DepEvents.empty() &&
- "Kernel submission must clean up dependencies.");
- MCurrentSubmitInfo.DepEvents.reserve(Events.size());
- for (auto &Event : Events) {
- assert(Event && "Event impl object can't be nullptr");
- MCurrentSubmitInfo.DepEvents.push_back(Event->getHandle());
- }
+ // Clean up previous kernel submit data to prepare structures for submission.
+ // It is done in the beginning of new submission to ensure that if previous
+ // submit throws we still can submit new kernel properly.
+ MCurrentSubmitInfo.DepEvents.clear();
+ MCurrentSubmitInfo.Range = {};
+
+ MCurrentSubmitInfo.DepEvents =
+ std::forward<std::vector<EventImplPtr>>(Events);
setKernelLaunchArgs(Range, MCurrentSubmitInfo.Range);
}
@@ -115,9 +119,20 @@ void QueueImpl::submitKernelImpl(DeviceKernelInfo &KernelInfo, void *ArgData,
// must be disabled for in-order queues. Once host tasks are added - cross
// context dependencies should be enabled and checked as well.
if (!MCurrentSubmitInfo.DepEvents.empty()) {
- callAndThrow(olWaitEvents, MOffloadQueue,
- MCurrentSubmitInfo.DepEvents.data(),
- MCurrentSubmitInfo.DepEvents.size());
+ std::vector<ol_event_handle_t> DepHandles;
+ DepHandles.reserve(MCurrentSubmitInfo.DepEvents.size());
+ for (const auto &Event : MCurrentSubmitInfo.DepEvents) {
+ // NULL handle stands for only 1 case now - default constructed event that
+ // is immediately ready. Ignore it.
+ // TODO: host_task implementation will require to handle NULL handles
+ // differently.
+ if (Event->getHandle())
+ DepHandles.push_back(Event->getHandle());
+ }
+ if (!DepHandles.empty()) {
+ callAndThrow(olWaitEvents, MOffloadQueue, DepHandles.data(),
+ DepHandles.size());
+ }
}
assert(ArgData && "At least one argument must exist");
@@ -128,10 +143,6 @@ void QueueImpl::submitKernelImpl(DeviceKernelInfo &KernelInfo, void *ArgData,
auto Result =
olLaunchKernel(MOffloadQueue, MDevice.getOLHandle(), Kernel,
&MCurrentSubmitInfo.Range, NULL, 1, ArgPtrs, ArgSizes);
- // Clean up current kernel submit data to prepare structures for next
- // submission.
- MCurrentSubmitInfo.DepEvents.clear();
- MCurrentSubmitInfo.Range = {};
if (isFailed(Result))
throw sycl::exception(sycl::make_error_code(sycl::errc::runtime),
std::string("Kernel submission (") +
@@ -143,7 +154,8 @@ void QueueImpl::submitKernelImpl(DeviceKernelInfo &KernelInfo, void *ArgData,
callAndThrow(olCreateEvent, MOffloadQueue, Flags, &NewEvent);
MCurrentSubmitInfo.LastEvent =
- EventImpl::createEventWithHandle(NewEvent, MDevice.getPlatformImpl());
+ EventImpl::createEventWithHandle(NewEvent, MDevice.getPlatformImpl(),
+ std::move(MCurrentSubmitInfo.DepEvents));
}
} // namespace detail
diff --git a/libsycl/src/detail/queue_impl.hpp b/libsycl/src/detail/queue_impl.hpp
index ac5b303e95dc6..d98788f7fc8e0 100644
--- a/libsycl/src/detail/queue_impl.hpp
+++ b/libsycl/src/detail/queue_impl.hpp
@@ -21,6 +21,7 @@
#include <OffloadAPI.h>
#include <memory>
+#include <vector>
_LIBSYCL_BEGIN_NAMESPACE_SYCL
namespace detail {
@@ -71,6 +72,13 @@ class QueueImpl : public std::enable_shared_from_this<QueueImpl> {
/// Waits for completion of all commands submitted to this queue.
void wait();
+ /// Waits for completion of all commands submitted to this queue and flushes
+ /// unconsumed async errors to the appropriate async handlers.
+ void waitAndThrow();
+
+ /// Flushes unconsumed async errors to the appropriate async handlers.
+ void throwAsynchronous();
+
/// Enqueues a kernel to liboffload.
/// Kernel parameters like dependencies and range must be passed in advance by
/// calling setKernelParameters.
@@ -96,6 +104,9 @@ class QueueImpl : public std::enable_shared_from_this<QueueImpl> {
void setKernelParameters(std::vector<EventImplPtr> &&Events,
const detail::UnifiedRangeView &Range);
+ /// \return the async_handler associated with this queue.
+ const async_handler &getAsyncHandler() const { return MAsyncHandler; }
+
private:
// Queue features.
ol_queue_handle_t MOffloadQueue = {};
@@ -109,11 +120,7 @@ class QueueImpl : public std::enable_shared_from_this<QueueImpl> {
struct KernelSubmitInfo {
EventImplPtr LastEvent;
ol_kernel_launch_size_args_t Range;
- // TODO: consider storing EventImplPtr here, it will work with plain handle
- // only because submission is done within queue::submit call. Otherwise we
- // need to ensure that event handle is still alive by keeping our own copy
- // of EventImpl.
- std::vector<ol_event_handle_t> DepEvents;
+ std::vector<EventImplPtr> DepEvents;
};
inline static thread_local KernelSubmitInfo MCurrentSubmitInfo = {};
};
diff --git a/libsycl/src/detail/spinlock.hpp b/libsycl/src/detail/spinlock.hpp
new file mode 100644
index 0000000000000..a8898a4e68463
--- /dev/null
+++ b/libsycl/src/detail/spinlock.hpp
@@ -0,0 +1,47 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file contains the declaration of a lightweight spin lock.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBSYCL_SPINLOCK
+#define _LIBSYCL_SPINLOCK
+
+#include <sycl/__impl/detail/config.hpp>
+
+#include <atomic>
+#include <thread>
+
+_LIBSYCL_BEGIN_NAMESPACE_SYCL
+
+namespace detail {
+
+/// SpinLock is a synchronization primitive based on std::atomic_flag.
+/// It is trivially constructible and suitable for global object wrappers.
+class SpinLock {
+public:
+ bool try_lock() { return !MLock.test_and_set(std::memory_order_acquire); }
+
+ void lock() {
+ while (MLock.test_and_set(std::memory_order_acquire))
+ std::this_thread::yield();
+ }
+
+ void unlock() { MLock.clear(std::memory_order_release); }
+
+private:
+ std::atomic_flag MLock = ATOMIC_FLAG_INIT;
+};
+
+} // namespace detail
+
+_LIBSYCL_END_NAMESPACE_SYCL
+
+#endif // _LIBSYCL_SPINLOCK
diff --git a/libsycl/src/event.cpp b/libsycl/src/event.cpp
index c0276b519f173..1a88cef5f9b0d 100644
--- a/libsycl/src/event.cpp
+++ b/libsycl/src/event.cpp
@@ -7,11 +7,14 @@
//===----------------------------------------------------------------------===//
#include <sycl/__impl/event.hpp>
+#include <sycl/__impl/exception.hpp>
#include <detail/event_impl.hpp>
_LIBSYCL_BEGIN_NAMESPACE_SYCL
+event::event() : impl(detail::EventImpl::createDefaultEvent()) {}
+
backend event::get_backend() const noexcept { return impl->getBackend(); }
void event::wait(const std::vector<event> &EventList) {
@@ -22,4 +25,30 @@ void event::wait(const std::vector<event> &EventList) {
void event::wait() { impl->wait(); }
+void event::wait_and_throw() { impl->waitAndThrow(); }
+
+void event::wait_and_throw(const std::vector<event> &EventList) {
+ for (auto E : EventList) {
+ E.wait_and_throw();
+ }
+}
+
+std::vector<event> event::get_wait_list() {
+ const auto &WaitList = impl->getWaitList();
+ std::vector<event> Result;
+ Result.reserve(WaitList.size());
+
+ for (const auto &EventImpl : WaitList)
+ Result.push_back(detail::createSyclObjFromImpl<event>(EventImpl));
+
+ return Result;
+}
+
+template <typename Param>
+typename detail::is_event_profiling_info_desc_t<Param>
+event::get_profiling_info() const {
+ throw sycl::exception(make_error_code(errc::feature_not_supported),
+ "Profiling features are not supported.");
+}
+
_LIBSYCL_END_NAMESPACE_SYCL
diff --git a/libsycl/src/exception_list.cpp b/libsycl/src/exception_list.cpp
index 1c0a78bd33681..61409fd6db77f 100644
--- a/libsycl/src/exception_list.cpp
+++ b/libsycl/src/exception_list.cpp
@@ -6,10 +6,11 @@
//
//===----------------------------------------------------------------------===//
-// SYCL 2020 4.13.2. Exception class interface.
#include <sycl/__impl/detail/config.hpp>
#include <sycl/__impl/exception.hpp>
+#include <iostream>
+
_LIBSYCL_BEGIN_NAMESPACE_SYCL
exception_list::size_type exception_list::size() const { return MList.size(); }
@@ -18,4 +19,24 @@ exception_list::iterator exception_list::begin() const { return MList.begin(); }
exception_list::iterator exception_list::end() const { return MList.cend(); }
+void detail::addAsyncException(exception_list &List,
+ const std::exception_ptr &Exception) {
+ List.MList.emplace_back(Exception);
+}
+
+void detail::defaultAsyncHandler(exception_list Exceptions) {
+ std::cerr << "Default async_handler caught exceptions:";
+ for (auto &EIt : Exceptions) {
+ try {
+ if (EIt) {
+ std::rethrow_exception(EIt);
+ }
+ } catch (const std::exception &E) {
+ std::cerr << "\n\t" << E.what();
+ }
+ }
+ std::cerr << std::endl;
+ std::terminate();
+}
+
_LIBSYCL_END_NAMESPACE_SYCL
diff --git a/libsycl/src/queue.cpp b/libsycl/src/queue.cpp
index b57324219e46b..66ae2b4698595 100644
--- a/libsycl/src/queue.cpp
+++ b/libsycl/src/queue.cpp
@@ -35,6 +35,10 @@ bool queue::is_in_order() const { return impl->isInOrder(); }
void queue::wait() { impl->wait(); }
+void queue::wait_and_throw() { impl->waitAndThrow(); }
+
+void queue::throw_asynchronous() { impl->throwAsynchronous(); }
+
event queue::getLastEvent() {
return detail::createSyclObjFromImpl<event>(impl->getLastEvent());
}
diff --git a/libsycl/unittests/CMakeLists.txt b/libsycl/unittests/CMakeLists.txt
index 56bb28161737c..21cdbf32570e3 100644
--- a/libsycl/unittests/CMakeLists.txt
+++ b/libsycl/unittests/CMakeLists.txt
@@ -6,6 +6,7 @@ add_custom_target(check-sycl-unittests)
add_subdirectory(mock)
add_subdirectory(device_selector)
+add_subdirectory(event)
add_subdirectory(platform)
add_subdirectory(program_manager)
add_subdirectory(queue)
diff --git a/libsycl/unittests/common/device_images.hpp b/libsycl/unittests/common/device_images.hpp
index 3c53c097a9e78..7402214d8f519 100644
--- a/libsycl/unittests/common/device_images.hpp
+++ b/libsycl/unittests/common/device_images.hpp
@@ -20,7 +20,8 @@
#include <llvm/Frontend/Offloading/Utility.h>
#include <llvm/Object/OffloadBinary.h>
-namespace sycl::unittest {
+_LIBSYCL_BEGIN_NAMESPACE_SYCL
+namespace unittests {
inline llvm::object::OffloadBinary::OffloadingImage createSYCLImage(
llvm::StringRef SymbolsBlob,
@@ -49,6 +50,7 @@ inline llvm::SmallString<0> createSYCLDeviceBinary(
return llvm::object::OffloadBinary::write(Image);
}
-} // namespace sycl::unittest
+} // namespace unittests
+_LIBSYCL_END_NAMESPACE_SYCL
#endif // _LIBSYCL_UNITTESTS_COMMON_DEVICE_IMAGES_HPP
diff --git a/libsycl/unittests/common/scoped_binary_registration.hpp b/libsycl/unittests/common/scoped_binary_registration.hpp
new file mode 100644
index 0000000000000..bd97d6a582503
--- /dev/null
+++ b/libsycl/unittests/common/scoped_binary_registration.hpp
@@ -0,0 +1,64 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// RAII helpers for registering and unregistering test device binaries.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBSYCL_UNITTESTS_COMMON_SCOPED_BINARY_REGISTRATION_HPP
+#define _LIBSYCL_UNITTESTS_COMMON_SCOPED_BINARY_REGISTRATION_HPP
+
+#include <common/device_images.hpp>
+
+#include <detail/program_manager.hpp>
+
+#include <array>
+#include <string>
+#include <utility>
+
+_LIBSYCL_BEGIN_NAMESPACE_SYCL
+namespace unittests {
+
+class ScopedBinaryRegistration {
+public:
+ explicit ScopedBinaryRegistration(llvm::ArrayRef<llvm::StringRef> KernelNames)
+ : MBinary(createSYCLDeviceBinary(KernelNames)) {
+ sycl::detail::ProgramAndKernelManager::getInstance().registerFatBin(
+ MBinary.data(), MBinary.size());
+ }
+
+ ~ScopedBinaryRegistration() {
+ sycl::detail::ProgramAndKernelManager::getInstance().unregisterFatBin(
+ MBinary.data(), MBinary.size());
+ }
+
+private:
+ llvm::SmallString<0> MBinary;
+};
+
+class ScopedKernelRegistration {
+public:
+ explicit ScopedKernelRegistration(std::string KernelName)
+ : MKernelName(std::move(KernelName)),
+ MRegistration(createKernelNames(MKernelName)) {}
+
+private:
+ static std::array<llvm::StringRef, 1>
+ createKernelNames(const std::string &KernelName) {
+ return {KernelName};
+ }
+
+ std::string MKernelName;
+ ScopedBinaryRegistration MRegistration;
+};
+
+} // namespace unittests
+_LIBSYCL_END_NAMESPACE_SYCL
+
+#endif // _LIBSYCL_UNITTESTS_COMMON_SCOPED_BINARY_REGISTRATION_HPP
diff --git a/libsycl/unittests/common/unittests_helper.hpp b/libsycl/unittests/common/unittests_helper.hpp
index a1d5c5d0ccd7f..a04995225f0e5 100644
--- a/libsycl/unittests/common/unittests_helper.hpp
+++ b/libsycl/unittests/common/unittests_helper.hpp
@@ -19,7 +19,6 @@
#include <mock/helpers.hpp>
_LIBSYCL_BEGIN_NAMESPACE_SYCL
-
namespace unittests {
// This helper is not included to LiboffloadMock to keep LiboffloadMock isolated
diff --git a/libsycl/unittests/device_selector/get_device_preference.cpp b/libsycl/unittests/device_selector/get_device_preference.cpp
index b4b6ccfa7bcd6..79920b9611a10 100644
--- a/libsycl/unittests/device_selector/get_device_preference.cpp
+++ b/libsycl/unittests/device_selector/get_device_preference.cpp
@@ -6,12 +6,10 @@
//
//===----------------------------------------------------------------------===//
-#include <common/device_images.hpp>
+#include <common/scoped_binary_registration.hpp>
#include <common/unittests_helper.hpp>
#include <detail/device_impl.hpp>
-#include <detail/program_manager.hpp>
-
#include <sycl/__impl/detail/obj_utils.hpp>
#include <sycl/__impl/device_selector.hpp>
#include <sycl/sycl.hpp>
@@ -24,23 +22,6 @@ using namespace ::testing;
namespace {
-class ScopedBinaryRegistration {
-public:
- explicit ScopedBinaryRegistration(llvm::ArrayRef<llvm::StringRef> KernelNames)
- : MBinary(sycl::unittest::createSYCLDeviceBinary(KernelNames)) {
- sycl::detail::ProgramAndKernelManager::getInstance().registerFatBin(
- MBinary.data(), MBinary.size());
- }
-
- ~ScopedBinaryRegistration() {
- sycl::detail::ProgramAndKernelManager::getInstance().unregisterFatBin(
- MBinary.data(), MBinary.size());
- }
-
-private:
- llvm::SmallString<0> MBinary;
-};
-
class DeviceSelectorScoreTest : public ::testing::Test {
protected:
void SetUp() override {
@@ -138,7 +119,7 @@ TEST_F(DeviceSelectorScoreTest, TwoGpusOneCompatibleImage) {
});
std::array<llvm::StringRef, 1> KernelNames = {"kernel"};
- ScopedBinaryRegistration Registration{KernelNames};
+ sycl::unittests::ScopedBinaryRegistration Registration{KernelNames};
auto Devices = sycl::device::get_devices();
ASSERT_EQ(Devices.size(), 2u);
diff --git a/libsycl/unittests/event/CMakeLists.txt b/libsycl/unittests/event/CMakeLists.txt
new file mode 100644
index 0000000000000..fab70b9c6537b
--- /dev/null
+++ b/libsycl/unittests/event/CMakeLists.txt
@@ -0,0 +1,4 @@
+add_sycl_unittest(EventTests
+ async_handler.cpp
+ event.cpp
+)
diff --git a/libsycl/unittests/event/async_handler.cpp b/libsycl/unittests/event/async_handler.cpp
new file mode 100644
index 0000000000000..1989819020a53
--- /dev/null
+++ b/libsycl/unittests/event/async_handler.cpp
@@ -0,0 +1,152 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <mock/helpers.hpp>
+
+#include <detail/event_impl.hpp>
+#include <detail/global_objects.hpp>
+
+#include <sycl/__impl/detail/obj_utils.hpp>
+#include <sycl/__impl/device.hpp>
+#include <sycl/__impl/event.hpp>
+#include <sycl/__impl/platform.hpp>
+#include <sycl/__impl/queue.hpp>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <exception>
+#include <memory>
+#include <string>
+#include <system_error>
+#include <vector>
+
+using namespace sycl;
+using namespace ::testing;
+
+namespace {
+
+using EventImplPtr = std::shared_ptr<detail::EventImpl>;
+
+EventImplPtr createEventImplWithHandle(detail::PlatformImpl &PlatformImpl) {
+ ol_event_handle_t Handle = mock::createDummyHandle<ol_event_handle_t>();
+ return detail::EventImpl::createEventWithHandle(Handle, PlatformImpl, {});
+}
+
+template <typename FlushAction>
+void runAsyncExceptionFlushTest(mock::MockLiboffload &Mock,
+ const char *ErrorMsg, FlushAction Flush) {
+ size_t NumExceptionsHandled = 0;
+ std::vector<std::error_code> ReportedCodes;
+ std::vector<std::string> ReportedMessages;
+ auto expectSingleRuntimeErrorContaining = [&](const char *ExpectedMessage) {
+ EXPECT_EQ(NumExceptionsHandled, 1u);
+ ASSERT_THAT(ReportedCodes, SizeIs(1));
+ ASSERT_THAT(ReportedMessages, SizeIs(1));
+ EXPECT_EQ(ReportedCodes.back(), make_error_code(errc::runtime));
+ EXPECT_THAT(ReportedMessages.back(), HasSubstr(ExpectedMessage));
+ };
+
+ async_handler Handler = [&](exception_list Exceptions) {
+ for (const auto &ExceptionPtr : Exceptions) {
+ ++NumExceptionsHandled;
+ try {
+ std::rethrow_exception(ExceptionPtr);
+ } catch (const sycl::exception &E) {
+ ReportedCodes.push_back(E.code());
+ ReportedMessages.emplace_back(E.what());
+ } catch (...) {
+ ADD_FAILURE() << "Unexpected exception type in async_handler";
+ }
+ }
+ };
+
+ auto Device = device(default_selector_v);
+ queue Q(Device, Handler);
+ auto QImpl = detail::getSyclObjImpl(Q);
+
+ platform P = Device.get_platform();
+ EventImplPtr EImpl = createEventImplWithHandle(*detail::getSyclObjImpl(P));
+ event E = detail::createSyclObjFromImpl<event>(EImpl);
+
+ detail::reportAsyncException(
+ QImpl, std::make_exception_ptr(
+ exception(make_error_code(errc::runtime), ErrorMsg)));
+
+ Flush(Mock, Q, QImpl, E, EImpl);
+ expectSingleRuntimeErrorContaining(ErrorMsg);
+}
+
+} // namespace
+
+TEST(EventAsyncHandler, QueueWaitAndThrow) {
+ mock::MockWrapper Mock;
+ constexpr const char *QueueWaitAndThrowErrorMsg =
+ "queue wait_and_throw error";
+
+ runAsyncExceptionFlushTest(Mock.get(), QueueWaitAndThrowErrorMsg,
+ [](mock::MockLiboffload &Mock, queue &Q,
+ const auto &, const auto &, const auto &) {
+ EXPECT_CALL(Mock, olSyncQueue(_)).Times(1);
+ Q.wait_and_throw();
+ });
+}
+
+TEST(EventAsyncHandler, EventWaitAndThrow) {
+ mock::MockWrapper Mock;
+ constexpr const char *EventWaitAndThrowErrorMsg =
+ "event wait_and_throw error";
+
+ runAsyncExceptionFlushTest(
+ Mock.get(), EventWaitAndThrowErrorMsg,
+ [](mock::MockLiboffload &Mock, queue &, const auto &, event &E,
+ EventImplPtr &EImpl) {
+ EXPECT_CALL(Mock, olSyncQueue(_)).Times(0);
+ EXPECT_CALL(Mock, olSyncEvent(EImpl->getHandle())).Times(1);
+ E.wait_and_throw();
+ });
+}
+
+TEST(EventAsyncHandler, QueueThrowAsynchronous) {
+ mock::MockWrapper Mock;
+ constexpr const char *QueueThrowAsynchronousErrorMsg =
+ "queue throw_asynchronous error";
+
+ runAsyncExceptionFlushTest(Mock.get(), QueueThrowAsynchronousErrorMsg,
+ [](mock::MockLiboffload &Mock, queue &Q,
+ const auto &, const auto &, const auto &) {
+ EXPECT_CALL(Mock, olSyncQueue(_)).Times(0);
+ Q.throw_asynchronous();
+ });
+}
+
+TEST(EventAsyncHandler, DeadQueueFallsBackToDefaultAsyncHandler) {
+// EXPECT_DEATH is not supported on Windows.
+#if GTEST_HAS_DEATH_TEST
+ EXPECT_DEATH(
+ {
+ mock::MockWrapper Mock;
+ async_handler Handler = [](exception_list) {
+ ADD_FAILURE() << "Queue async_handler should not be called";
+ };
+ {
+ queue Q(device(default_selector_v), Handler);
+ auto QImpl = detail::getSyclObjImpl(Q);
+ detail::reportAsyncException(
+ QImpl,
+ std::make_exception_ptr(exception(make_error_code(errc::runtime),
+ "fallback async error")));
+ }
+
+ detail::flushAsyncExceptions();
+ },
+ "Default async_handler caught exceptions:\n\tfallback async error");
+#else
+ GTEST_SKIP() << "Death tests are not supported on this platform";
+#endif
+}
diff --git a/libsycl/unittests/event/event.cpp b/libsycl/unittests/event/event.cpp
new file mode 100644
index 0000000000000..c1081b64894ca
--- /dev/null
+++ b/libsycl/unittests/event/event.cpp
@@ -0,0 +1,139 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <common/scoped_binary_registration.hpp>
+#include <mock/helpers.hpp>
+
+#include <detail/event_impl.hpp>
+#include <detail/queue_impl.hpp>
+
+#include <sycl/__impl/detail/obj_utils.hpp>
+#include <sycl/__impl/device.hpp>
+#include <sycl/__impl/event.hpp>
+#include <sycl/__impl/platform.hpp>
+#include <sycl/__impl/queue.hpp>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <memory>
+#include <vector>
+
+using namespace sycl;
+using namespace ::testing;
+
+using EventImplPtr = std::shared_ptr<detail::EventImpl>;
+
+static EventImplPtr
+createEventImplWithHandle(detail::PlatformImpl &PlatformImpl,
+ std::vector<EventImplPtr> WaitList = {}) {
+ ol_event_handle_t Handle = mock::createDummyHandle<ol_event_handle_t>();
+ return detail::EventImpl::createEventWithHandle(Handle, PlatformImpl,
+ std::move(WaitList));
+}
+
+// MockQueue to expose protected methods for testing
+class sycl::detail::MockQueue : public sycl::queue {
+public:
+ using sycl::queue::getLastEvent;
+ using sycl::queue::setKernelParameters;
+ using sycl::queue::sycl_kernel_launch;
+};
+
+TEST(Event, CopyMoveAssign) {
+ mock::MockWrapper Mock;
+
+ event Event;
+ const size_t Hash = std::hash<event>{}(Event);
+ event MovedConstructed(std::move(Event));
+ EXPECT_EQ(Hash, std::hash<event>{}(MovedConstructed));
+
+ event AssignedSource;
+ const size_t AssignedHash = std::hash<event>{}(AssignedSource);
+ event MoveAssigned;
+ MoveAssigned = std::move(AssignedSource);
+ EXPECT_EQ(AssignedHash, std::hash<event>{}(MoveAssigned));
+
+ event CopiedSource;
+ const size_t CopiedHash = std::hash<event>{}(CopiedSource);
+ event CopyConstructed(CopiedSource);
+ EXPECT_EQ(CopiedHash, std::hash<event>{}(CopiedSource));
+ EXPECT_EQ(CopiedHash, std::hash<event>{}(CopyConstructed));
+ EXPECT_EQ(CopiedSource, CopyConstructed);
+
+ event CopyAssigned;
+ CopyAssigned = CopiedSource;
+ EXPECT_EQ(CopiedHash, std::hash<event>{}(CopyAssigned));
+ EXPECT_EQ(CopiedSource, CopyAssigned);
+}
+
+TEST(Event, WaitAPIsForDefaultConstructedEvent) {
+ mock::MockWrapper Mock;
+
+ event Event;
+ EXPECT_CALL(Mock.get(), olSyncEvent(_)).Times(0);
+
+ EXPECT_NO_THROW(Event.wait());
+ EXPECT_NO_THROW(Event.wait_and_throw());
+ EXPECT_TRUE(Event.get_wait_list().empty());
+
+ std::vector<event> EventList = {Event};
+ EXPECT_NO_THROW(event::wait(EventList));
+ EXPECT_NO_THROW(event::wait_and_throw(EventList));
+}
+
+TEST(Event, GetWaitListWithSetKernelParametersAndLaunch) {
+ static constexpr char TestKernelWithDeps[] = "TestKernelWithDeps";
+ mock::MockWrapper Mock;
+ sycl::unittests::ScopedKernelRegistration Registration{TestKernelWithDeps};
+
+ platform P = device(default_selector_v).get_platform();
+ auto &PlatformImpl = *detail::getSyclObjImpl(P);
+
+ detail::MockQueue Q;
+
+ EventImplPtr Dep1Impl = createEventImplWithHandle(PlatformImpl);
+ EventImplPtr Dep2Impl = createEventImplWithHandle(PlatformImpl);
+ event Dep1 = detail::createSyclObjFromImpl<event>(Dep1Impl);
+ event Dep2 = detail::createSyclObjFromImpl<event>(Dep2Impl);
+
+ struct KernelData {
+ int Value = 42;
+ } Data;
+
+ EXPECT_CALL(Mock.get(), olLaunchKernel(_, _, _, _, _, 1, _, _))
+ .WillOnce([](ol_queue_handle_t Queue, ol_device_handle_t Device,
+ ol_symbol_handle_t Kernel,
+ const ol_kernel_launch_size_args_t *LaunchSizeArgs,
+ const ol_kernel_launch_prop_t *Properties, size_t NumArgs,
+ void **ArgPtrs, const size_t *ArgSizes) -> ol_result_t {
+ EXPECT_NE(Queue, nullptr);
+ EXPECT_NE(Device, nullptr);
+ EXPECT_NE(Kernel, nullptr);
+ return OL_SUCCESS;
+ });
+
+ std::vector<event> DepEvents = {Dep1, Dep2};
+
+ Q.setKernelParameters(DepEvents);
+ Q.sycl_kernel_launch<class TestKernelWithDeps>(TestKernelWithDeps, Data);
+ event KernelEvent = Q.getLastEvent();
+
+ std::vector<event> WaitList = KernelEvent.get_wait_list();
+ ASSERT_THAT(WaitList, SizeIs(2));
+ EXPECT_EQ(WaitList[0], Dep1);
+ EXPECT_EQ(WaitList[1], Dep2);
+
+ auto KernelEventImpl = detail::getSyclObjImpl(KernelEvent);
+ EXPECT_CALL(Mock.get(), olSyncEvent(KernelEventImpl->getHandle())).Times(1);
+ KernelEvent.wait();
+
+ EXPECT_CALL(Mock.get(), olSyncEvent(Dep1Impl->getHandle())).Times(1);
+ EXPECT_CALL(Mock.get(), olSyncEvent(Dep2Impl->getHandle())).Times(1);
+ event::wait(WaitList);
+}
diff --git a/libsycl/unittests/mock/helpers.cpp b/libsycl/unittests/mock/helpers.cpp
index 6bc89e1b5713f..72be7706b03b4 100644
--- a/libsycl/unittests/mock/helpers.cpp
+++ b/libsycl/unittests/mock/helpers.cpp
@@ -277,6 +277,13 @@ void mock::MockLiboffload::initDefault() {
return OL_SUCCESS;
});
+ ON_CALL(*this, olSyncEvent)
+ .WillByDefault([this](ol_event_handle_t Event) -> ol_result_t {
+ if (!Event)
+ return makeEmptyStrError(OL_ERRC_INVALID_NULL_HANDLE);
+ return OL_SUCCESS;
+ });
+
ON_CALL(*this, olCreateEvent)
.WillByDefault([this](ol_queue_handle_t Queue, ol_event_flags_t Flags,
ol_event_handle_t *Event) -> ol_result_t {
diff --git a/libsycl/unittests/mock/helpers.hpp b/libsycl/unittests/mock/helpers.hpp
index 793c687b7276b..9405ae0b9199f 100644
--- a/libsycl/unittests/mock/helpers.hpp
+++ b/libsycl/unittests/mock/helpers.hpp
@@ -107,6 +107,7 @@ class MockLiboffload {
MOCK_METHOD(ol_result_t, olWaitEvents,
(ol_queue_handle_t Queue, ol_event_handle_t *Events,
size_t NumEvents));
+ MOCK_METHOD(ol_result_t, olSyncEvent, (ol_event_handle_t Event));
MOCK_METHOD(ol_result_t, olCreateEvent,
(ol_queue_handle_t Queue, ol_event_flags_t Flags,
ol_event_handle_t *Event));
diff --git a/libsycl/unittests/mock/mock.cpp b/libsycl/unittests/mock/mock.cpp
index a4e66548c7f40..4cb8e6766612d 100644
--- a/libsycl/unittests/mock/mock.cpp
+++ b/libsycl/unittests/mock/mock.cpp
@@ -95,6 +95,10 @@ ol_result_t olLaunchKernel(ol_queue_handle_t Queue, ol_device_handle_t Device,
NumArgs, ArgPtrs, ArgSizes);
}
+ol_result_t olSyncEvent(ol_event_handle_t Event) {
+ return mock::getMockLiboffload().olSyncEvent(Event);
+}
+
ol_result_t olCreateEvent(ol_queue_handle_t Queue, ol_event_flags_t Flags,
ol_event_handle_t *Event) {
return mock::getMockLiboffload().olCreateEvent(Queue, Flags, Event);
diff --git a/libsycl/unittests/program_manager/register_and_unregister.cpp b/libsycl/unittests/program_manager/register_and_unregister.cpp
index f266e98a02e14..648e1ef6cfc7f 100644
--- a/libsycl/unittests/program_manager/register_and_unregister.cpp
+++ b/libsycl/unittests/program_manager/register_and_unregister.cpp
@@ -32,7 +32,7 @@ struct MockProgramAndKernelManager : public detail::ProgramAndKernelManager {
TEST(ProgramAndKernelManager, CheckUnsupportedVersionOfFatbin) {
std::array<llvm::StringRef, 1> KernelNames = {"kernel"};
llvm::SmallString<0> Binary =
- sycl::unittest::createSYCLDeviceBinary(KernelNames);
+ sycl::unittests::createSYCLDeviceBinary(KernelNames);
llvm::MemoryBufferRef MBR(
llvm::StringRef(static_cast<const char *>(Binary.data()), Binary.size()),
@@ -59,8 +59,8 @@ TEST(ProgramAndKernelManager, CheckUnsupportedVersionOfFatbin) {
TEST(ProgramAndKernelManager, CheckUnsupportedVersionOfImage) {
std::array<llvm::StringRef, 1> KernelNames = {"kernel"};
llvm::SmallString<0> IncompatibleImageBinary =
- sycl::unittest::createSYCLDeviceBinary(KernelNames,
- llvm::object::IMG_Bitcode);
+ sycl::unittests::createSYCLDeviceBinary(KernelNames,
+ llvm::object::IMG_Bitcode);
MockProgramAndKernelManager Manager;
@@ -75,8 +75,8 @@ TEST(ProgramAndKernelManager, CheckUnsupportedVersionOfImage) {
Property(&sycl::exception::code, Eq(sycl::errc::runtime)))));
llvm::SmallString<0> CompatibleImageBinary =
- sycl::unittest::createSYCLDeviceBinary(KernelNames,
- llvm::object::IMG_SPIRV);
+ sycl::unittests::createSYCLDeviceBinary(KernelNames,
+ llvm::object::IMG_SPIRV);
EXPECT_NO_THROW(Manager.registerFatBin(CompatibleImageBinary.data(),
CompatibleImageBinary.size()));
EXPECT_NO_THROW(Manager.unregisterFatBin(CompatibleImageBinary.data(),
@@ -95,8 +95,8 @@ TEST(ProgramAndKernelManager, CheckRegisterAndUnregister) {
llvm::offloading::sycl::writeSymbolTable(Image2Kernels, Symbols[1]);
llvm::SmallVector<llvm::object::OffloadBinary::OffloadingImage, 2> Images;
- Images.push_back(sycl::unittest::createSYCLImage(Symbols[0]));
- Images.push_back(sycl::unittest::createSYCLImage(Symbols[1]));
+ Images.push_back(sycl::unittests::createSYCLImage(Symbols[0]));
+ Images.push_back(sycl::unittests::createSYCLImage(Symbols[1]));
llvm::SmallString<0> Binary = llvm::object::OffloadBinary::write(Images);
diff --git a/libsycl/unittests/queue/sycl_kernel_launch.cpp b/libsycl/unittests/queue/sycl_kernel_launch.cpp
index 653dc7306d623..7423dbbfff4b1 100644
--- a/libsycl/unittests/queue/sycl_kernel_launch.cpp
+++ b/libsycl/unittests/queue/sycl_kernel_launch.cpp
@@ -6,47 +6,19 @@
//
//===----------------------------------------------------------------------===//
-#include <common/device_images.hpp>
+#include <common/scoped_binary_registration.hpp>
#include <mock/helpers.hpp>
-#include <detail/program_manager.hpp>
-
#include <sycl/__impl/queue.hpp>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
-#include <array>
#include <cstring>
-#include <string>
using namespace sycl;
using namespace ::testing;
-namespace {
-
-class ScopedKernelRegistration {
-public:
- explicit ScopedKernelRegistration(std::string KernelName)
- : MKernelName(std::move(KernelName)) {
- std::array<llvm::StringRef, 1> KernelNames = {MKernelName};
- MBinary = sycl::unittest::createSYCLDeviceBinary(KernelNames);
- sycl::detail::ProgramAndKernelManager::getInstance().registerFatBin(
- MBinary.data(), MBinary.size());
- }
-
- ~ScopedKernelRegistration() {
- sycl::detail::ProgramAndKernelManager::getInstance().unregisterFatBin(
- MBinary.data(), MBinary.size());
- }
-
-private:
- std::string MKernelName;
- llvm::SmallString<0> MBinary;
-};
-
-} // namespace
-
class sycl::detail::MockQueue : public sycl::queue {
public:
using sycl::queue::sycl_kernel_launch;
@@ -60,7 +32,7 @@ struct KernelData {
TEST(Queue, KernelLaunch) {
mock::MockWrapper Mock;
- ScopedKernelRegistration Registration{"TestKernel"};
+ sycl::unittests::ScopedKernelRegistration Registration{"TestKernel"};
sycl::detail::MockQueue Q;
KernelData Data{};
Data.Arr[0] = 'A';
More information about the llvm-commits
mailing list