Skip to content
15 changes: 11 additions & 4 deletions sycl/source/detail/queue_impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -615,14 +615,21 @@ 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.
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.");
//
// 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 or waiting "
"behind a command which is not enqueued in the backend.");
}

std::unique_ptr<detail::CG> CommandGroup;
Expand Down
36 changes: 27 additions & 9 deletions sycl/source/reusable_events.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,31 @@ __SYCL_EXPORT sycl::event make_event(const sycl::context &ctxt,
return RetEvent;
}

static void CheckEventAndThrow(detail::event_impl &EventImpl,
Comment thread
slawekptak marked this conversation as resolved.
detail::context_impl &ContextImpl) {
if (EventImpl.isHost()) {
throw sycl::exception(sycl::make_error_code(errc::invalid),
"Host events cannot be enqueued for waiting.");
}

// 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 (&EventImpl.getContextImpl() != &ContextImpl) {
throw sycl::exception(sycl::make_error_code(errc::invalid),
"Event context must match the queue context.");
}
Comment thread
slawekptak marked this conversation as resolved.
}

} // namespace detail

__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);

detail::CheckEventAndThrow(EventImpl, QueueImpl.getContextImpl());

QueueImpl.submit_barrier_direct_without_event(
sycl::span<const event>(&evt, 1), detail::CGType::BarrierWaitlist,
Expand All @@ -70,6 +91,11 @@ __SYCL_EXPORT void enqueue_wait_events(sycl::queue q,
const std::vector<event> &evts) {
detail::queue_impl &QueueImpl = *sycl::detail::getSyclObjImpl(q);

for (const sycl::event &evt : evts) {
detail::CheckEventAndThrow(*sycl::detail::getSyclObjImpl(evt),
QueueImpl.getContextImpl());
}

QueueImpl.submit_barrier_direct_without_event(
evts, detail::CGType::BarrierWaitlist, detail::code_location::current());
}
Expand All @@ -90,15 +116,7 @@ __SYCL_EXPORT void enqueue_signal_event(sycl::queue q, event &evt) {
"on a queue which is recording a graph.");
}

detail::context_impl &QueueContextImpl =
*sycl::detail::getSyclObjImpl(q.get_context());

detail::context_impl &EventContextImpl = EventImpl.getContextImpl();

if (&QueueContextImpl != &EventContextImpl) {
throw sycl::exception(sycl::make_error_code(errc::invalid),
"Event context must match the queue context.");
}
detail::CheckEventAndThrow(EventImpl, QueueImpl.getContextImpl());

// An IPC event cannot be signaled on a profiling-enabled queue.
if (EventImpl.isIPCEnabled() && QueueImpl.MIsProfilingEnabled) {
Expand Down
75 changes: 60 additions & 15 deletions sycl/unittests/Extensions/ReusableEvents.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -443,7 +443,9 @@ TEST_F(ReusableEventsTest, EventInDependsOn) {
Queue.wait();
}

// Cross-context events with 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);
Expand All @@ -467,26 +469,69 @@ TEST_F(ReusableEventsTest, CrossContextEventWait) {

auto event = syclex::make_event(Ctx1);

mock::getCallbacks().set_replace_callback(
"urEnqueueEventsWaitWithBarrierExt",
&redefinedUrEnqueueEventsWaitWithBarrierExt_signal);
bool exception = false;

syclex::enqueue_signal_event(Queue1, event);
// Current limitation 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(), "Event context must match the queue context.");
}

EXPECT_EQ(RedefinedUrEnqueueEventsWaitWithBarrierExt_signal_counter, 1);
EXPECT_TRUE(exception);

ExpectedNumEventsInWaitList = 1;
mock::getCallbacks().set_replace_callback(
"urEnqueueEventsWaitWithBarrierExt",
&redefinedUrEnqueueEventsWaitWithBarrierExt_wait);
Queue1.wait();
Queue2.wait();
}

// Event from different context should still work with enqueue_wait_event
EXPECT_NO_THROW({ syclex::enqueue_wait_event(Queue2, event); });
// 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);

EXPECT_EQ(RedefinedUrEnqueueEventsWaitWithBarrierExt_wait_counter, 1);
sycl::platform Plt = sycl::platform();
auto Devices = Plt.get_devices();

Queue1.wait();
Queue2.wait();
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 Queue{Ctx1, Dev1};

// Check if the exception is thrown if one of the events
// has different context than the queue.
auto event1 = syclex::make_event(Ctx1);
auto event2 = syclex::make_event(Ctx2);

std::vector<sycl::event> events{event1, event2};

bool exception = false;

// Current limitation is that an event from different context
// cannot be used with enqueue_wait_event
try {
syclex::enqueue_wait_events(Queue, events);
} catch (sycl::exception const &e) {
exception = true;
EXPECT_EQ(e.code(), sycl::errc::invalid);
EXPECT_STREQ(e.what(), "Event context must match the queue context.");
}

EXPECT_TRUE(exception);

Queue.wait();
}

// Cross-context make_event and signal event - not allowed
Expand Down
Loading