diff --git a/sycl/source/detail/event_impl.hpp b/sycl/source/detail/event_impl.hpp index b560d721728a6..312bb589760b7 100644 --- a/sycl/source/detail/event_impl.hpp +++ b/sycl/source/detail/event_impl.hpp @@ -329,6 +329,13 @@ class event_impl { bool isProfilingTagEvent() const noexcept { return MProfilingTagEvent; } + // Check if this event is an interoperability event. + bool isInterop() const noexcept { + // As an indication of interoperability event, we use the absence of the + // queue and command, as well as the fact that it is not in enqueued state. + return MEvent && MQueue.expired() && !MIsEnqueued && !MCommand; + } + protected: // When instrumentation is enabled emits trace event for event wait begin and // returns the telemetry event generated for the wait diff --git a/sycl/source/detail/scheduler/commands.cpp b/sycl/source/detail/scheduler/commands.cpp index 953ad2bee0444..c5e8fc2c3a2cd 100644 --- a/sycl/source/detail/scheduler/commands.cpp +++ b/sycl/source/detail/scheduler/commands.cpp @@ -265,9 +265,12 @@ std::vector Command::getUrEventsBlocking( if (EventImpl->isDefaultConstructed() || EventImpl->isHost() || EventImpl->isNOP()) continue; - // In this path nullptr native event means that the command has not been - // enqueued. It may happen if async enqueue in a host task is involved. - if (!EventImpl->isEnqueued()) { + + // If command has not been enqueued then we have to enqueue it. + // It may happen if async enqueue in a host task is involved. + // Interoperability events are special cases and they are not enqueued, as + // they don't have an associated queue and command. + if (!EventImpl->isInterop() && !EventImpl->isEnqueued()) { if (!EventImpl->getCommand() || !static_cast(EventImpl->getCommand())->producesPiEvent()) continue; diff --git a/sycl/test-e2e/Regression/barrier_waitlist_with_interop_event.cpp b/sycl/test-e2e/Regression/barrier_waitlist_with_interop_event.cpp new file mode 100644 index 0000000000000..f5a54f1a67dc2 --- /dev/null +++ b/sycl/test-e2e/Regression/barrier_waitlist_with_interop_event.cpp @@ -0,0 +1,47 @@ +// REQUIRES: level_zero, level_zero_dev_kit +// RUN: %{build} %level_zero_options -o %t.out +// RUN: %{run} %t.out +// UNSUPPORTED: ze_debug + +#include +#include +#include +#include + +// Test checks the case when an interoperability event is passed as a dependency +// to the barrier. In such case, waiting for the event produced by barrier must +// guarantee completion of the interoperability event. + +using namespace sycl; + +int main() { + sycl::queue Queue; + if (!Queue.get_device().get_info()) + return 0; + + const size_t N = 1024; + int *Data = sycl::malloc_shared(N, Queue); + auto FillEvent = Queue.fill(Data, 0, N); + auto FillZeEvent = get_native(FillEvent); + + backend_input_t EventInteropInput = { + FillZeEvent}; + EventInteropInput.Ownership = sycl::ext::oneapi::level_zero::ownership::keep; + auto EventInterop = make_event( + EventInteropInput, Queue.get_context()); + + auto BarrierEvent = Queue.ext_oneapi_submit_barrier({EventInterop}); + BarrierEvent.wait(); + + if (EventInterop.get_info() != + sycl::info::event_command_status::complete) { + Queue.wait(); + sycl::free(Data, Queue); + return -1; + } + + // Free the USM memory + sycl::free(Data, Queue); + + return 0; +}