Skip to content

Commit 767ddfe

Browse files
authored
[SYCL] Fix waiting for events completion by commands on host-queue (#1737)
Signed-off-by: Sergey Kanaev <[email protected]>
1 parent f9b6b5e commit 767ddfe

File tree

4 files changed

+160
-4
lines changed

4 files changed

+160
-4
lines changed

sycl/source/detail/scheduler/commands.cpp

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -237,11 +237,44 @@ void Command::waitForEvents(QueueImplPtr Queue,
237237
RT::PiEvent &Event) {
238238

239239
if (!EventImpls.empty()) {
240-
std::vector<RT::PiEvent> RawEvents = getPiEvents(EventImpls);
241240
if (Queue->is_host()) {
242-
const detail::plugin &Plugin = EventImpls[0]->getPlugin();
243-
Plugin.call<PiApiKind::piEventsWait>(RawEvents.size(), &RawEvents[0]);
241+
// Host queue can wait for events from different contexts, i.e. it may
242+
// contain events with different contexts in its MPreparedDepsEvents.
243+
// OpenCL 2.1 spec says that clWaitForEvents will return
244+
// CL_INVALID_CONTEXT if events specified in the list do not belong to
245+
// the same context. Thus we split all the events into per-context map.
246+
// An example. We have two queues for the same CPU device: Q1, Q2. Thus
247+
// we will have two different contexts for the same CPU device: C1, C2.
248+
// Also we have default host queue. This queue is accessible via
249+
// Scheduler. Now, let's assume we have three different events: E1(C1),
250+
// E2(C1), E3(C2). Also, we have an EmptyCommand which is to be executed
251+
// on host queue. The command's MPreparedDepsEvents will contain all three
252+
// events (E1, E2, E3). Now, if piEventsWait is called for all three
253+
// events we'll experience failure with CL_INVALID_CONTEXT 'cause these
254+
// events refer to different contexts.
255+
std::map<context_impl *, std::vector<EventImplPtr>>
256+
RequiredEventsPerContext;
257+
258+
for (const EventImplPtr &Event : EventImpls) {
259+
ContextImplPtr Context = Event->getContextImpl();
260+
assert(Context.get() &&
261+
"Only non-host events are expected to be waited for here");
262+
RequiredEventsPerContext[Context.get()].push_back(Event);
263+
}
264+
265+
for (auto &CtxWithEvents : RequiredEventsPerContext) {
266+
std::vector<RT::PiEvent> RawEvents = getPiEvents(CtxWithEvents.second);
267+
CtxWithEvents.first->getPlugin().call<PiApiKind::piEventsWait>(
268+
RawEvents.size(), RawEvents.data());
269+
}
244270
} else {
271+
#ifndef NDEBUG
272+
for (const EventImplPtr &Event : EventImpls)
273+
assert(Event->getContextImpl().get() &&
274+
"Only non-host events are expected to be waited for here");
275+
#endif
276+
277+
std::vector<RT::PiEvent> RawEvents = getPiEvents(EventImpls);
245278
const detail::plugin &Plugin = Queue->getPlugin();
246279
Plugin.call<PiApiKind::piEnqueueEventsWait>(
247280
Queue->getHandleRef(), RawEvents.size(), &RawEvents[0], &Event);

sycl/unittests/scheduler/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@ add_sycl_unittest(SchedulerTests OBJECT
44
FinishedCmdCleanup.cpp
55
LeafLimit.cpp
66
MemObjCommandCleanup.cpp
7+
CommandsWaitForEvents.cpp
78
utils.cpp
8-
)
9+
)
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
//==-------- CommandsWaitForEvents.cpp --- Scheduler unit tests ------------==//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "SchedulerTest.hpp"
10+
#include "SchedulerTestUtils.hpp"
11+
#include <helpers/PiMock.hpp>
12+
13+
using namespace cl::sycl;
14+
15+
struct TestCtx {
16+
queue &Q1;
17+
queue &Q2;
18+
19+
std::shared_ptr<detail::context_impl> Ctx1;
20+
std::shared_ptr<detail::context_impl> Ctx2;
21+
22+
pi_event EventCtx1 = reinterpret_cast<pi_event>(0x01);
23+
pi_event EventCtx2 = reinterpret_cast<pi_event>(0x02);
24+
25+
bool EventCtx1WasWaited = false;
26+
bool EventCtx2WasWaited = false;
27+
28+
TestCtx(queue &Queue1, queue &Queue2)
29+
: Q1(Queue1), Q2(Queue2), Ctx1{detail::getSyclObjImpl(Q1.get_context())},
30+
Ctx2{detail::getSyclObjImpl(Q2.get_context())} {}
31+
};
32+
33+
std::unique_ptr<TestCtx> TestContext;
34+
35+
pi_result waitFunc(pi_uint32 N, const pi_event *List) {
36+
EXPECT_EQ(N, 1u) << "piEventsWait called for different contexts\n";
37+
38+
EXPECT_TRUE((TestContext->EventCtx1 == *List) ||
39+
(TestContext->EventCtx2 == *List))
40+
<< "piEventsWait called for unknown event";
41+
42+
if (TestContext->EventCtx1 == *List)
43+
TestContext->EventCtx1WasWaited = true;
44+
45+
if (TestContext->EventCtx2 == *List)
46+
TestContext->EventCtx2WasWaited = true;
47+
48+
return PI_SUCCESS;
49+
}
50+
51+
pi_result retainReleaseFunc(pi_event) { return PI_SUCCESS; }
52+
53+
pi_result getEventInfoFunc(pi_event Event, pi_event_info PName, size_t PVSize,
54+
void *PV, size_t *PVSizeRet) {
55+
EXPECT_EQ(PName, PI_EVENT_INFO_CONTEXT) << "Unknown param name";
56+
57+
if (Event == TestContext->EventCtx1)
58+
*reinterpret_cast<pi_context *>(PV) =
59+
reinterpret_cast<pi_context>(TestContext->Ctx1->get());
60+
else if (Event == TestContext->EventCtx2)
61+
*reinterpret_cast<pi_context *>(PV) =
62+
reinterpret_cast<pi_context>(TestContext->Ctx2->get());
63+
64+
return PI_SUCCESS;
65+
}
66+
67+
TEST_F(SchedulerTest, CommandsWaitForEvents) {
68+
default_selector Selector{};
69+
if (Selector.select_device().is_host()) {
70+
std::cerr << "Not run due to host-only environment\n";
71+
return;
72+
}
73+
74+
queue Q1;
75+
queue Q2;
76+
77+
unittest::PiMock Mock1(Q1);
78+
unittest::PiMock Mock2(Q2);
79+
80+
Mock1.redefine<detail::PiApiKind::piEventsWait>(waitFunc);
81+
Mock1.redefine<detail::PiApiKind::piEventRetain>(retainReleaseFunc);
82+
Mock1.redefine<detail::PiApiKind::piEventRelease>(retainReleaseFunc);
83+
Mock1.redefine<detail::PiApiKind::piEventGetInfo>(getEventInfoFunc);
84+
85+
Mock2.redefine<detail::PiApiKind::piEventsWait>(waitFunc);
86+
Mock2.redefine<detail::PiApiKind::piEventRetain>(retainReleaseFunc);
87+
Mock2.redefine<detail::PiApiKind::piEventRelease>(retainReleaseFunc);
88+
Mock2.redefine<detail::PiApiKind::piEventGetInfo>(getEventInfoFunc);
89+
90+
TestContext.reset(new TestCtx(Q1, Q2));
91+
92+
std::shared_ptr<detail::event_impl> E1(
93+
new detail::event_impl(TestContext->EventCtx1, Q1.get_context()));
94+
std::shared_ptr<detail::event_impl> E2(
95+
new detail::event_impl(TestContext->EventCtx2, Q2.get_context()));
96+
97+
sycl::device HostDevice;
98+
std::shared_ptr<detail::queue_impl> DefaultHostQueue(new detail::queue_impl(
99+
detail::getSyclObjImpl(HostDevice), /*AsyncHandler=*/{},
100+
detail::QueueOrder::Ordered, /*PropList=*/{}));
101+
102+
MockCommand Cmd(DefaultHostQueue);
103+
104+
std::vector<std::shared_ptr<detail::event_impl>> Events;
105+
Events.push_back(E1);
106+
Events.push_back(E2);
107+
108+
pi_event EventResult = nullptr;
109+
110+
Cmd.waitForEventsCall(DefaultHostQueue, Events, EventResult);
111+
112+
ASSERT_TRUE(TestContext->EventCtx1WasWaited &&
113+
TestContext->EventCtx2WasWaited)
114+
<< "Not all events were waited for";
115+
}

sycl/unittests/scheduler/SchedulerTestUtils.hpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,13 @@ class MockCommand : public cl::sycl::detail::Command {
4040

4141
cl_int MRetVal = CL_SUCCESS;
4242

43+
void waitForEventsCall(
44+
std::shared_ptr<cl::sycl::detail::queue_impl> Queue,
45+
std::vector<std::shared_ptr<cl::sycl::detail::event_impl>> &RawEvents,
46+
pi_event &Event) {
47+
Command::waitForEvents(Queue, RawEvents, Event);
48+
}
49+
4350
protected:
4451
cl::sycl::detail::Requirement MRequirement;
4552
};

0 commit comments

Comments
 (0)