From 329c8e3bbdbe37c20172767141e08fda8f44f9c2 Mon Sep 17 00:00:00 2001 From: "Ptak, Slawomir" Date: Wed, 15 Jul 2026 10:06:15 +0000 Subject: [PATCH 1/9] [SYCL] Reusable Events limitation for cross-context dependencies. Currently, cross-context dependencies are not supported for the Reusable Events APIs. Cross-context dependencies use host tasks internally, so the event wait might be queued behind the host task in the runtime, which is currently not supported by the extension APIs. --- sycl/source/detail/queue_impl.cpp | 8 +++- sycl/source/reusable_events.cpp | 26 +++++++++++ sycl/unittests/Extensions/ReusableEvents.cpp | 46 -------------------- 3 files changed, 33 insertions(+), 47 deletions(-) diff --git a/sycl/source/detail/queue_impl.cpp b/sycl/source/detail/queue_impl.cpp index fbfadfc8c39e8..ac84c9491653e 100644 --- a/sycl/source/detail/queue_impl.cpp +++ b/sycl/source/detail/queue_impl.cpp @@ -615,11 +615,17 @@ EventImplPtr queue_impl::submit_barrier_direct_impl( /*SchedulerBypass*/ true}; } - if (EventForReuse) { + if (EventForReuse || !CallerNeedsEvent) { // Current limitation: reusable events require scheduler bypass so that // the barrier can be submitted directly to the backend with the reusable // event's handle as the output event. Scheduler bypass is not possible // when dependencies include host tasks or cross-context dependencies. + // + // This limitation applies to both: enqueue_signal_event and + // enqueue_wait_event(s). + // + // The !CallerNeedsEvent condition is used to detect the + // enqueue_wait_event(s) function calls. throw sycl::exception(sycl::make_error_code(errc::invalid), "An event cannot be enqueued for signaling behind " "a command which is not enqueued in the backend."); diff --git a/sycl/source/reusable_events.cpp b/sycl/source/reusable_events.cpp index 642edd4e298d8..2cf719051cdb7 100644 --- a/sycl/source/reusable_events.cpp +++ b/sycl/source/reusable_events.cpp @@ -60,6 +60,18 @@ __SYCL_EXPORT sycl::event make_event(const sycl::context &ctxt, __SYCL_EXPORT void enqueue_wait_event(sycl::queue q, const event &evt) { detail::queue_impl &QueueImpl = *sycl::detail::getSyclObjImpl(q); + detail::event_impl &EventImpl = *sycl::detail::getSyclObjImpl(evt); + + // Current limitation: + // The queue and an event need to be in the same context. The reason + // is, that cross-context dependencies use host tasks, and the wait + // command might be queued in the runtime. This flow is currently + // not supported by the Reusable Events APIs. + if (&QueueImpl.getContextImpl() != &EventImpl.getContextImpl()) { + throw sycl::exception( + sycl::make_error_code(errc::invalid), + "A queue and an event need to be in the same context."); + } QueueImpl.submit_barrier_direct_without_event( sycl::span(&evt, 1), detail::CGType::BarrierWaitlist, @@ -70,6 +82,20 @@ __SYCL_EXPORT void enqueue_wait_events(sycl::queue q, const std::vector &evts) { detail::queue_impl &QueueImpl = *sycl::detail::getSyclObjImpl(q); + // Current limitation: + // The queue and all the events need to be in the same context. The + // reason is, that cross-context dependencies use host tasks, and the + // wait command might be queued in the runtime. This flow is currently + // not supported by the Reusable Events APIs. + for (sycl::event evt : evts) { + detail::event_impl &EventImpl = *sycl::detail::getSyclObjImpl(evt); + if (&QueueImpl.getContextImpl() != &EventImpl.getContextImpl()) { + throw sycl::exception( + sycl::make_error_code(errc::invalid), + "A queue and all the events need to be in the same context."); + } + } + QueueImpl.submit_barrier_direct_without_event( evts, detail::CGType::BarrierWaitlist, detail::code_location::current()); } diff --git a/sycl/unittests/Extensions/ReusableEvents.cpp b/sycl/unittests/Extensions/ReusableEvents.cpp index f572f25e8fcba..f3fbb7164956d 100644 --- a/sycl/unittests/Extensions/ReusableEvents.cpp +++ b/sycl/unittests/Extensions/ReusableEvents.cpp @@ -443,52 +443,6 @@ TEST_F(ReusableEventsTest, EventInDependsOn) { Queue.wait(); } -// Cross-context events with wait -TEST_F(ReusableEventsTest, CrossContextEventWait) { - mock::getCallbacks().set_replace_callback("urDeviceGet", - &redefinedUrDeviceGet); - mock::getCallbacks().set_replace_callback("urDeviceRelease", - &redefinedUrDeviceRelease); - - sycl::platform Plt = sycl::platform(); - auto Devices = Plt.get_devices(); - - if (Devices.size() < 2) { - GTEST_SKIP() << "Need at least 2 devices for this test"; - } - - const sycl::device Dev1 = Devices[0]; - const sycl::device Dev2 = Devices[1]; - sycl::context Ctx1{Dev1}; - sycl::context Ctx2{Dev2}; - - sycl::queue Queue1{Ctx1, Dev1}; - sycl::queue Queue2{Ctx2, Dev2}; - - auto event = syclex::make_event(Ctx1); - - mock::getCallbacks().set_replace_callback( - "urEnqueueEventsWaitWithBarrierExt", - &redefinedUrEnqueueEventsWaitWithBarrierExt_signal); - - syclex::enqueue_signal_event(Queue1, event); - - EXPECT_EQ(RedefinedUrEnqueueEventsWaitWithBarrierExt_signal_counter, 1); - - ExpectedNumEventsInWaitList = 1; - mock::getCallbacks().set_replace_callback( - "urEnqueueEventsWaitWithBarrierExt", - &redefinedUrEnqueueEventsWaitWithBarrierExt_wait); - - // Event from different context should still work with enqueue_wait_event - EXPECT_NO_THROW({ syclex::enqueue_wait_event(Queue2, event); }); - - EXPECT_EQ(RedefinedUrEnqueueEventsWaitWithBarrierExt_wait_counter, 1); - - Queue1.wait(); - Queue2.wait(); -} - // Cross-context make_event and signal event - not allowed TEST_F(ReusableEventsTest, CrossContextMakeEventSignalEvent) { mock::getCallbacks().set_replace_callback("urDeviceGet", From f8fb98a77858194b3c52d6fbf8b529f100179bb0 Mon Sep 17 00:00:00 2001 From: "Ptak, Slawomir" Date: Fri, 17 Jul 2026 09:47:30 +0000 Subject: [PATCH 2/9] Update the unit tests to check for the exception, change the exception messages. --- sycl/source/reusable_events.cpp | 5 +- sycl/unittests/Extensions/ReusableEvents.cpp | 93 ++++++++++++++++++++ 2 files changed, 96 insertions(+), 2 deletions(-) diff --git a/sycl/source/reusable_events.cpp b/sycl/source/reusable_events.cpp index 2cf719051cdb7..c103ba0637312 100644 --- a/sycl/source/reusable_events.cpp +++ b/sycl/source/reusable_events.cpp @@ -70,7 +70,7 @@ __SYCL_EXPORT void enqueue_wait_event(sycl::queue q, const event &evt) { if (&QueueImpl.getContextImpl() != &EventImpl.getContextImpl()) { throw sycl::exception( sycl::make_error_code(errc::invalid), - "A queue and an event need to be in the same context."); + "Not implemented yet. Event context must match the queue context."); } QueueImpl.submit_barrier_direct_without_event( @@ -92,7 +92,8 @@ __SYCL_EXPORT void enqueue_wait_events(sycl::queue q, if (&QueueImpl.getContextImpl() != &EventImpl.getContextImpl()) { throw sycl::exception( sycl::make_error_code(errc::invalid), - "A queue and all the events need to be in the same context."); + "Not implemented yet. Context of all events must match the " + "queue context."); } } diff --git a/sycl/unittests/Extensions/ReusableEvents.cpp b/sycl/unittests/Extensions/ReusableEvents.cpp index f3fbb7164956d..828176517660d 100644 --- a/sycl/unittests/Extensions/ReusableEvents.cpp +++ b/sycl/unittests/Extensions/ReusableEvents.cpp @@ -443,6 +443,99 @@ TEST_F(ReusableEventsTest, EventInDependsOn) { Queue.wait(); } +// Cross-context event with wait. +// Current limitation is that the cross-context wait +// is not supported. +TEST_F(ReusableEventsTest, CrossContextEventWait) { + mock::getCallbacks().set_replace_callback("urDeviceGet", + &redefinedUrDeviceGet); + mock::getCallbacks().set_replace_callback("urDeviceRelease", + &redefinedUrDeviceRelease); + + sycl::platform Plt = sycl::platform(); + auto Devices = Plt.get_devices(); + + if (Devices.size() < 2) { + GTEST_SKIP() << "Need at least 2 devices for this test"; + } + + const sycl::device Dev1 = Devices[0]; + const sycl::device Dev2 = Devices[1]; + sycl::context Ctx1{Dev1}; + sycl::context Ctx2{Dev2}; + + sycl::queue Queue1{Ctx1, Dev1}; + sycl::queue Queue2{Ctx2, Dev2}; + + auto event = syclex::make_event(Ctx1); + + bool exception = false; + + // Current limiation is that an event from different context + // cannot be used with enqueue_wait_event + try { + syclex::enqueue_wait_event(Queue2, event); + } catch (sycl::exception const &e) { + exception = true; + EXPECT_EQ(e.code(), sycl::errc::invalid); + EXPECT_STREQ(e.what(), "Not implemented yet. Event context must " + "match the queue context."); + } + + EXPECT_TRUE(exception); + + Queue1.wait(); + Queue2.wait(); +} + +// Cross-context events with wait (multiple events). +// Current limitation is that the cross-context wait +// is not supported. +TEST_F(ReusableEventsTest, CrossContextEventsWait) { + mock::getCallbacks().set_replace_callback("urDeviceGet", + &redefinedUrDeviceGet); + mock::getCallbacks().set_replace_callback("urDeviceRelease", + &redefinedUrDeviceRelease); + + sycl::platform Plt = sycl::platform(); + auto Devices = Plt.get_devices(); + + if (Devices.size() < 2) { + GTEST_SKIP() << "Need at least 2 devices for this test"; + } + + const sycl::device Dev1 = Devices[0]; + const sycl::device Dev2 = Devices[1]; + sycl::context Ctx1{Dev1}; + sycl::context Ctx2{Dev2}; + + sycl::queue Queue1{Ctx1, Dev1}; + sycl::queue Queue2{Ctx2, Dev2}; + + auto event1 = syclex::make_event(Ctx1); + auto event2 = syclex::make_event(Ctx1); + + std::vector events{event1, event2}; + + bool exception = false; + + // Current limiation is that an event from different context + // cannot be used with enqueue_wait_event + try { + syclex::enqueue_wait_events(Queue2, events); + } catch (sycl::exception const &e) { + exception = true; + EXPECT_EQ(e.code(), sycl::errc::invalid); + EXPECT_STREQ(e.what(), "Not implemented yet. Context of all events must " + "match the queue context."); + } + + EXPECT_TRUE(exception); + + Queue1.wait(); + Queue2.wait(); +} + // Cross-context make_event and signal event - not allowed TEST_F(ReusableEventsTest, CrossContextMakeEventSignalEvent) { mock::getCallbacks().set_replace_callback("urDeviceGet", From 177f1e9d0408a86682d2796043348c192df414dd Mon Sep 17 00:00:00 2001 From: "Ptak, Slawomir" Date: Fri, 17 Jul 2026 10:56:36 +0000 Subject: [PATCH 3/9] Extend the exception message. --- sycl/source/detail/queue_impl.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sycl/source/detail/queue_impl.cpp b/sycl/source/detail/queue_impl.cpp index ac84c9491653e..f5ac91d69626f 100644 --- a/sycl/source/detail/queue_impl.cpp +++ b/sycl/source/detail/queue_impl.cpp @@ -627,8 +627,8 @@ EventImplPtr queue_impl::submit_barrier_direct_impl( // The !CallerNeedsEvent condition is used to detect the // enqueue_wait_event(s) function calls. throw sycl::exception(sycl::make_error_code(errc::invalid), - "An event cannot be enqueued for signaling behind " - "a command which is not enqueued in the backend."); + "An event cannot be enqueued for signaling or waiting " + "behind a command which is not enqueued in the backend."); } std::unique_ptr CommandGroup; From 97e987de1f63c1a62ac90677d8dfe8dd48a21feb Mon Sep 17 00:00:00 2001 From: "Ptak, Slawomir" Date: Fri, 17 Jul 2026 11:29:47 +0000 Subject: [PATCH 4/9] Make the limitation permanent. --- sycl/source/reusable_events.cpp | 13 ++++--------- sycl/unittests/Extensions/ReusableEvents.cpp | 14 ++++---------- 2 files changed, 8 insertions(+), 19 deletions(-) diff --git a/sycl/source/reusable_events.cpp b/sycl/source/reusable_events.cpp index c103ba0637312..70ca5c9920308 100644 --- a/sycl/source/reusable_events.cpp +++ b/sycl/source/reusable_events.cpp @@ -62,15 +62,13 @@ __SYCL_EXPORT void enqueue_wait_event(sycl::queue q, const event &evt) { detail::queue_impl &QueueImpl = *sycl::detail::getSyclObjImpl(q); detail::event_impl &EventImpl = *sycl::detail::getSyclObjImpl(evt); - // Current limitation: // The queue and an event need to be in the same context. The reason // is, that cross-context dependencies use host tasks, and the wait - // command might be queued in the runtime. This flow is currently - // not supported by the Reusable Events APIs. + // command might be queued in the runtime. if (&QueueImpl.getContextImpl() != &EventImpl.getContextImpl()) { throw sycl::exception( sycl::make_error_code(errc::invalid), - "Not implemented yet. Event context must match the queue context."); + "Event context must match the queue context."); } QueueImpl.submit_barrier_direct_without_event( @@ -82,18 +80,15 @@ __SYCL_EXPORT void enqueue_wait_events(sycl::queue q, const std::vector &evts) { detail::queue_impl &QueueImpl = *sycl::detail::getSyclObjImpl(q); - // Current limitation: // The queue and all the events need to be in the same context. The // reason is, that cross-context dependencies use host tasks, and the - // wait command might be queued in the runtime. This flow is currently - // not supported by the Reusable Events APIs. + // wait command might be queued in the runtime. for (sycl::event evt : evts) { detail::event_impl &EventImpl = *sycl::detail::getSyclObjImpl(evt); if (&QueueImpl.getContextImpl() != &EventImpl.getContextImpl()) { throw sycl::exception( sycl::make_error_code(errc::invalid), - "Not implemented yet. Context of all events must match the " - "queue context."); + "Context of all events must match the queue context."); } } diff --git a/sycl/unittests/Extensions/ReusableEvents.cpp b/sycl/unittests/Extensions/ReusableEvents.cpp index 828176517660d..824f311b25a40 100644 --- a/sycl/unittests/Extensions/ReusableEvents.cpp +++ b/sycl/unittests/Extensions/ReusableEvents.cpp @@ -443,9 +443,7 @@ TEST_F(ReusableEventsTest, EventInDependsOn) { Queue.wait(); } -// Cross-context event with wait. -// Current limitation is that the cross-context wait -// is not supported. +// Cross-context event with wait - not supported. TEST_F(ReusableEventsTest, CrossContextEventWait) { mock::getCallbacks().set_replace_callback("urDeviceGet", &redefinedUrDeviceGet); @@ -478,8 +476,7 @@ TEST_F(ReusableEventsTest, CrossContextEventWait) { } catch (sycl::exception const &e) { exception = true; EXPECT_EQ(e.code(), sycl::errc::invalid); - EXPECT_STREQ(e.what(), "Not implemented yet. Event context must " - "match the queue context."); + EXPECT_STREQ(e.what(), "Event context must match the queue context."); } EXPECT_TRUE(exception); @@ -488,9 +485,7 @@ TEST_F(ReusableEventsTest, CrossContextEventWait) { Queue2.wait(); } -// Cross-context events with wait (multiple events). -// Current limitation is that the cross-context wait -// is not supported. +// Cross-context events with wait (multiple events) - not supported. TEST_F(ReusableEventsTest, CrossContextEventsWait) { mock::getCallbacks().set_replace_callback("urDeviceGet", &redefinedUrDeviceGet); @@ -526,8 +521,7 @@ TEST_F(ReusableEventsTest, CrossContextEventsWait) { } catch (sycl::exception const &e) { exception = true; EXPECT_EQ(e.code(), sycl::errc::invalid); - EXPECT_STREQ(e.what(), "Not implemented yet. Context of all events must " - "match the queue context."); + EXPECT_STREQ(e.what(), "Context of all events must match the queue context."); } EXPECT_TRUE(exception); From c4b8e548bf259a7302283337e870837787ccfdc7 Mon Sep 17 00:00:00 2001 From: "Ptak, Slawomir" Date: Mon, 20 Jul 2026 11:18:52 +0000 Subject: [PATCH 5/9] Introduce reusable and non-reusable events. --- sycl/include/sycl/event.hpp | 4 + sycl/source/detail/event_deps.hpp | 5 + sycl/source/detail/event_impl.hpp | 8 +- sycl/source/event.cpp | 2 + sycl/source/handler.cpp | 7 ++ sycl/source/queue.cpp | 7 ++ sycl/source/reusable_events.cpp | 29 +++++- sycl/unittests/Extensions/CMakeLists.txt | 1 - .../Extensions/ExtOneapiBarrierOpt.cpp | 58 ------------ sycl/unittests/Extensions/ReusableEvents.cpp | 91 ++++++++++++++++--- 10 files changed, 138 insertions(+), 74 deletions(-) delete mode 100644 sycl/unittests/Extensions/ExtOneapiBarrierOpt.cpp diff --git a/sycl/include/sycl/event.hpp b/sycl/include/sycl/event.hpp index 5e79422e398b1..5bc98973c8d95 100644 --- a/sycl/include/sycl/event.hpp +++ b/sycl/include/sycl/event.hpp @@ -151,6 +151,10 @@ class __SYCL_EXPORT event : public detail::OwnerLessBase { /// enabled. bool ext_oneapi_ipc_enabled() const noexcept; + /// Returns true if the event is reusable (can be enqueued for signaling + /// multiple times). + bool ext_oneapi_reusable() const noexcept; + private: event(std::shared_ptr EventImpl); diff --git a/sycl/source/detail/event_deps.hpp b/sycl/source/detail/event_deps.hpp index fb30099dc9821..5ef3c090d0acb 100644 --- a/sycl/source/detail/event_deps.hpp +++ b/sycl/source/detail/event_deps.hpp @@ -59,6 +59,11 @@ void registerEventDependency( "Queue operation cannot depend on discarded event."); } + if (EventImpl->isReusable()) { + throw sycl::exception(make_error_code(errc::invalid), + "Reusable events cannot be used as a dependency."); + } + // Async alloc calls adapter immediately. Any explicit/implicit dependencies // are handled at that point, including in order queue deps. Further calls to // depends_on after an async alloc are explicitly disallowed. diff --git a/sycl/source/detail/event_impl.hpp b/sycl/source/detail/event_impl.hpp index 3c3eb0c0864f8..c35ebb768f090 100644 --- a/sycl/source/detail/event_impl.hpp +++ b/sycl/source/detail/event_impl.hpp @@ -52,7 +52,7 @@ class event_impl { /// If the constructed SYCL event is waited on it will complete immediately. event_impl(private_tag) : MIsFlushed(true), MState(HES_Complete), MIsDefaultConstructed(true), - MIsHostEvent(false) { + MIsHostEvent(false), MReusable(true) { // Need to fail in event() constructor if there are problems with the // ONEAPI_DEVICE_SELECTOR. Deferring may lead to conficts with noexcept // event methods. This ::get() call uses static vars to read and parse the @@ -399,6 +399,8 @@ class event_impl { // Initializes the host profiling info for the event. void initHostProfilingInfo(); + bool isReusable() const noexcept { return MReusable; } + protected: // When instrumentation is enabled emits trace event for event wait begin and // returns the telemetry event generated for the wait @@ -505,6 +507,10 @@ class event_impl { // MEvent is lazily created in first ur handle query. bool MIsDefaultConstructed = false; bool MIsHostEvent = false; + + /// True if the event is reusable (can be enqueued for signaling multiple + /// times). + bool MReusable = false; }; using events_iterator = diff --git a/sycl/source/event.cpp b/sycl/source/event.cpp index 0d6554a5fe102..41d1699bfb202 100644 --- a/sycl/source/event.cpp +++ b/sycl/source/event.cpp @@ -120,6 +120,8 @@ bool event::ext_oneapi_ipc_enabled() const noexcept { return impl->isIPCEnabled(); } +bool event::ext_oneapi_reusable() const noexcept { return impl->isReusable(); } + ur_native_handle_t event::getNative() const { return impl->getNative(); } std::vector event::getNativeVector() const { diff --git a/sycl/source/handler.cpp b/sycl/source/handler.cpp index ae4aa87781dd2..1257be7d137e8 100644 --- a/sycl/source/handler.cpp +++ b/sycl/source/handler.cpp @@ -938,6 +938,13 @@ void handler::ext_oneapi_barrier(const std::vector &WaitList) { impl->MEventsWaitWithBarrier.reserve(WaitList.size()); for (auto &Event : WaitList) { auto EventImpl = detail::getSyclObjImpl(Event); + + if (EventImpl->isReusable()) { + throw sycl::exception( + make_error_code(errc::invalid), + "Reusable events cannot be used as barrier events."); + } + // We could not wait for host task events in backend. // Adding them as dependency to enable proper scheduling. if (EventImpl->isHost()) { diff --git a/sycl/source/queue.cpp b/sycl/source/queue.cpp index 91e5aa5f4f82a..8466ee98e2600 100644 --- a/sycl/source/queue.cpp +++ b/sycl/source/queue.cpp @@ -214,6 +214,13 @@ event queue::ext_oneapi_submit_barrier(const detail::code_location &CodeLoc) { /// group is being enqueued on. event queue::ext_oneapi_submit_barrier(const std::vector &WaitList, const detail::code_location &CodeLoc) { + for (event e : WaitList) { + if (e.ext_oneapi_reusable()) { + throw sycl::exception( + make_error_code(errc::invalid), + "Reusable events cannot be used as barrier events."); + } + } return impl->submit_barrier_direct_with_event( WaitList, detail::CGType::BarrierWaitlist, CodeLoc); } diff --git a/sycl/source/reusable_events.cpp b/sycl/source/reusable_events.cpp index 70ca5c9920308..72fb295520822 100644 --- a/sycl/source/reusable_events.cpp +++ b/sycl/source/reusable_events.cpp @@ -62,6 +62,12 @@ __SYCL_EXPORT void enqueue_wait_event(sycl::queue q, const event &evt) { detail::queue_impl &QueueImpl = *sycl::detail::getSyclObjImpl(q); detail::event_impl &EventImpl = *sycl::detail::getSyclObjImpl(evt); + // Only reusable events are supported. + if (!EventImpl.isReusable()) { + throw sycl::exception(sycl::make_error_code(errc::invalid), + "Event must be reusable."); + } + // The queue and an event need to be in the same context. The reason // is, that cross-context dependencies use host tasks, and the wait // command might be queued in the runtime. @@ -80,11 +86,21 @@ __SYCL_EXPORT void enqueue_wait_events(sycl::queue q, const std::vector &evts) { detail::queue_impl &QueueImpl = *sycl::detail::getSyclObjImpl(q); - // The queue and all the events need to be in the same context. The - // reason is, that cross-context dependencies use host tasks, and the - // wait command might be queued in the runtime. + // For reusable events, the queue and all the events need to be in + // the same context. The reason is, that cross-context dependencies + // use host tasks, and the wait command might be queued in the runtime. + // This would prevent the runtime from capturing the dependency + // at the point in time where this function is called (might delay + // the dependency capture). + // + // All the events need to be reusable. for (sycl::event evt : evts) { detail::event_impl &EventImpl = *sycl::detail::getSyclObjImpl(evt); + if (!EventImpl.isReusable()) { + throw sycl::exception(sycl::make_error_code(errc::invalid), + "All events must be reusable."); + } + if (&QueueImpl.getContextImpl() != &EventImpl.getContextImpl()) { throw sycl::exception( sycl::make_error_code(errc::invalid), @@ -100,6 +116,13 @@ __SYCL_EXPORT void enqueue_signal_event(sycl::queue q, event &evt) { detail::queue_impl &QueueImpl = *sycl::detail::getSyclObjImpl(q); detail::event_impl &EventImpl = *sycl::detail::getSyclObjImpl(evt); + // Only reusable events can be enqueued for signaling. + if (!EventImpl.isReusable()) { + throw sycl::exception( + sycl::make_error_code(errc::invalid), + "Only a reusable event can be enqueued for signaling."); + } + if (EventImpl.isInterop()) { throw sycl::exception( sycl::make_error_code(errc::runtime), diff --git a/sycl/unittests/Extensions/CMakeLists.txt b/sycl/unittests/Extensions/CMakeLists.txt index be80033a525b6..900b2a22fa34a 100644 --- a/sycl/unittests/Extensions/CMakeLists.txt +++ b/sycl/unittests/Extensions/CMakeLists.txt @@ -13,7 +13,6 @@ add_sycl_unittest(ExtensionsTests OBJECT OneAPIProd.cpp EnqueueFunctionsEvents.cpp EnqueueFunctionsPrefetch.cpp - ExtOneapiBarrierOpt.cpp ProfilingTag.cpp KernelProperties.cpp NoDeviceIPVersion.cpp diff --git a/sycl/unittests/Extensions/ExtOneapiBarrierOpt.cpp b/sycl/unittests/Extensions/ExtOneapiBarrierOpt.cpp deleted file mode 100644 index aeba536a15bd3..0000000000000 --- a/sycl/unittests/Extensions/ExtOneapiBarrierOpt.cpp +++ /dev/null @@ -1,58 +0,0 @@ -//==------------------- ExtOneapiBarrierOpt.cpp ----------------------------==// -// -// 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 -#include -#include -#include - -using namespace sycl; - -inline thread_local uint32_t NumEventsInWaitList; - -static ur_result_t redefinedEnqueueEventsWaitWithBarrierExt(void *pParams) { - auto params = - *static_cast(pParams); - NumEventsInWaitList = *(params.pnumEventsInWaitList); - return UR_RESULT_SUCCESS; -} - -class ExtOneapiBarrierOptTest : public ::testing::Test { -public: - ExtOneapiBarrierOptTest() : Mock{} {} - -protected: - void SetUp() override { NumEventsInWaitList = 0; } - -protected: - sycl::unittest::UrMock<> Mock; -}; - -// Check that ext_oneapi_submit_barrier works fine in the scenarios -// when provided waitlist consists of only empty events. -// Tets for https://github.com/intel/llvm/pull/12951 -TEST_F(ExtOneapiBarrierOptTest, EmptyEventTest) { - sycl::queue q1{{sycl::property::queue::in_order()}}; - - mock::getCallbacks().set_after_callback( - "urEnqueueEventsWaitWithBarrierExt", - &redefinedEnqueueEventsWaitWithBarrierExt); - - NumEventsInWaitList = 100; - q1.ext_oneapi_submit_barrier(); - ASSERT_EQ(0u, NumEventsInWaitList); - - // ext_oneapi_submit_barrier should ignore empty, default constructed events. - // Spec says that ext_oneapi_submit_barrier({E1}) should submit a barrier - // that's blocked on E1. But if E1 is empty event, SYCL RT will ignore it and - // won't make the UR call at all. - sycl::event E1{}; - NumEventsInWaitList = 100; - q1.ext_oneapi_submit_barrier({E1}); - ASSERT_EQ(100u, NumEventsInWaitList); -} diff --git a/sycl/unittests/Extensions/ReusableEvents.cpp b/sycl/unittests/Extensions/ReusableEvents.cpp index 824f311b25a40..8a028c260e93b 100644 --- a/sycl/unittests/Extensions/ReusableEvents.cpp +++ b/sycl/unittests/Extensions/ReusableEvents.cpp @@ -231,6 +231,7 @@ TEST_F(ReusableEventsTest, MakeEventDefault) { auto status = event.get_info(); EXPECT_EQ(status, sycl::info::event_command_status::complete); + EXPECT_TRUE(event.ext_oneapi_reusable()); } // make_event should not call UR. @@ -354,6 +355,7 @@ TEST_F(ReusableEventsTest, DefaultConstructor) { auto status = event.get_info(); EXPECT_EQ(status, sycl::info::event_command_status::complete); + EXPECT_TRUE(event.ext_oneapi_reusable()); } EXPECT_EQ(UrEventCreateExp_counter, 0); @@ -374,13 +376,19 @@ TEST_F(ReusableEventsTest, QueueReturnedEvent) { auto event = Queue.submit( [&](sycl::handler &cgh) { cgh.single_task([]() {}); }); - ExpectedNumEventsInWaitList = 1; + EXPECT_FALSE(event.ext_oneapi_reusable()); - // Queue-returned events are associated with the queue's context per spec; - // verify the event is usable with extension wait functions - EXPECT_NO_THROW({ syclex::enqueue_wait_event(Queue, event); }); + bool exception = false; + + try { + syclex::enqueue_wait_event(Queue, event); + } catch (sycl::exception const &e) { + exception = true; + EXPECT_EQ(e.code(), sycl::errc::invalid); + EXPECT_STREQ(e.what(), "Event must be reusable."); + } - EXPECT_EQ(RedefinedUrEnqueueEventsWaitWithBarrierExt_wait_counter, 1); + EXPECT_TRUE(exception); Queue.wait(); } @@ -412,7 +420,7 @@ TEST_F(ReusableEventsTest, ProfilingInfoQuery) { }); } -// Event can be used in depends_on +// Reusable event used in depends_on - not allowed TEST_F(ReusableEventsTest, EventInDependsOn) { mock::getCallbacks().set_replace_callback( "urEnqueueEventsWaitWithBarrierExt", @@ -431,14 +439,20 @@ TEST_F(ReusableEventsTest, EventInDependsOn) { EXPECT_EQ(RedefinedUrEnqueueEventsWaitWithBarrierExt_signal_counter, 1); - ExpectedNumEventsInWaitListKernelLaunch = 1; + bool exception = false; - EXPECT_NO_THROW({ + try { Queue.submit([&](sycl::handler &cgh) { cgh.depends_on(event); cgh.single_task([]() {}); }); - }); + } catch (sycl::exception const &e) { + exception = true; + EXPECT_EQ(e.code(), sycl::errc::invalid); + EXPECT_STREQ(e.what(), "Reusable events cannot be used as a dependency."); + } + + EXPECT_TRUE(exception); Queue.wait(); } @@ -469,8 +483,8 @@ TEST_F(ReusableEventsTest, CrossContextEventWait) { bool exception = false; - // Current limiation is that an event from different context - // cannot be used with enqueue_wait_event + // Event from different context cannot be used + // with enqueue_wait_event. try { syclex::enqueue_wait_event(Queue2, event); } catch (sycl::exception const &e) { @@ -568,6 +582,61 @@ TEST_F(ReusableEventsTest, CrossContextMakeEventSignalEvent) { Queue1.wait(); } +// Reusable event on a barrier wait list - not allowed +TEST_F(ReusableEventsTest, BarrierWaitListReusableEvent) { + sycl::platform Plt = sycl::platform(); + const sycl::device Dev = Plt.get_devices()[0]; + sycl::context Ctx{Dev}; + sycl::queue Queue{Ctx, Dev}; + + auto event = syclex::make_event(Ctx); + + std::vector barrier_wait_list; + barrier_wait_list.push_back(event); + + bool exception = false; + + try { + Queue.ext_oneapi_submit_barrier(barrier_wait_list); + } catch (sycl::exception const &e) { + exception = true; + EXPECT_EQ(e.code(), sycl::errc::invalid); + EXPECT_STREQ(e.what(), "Reusable events cannot be used as barrier events."); + } + + EXPECT_TRUE(exception); + + Queue.wait(); +} + +// Reusable event on a barrier wait list - handler - not allowed +TEST_F(ReusableEventsTest, BarrierWaitListReusableEventHandler) { + sycl::platform Plt = sycl::platform(); + const sycl::device Dev = Plt.get_devices()[0]; + sycl::context Ctx{Dev}; + sycl::queue Queue{Ctx, Dev}; + + auto event = syclex::make_event(Ctx); + + std::vector barrier_wait_list; + barrier_wait_list.push_back(event); + + bool exception = false; + + try { + Queue.submit( + [&](sycl::handler &cgh) { cgh.ext_oneapi_barrier(barrier_wait_list); }); + } catch (sycl::exception const &e) { + exception = true; + EXPECT_EQ(e.code(), sycl::errc::invalid); + EXPECT_STREQ(e.what(), "Reusable events cannot be used as barrier events."); + } + + EXPECT_TRUE(exception); + + Queue.wait(); +} + // Empty event vector wait TEST_F(ReusableEventsTest, EmptyEventVectorWait) { sycl::platform Plt = sycl::platform(); From 38b083052bd6c4ddbb9d571fe9995a4ce2e54ef5 Mon Sep 17 00:00:00 2001 From: "Ptak, Slawomir" Date: Mon, 20 Jul 2026 11:51:58 +0000 Subject: [PATCH 6/9] Update Linux symbols. --- sycl/test/abi/sycl_symbols_linux.dump | 1 + 1 file changed, 1 insertion(+) diff --git a/sycl/test/abi/sycl_symbols_linux.dump b/sycl/test/abi/sycl_symbols_linux.dump index c4987b6733b7b..c56af80e8fcc5 100644 --- a/sycl/test/abi/sycl_symbols_linux.dump +++ b/sycl/test/abi/sycl_symbols_linux.dump @@ -3703,6 +3703,7 @@ _ZNK4sycl3_V15event15getNativeVectorEv _ZNK4sycl3_V15event18get_profiling_infoINS0_4info15event_profiling11command_endEEENS0_6detail28is_event_profiling_info_descIT_E11return_typeEv _ZNK4sycl3_V15event18get_profiling_infoINS0_4info15event_profiling13command_startEEENS0_6detail28is_event_profiling_info_descIT_E11return_typeEv _ZNK4sycl3_V15event18get_profiling_infoINS0_4info15event_profiling14command_submitEEENS0_6detail28is_event_profiling_info_descIT_E11return_typeEv +_ZNK4sycl3_V15event19ext_oneapi_reusableEv _ZNK4sycl3_V15event22ext_oneapi_ipc_enabledEv _ZNK4sycl3_V15event8get_infoINS0_4info5event15reference_countEEENS0_6detail18is_event_info_descIT_E11return_typeEv _ZNK4sycl3_V15event8get_infoINS0_4info5event24command_execution_statusEEENS0_6detail18is_event_info_descIT_E11return_typeEv From be78b88e44a232a90d5627b4f72aca628a4ffcff Mon Sep 17 00:00:00 2001 From: "Ptak, Slawomir" Date: Mon, 20 Jul 2026 12:08:08 +0000 Subject: [PATCH 7/9] Fix formatting. --- sycl/source/detail/queue_impl.cpp | 7 ++++--- sycl/source/reusable_events.cpp | 5 ++--- sycl/unittests/Extensions/ReusableEvents.cpp | 7 ++++--- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/sycl/source/detail/queue_impl.cpp b/sycl/source/detail/queue_impl.cpp index f5ac91d69626f..939a60b90fda0 100644 --- a/sycl/source/detail/queue_impl.cpp +++ b/sycl/source/detail/queue_impl.cpp @@ -626,9 +626,10 @@ EventImplPtr queue_impl::submit_barrier_direct_impl( // // The !CallerNeedsEvent condition is used to detect the // enqueue_wait_event(s) function calls. - throw sycl::exception(sycl::make_error_code(errc::invalid), - "An event cannot be enqueued for signaling or waiting " - "behind a command which is not enqueued in the backend."); + throw sycl::exception( + sycl::make_error_code(errc::invalid), + "An event cannot be enqueued for signaling or waiting " + "behind a command which is not enqueued in the backend."); } std::unique_ptr CommandGroup; diff --git a/sycl/source/reusable_events.cpp b/sycl/source/reusable_events.cpp index 72fb295520822..a94e135cfb3dc 100644 --- a/sycl/source/reusable_events.cpp +++ b/sycl/source/reusable_events.cpp @@ -72,9 +72,8 @@ __SYCL_EXPORT void enqueue_wait_event(sycl::queue q, const event &evt) { // is, that cross-context dependencies use host tasks, and the wait // command might be queued in the runtime. if (&QueueImpl.getContextImpl() != &EventImpl.getContextImpl()) { - throw sycl::exception( - sycl::make_error_code(errc::invalid), - "Event context must match the queue context."); + throw sycl::exception(sycl::make_error_code(errc::invalid), + "Event context must match the queue context."); } QueueImpl.submit_barrier_direct_without_event( diff --git a/sycl/unittests/Extensions/ReusableEvents.cpp b/sycl/unittests/Extensions/ReusableEvents.cpp index 8a028c260e93b..0ae6f7d3f9207 100644 --- a/sycl/unittests/Extensions/ReusableEvents.cpp +++ b/sycl/unittests/Extensions/ReusableEvents.cpp @@ -528,14 +528,15 @@ TEST_F(ReusableEventsTest, CrossContextEventsWait) { bool exception = false; - // Current limiation is that an event from different context - // cannot be used with enqueue_wait_event + // An event from different context cannot be used + // with enqueue_wait_event try { syclex::enqueue_wait_events(Queue2, events); } catch (sycl::exception const &e) { exception = true; EXPECT_EQ(e.code(), sycl::errc::invalid); - EXPECT_STREQ(e.what(), "Context of all events must match the queue context."); + EXPECT_STREQ(e.what(), + "Context of all events must match the queue context."); } EXPECT_TRUE(exception); From d507cb754f93a9da1879e6dbbc37389de67ec2cb Mon Sep 17 00:00:00 2001 From: "Ptak, Slawomir" Date: Mon, 20 Jul 2026 12:11:29 +0000 Subject: [PATCH 8/9] Address review. --- sycl/source/queue.cpp | 2 +- sycl/source/reusable_events.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sycl/source/queue.cpp b/sycl/source/queue.cpp index 8466ee98e2600..d4ef07465833f 100644 --- a/sycl/source/queue.cpp +++ b/sycl/source/queue.cpp @@ -214,7 +214,7 @@ event queue::ext_oneapi_submit_barrier(const detail::code_location &CodeLoc) { /// group is being enqueued on. event queue::ext_oneapi_submit_barrier(const std::vector &WaitList, const detail::code_location &CodeLoc) { - for (event e : WaitList) { + for (const event &e : WaitList) { if (e.ext_oneapi_reusable()) { throw sycl::exception( make_error_code(errc::invalid), diff --git a/sycl/source/reusable_events.cpp b/sycl/source/reusable_events.cpp index a94e135cfb3dc..041730c91ece6 100644 --- a/sycl/source/reusable_events.cpp +++ b/sycl/source/reusable_events.cpp @@ -93,7 +93,7 @@ __SYCL_EXPORT void enqueue_wait_events(sycl::queue q, // the dependency capture). // // All the events need to be reusable. - for (sycl::event evt : evts) { + for (const sycl::event &evt : evts) { detail::event_impl &EventImpl = *sycl::detail::getSyclObjImpl(evt); if (!EventImpl.isReusable()) { throw sycl::exception(sycl::make_error_code(errc::invalid), From 081b9a7c4f5156fa699bc7595d848beaf9dcd199 Mon Sep 17 00:00:00 2001 From: "Ptak, Slawomir" Date: Mon, 20 Jul 2026 13:22:07 +0000 Subject: [PATCH 9/9] Update Windows symbols. --- sycl/test/abi/sycl_symbols_windows.dump | 1 + 1 file changed, 1 insertion(+) diff --git a/sycl/test/abi/sycl_symbols_windows.dump b/sycl/test/abi/sycl_symbols_windows.dump index 29870ad4f5e92..66471ba9b9527 100644 --- a/sycl/test/abi/sycl_symbols_windows.dump +++ b/sycl/test/abi/sycl_symbols_windows.dump @@ -3933,6 +3933,7 @@ ?ext_oneapi_owner_before@device@_V1@sycl@@QEBA_NAEBV123@@Z ?ext_oneapi_owner_before@device@_V1@sycl@@QEBA_NAEBV?$weak_object@Vdevice@_V1@sycl@@@oneapi@ext@23@@Z ?ext_oneapi_prod@queue@_V1@sycl@@QEAAXXZ +?ext_oneapi_reusable@event@_V1@sycl@@QEBA_NXZ ?ext_oneapi_set_external_event@queue@_V1@sycl@@QEAAXAEBVevent@23@@Z ?ext_oneapi_signal_external_semaphore@handler@_V1@sycl@@QEAAXUexternal_semaphore@experimental@oneapi@ext@23@@Z ?ext_oneapi_signal_external_semaphore@handler@_V1@sycl@@QEAAXUexternal_semaphore@experimental@oneapi@ext@23@_K@Z