Skip to content

Commit 28ada78

Browse files
authored
[SYCL] Fix event destruction (#5697)
The fix 34a4689 introduced problem with event::get_wait_list(). If a command has only dependencies introduced by user through "handler::depends_on()", these dependencies will be removed with the command. Proposed solution suggests not to remove direct dependencies of this event, but dependencies of the dependencies of the event when a command is released. Signed-off-by: mdimakov <[email protected]>
1 parent 53a9d54 commit 28ada78

File tree

7 files changed

+227
-27
lines changed

7 files changed

+227
-27
lines changed

sycl/source/detail/event_impl.cpp

+10
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,16 @@ void event_impl::cleanupDependencyEvents() {
411411
MPreparedHostDepsEvents.clear();
412412
}
413413

414+
void event_impl::cleanDepEventsThroughOneLevel() {
415+
std::lock_guard<std::mutex> Lock(MMutex);
416+
for (auto &Event : MPreparedDepsEvents) {
417+
Event->cleanupDependencyEvents();
418+
}
419+
for (auto &Event : MPreparedHostDepsEvents) {
420+
Event->cleanupDependencyEvents();
421+
}
422+
}
423+
414424
} // namespace detail
415425
} // namespace sycl
416426
} // __SYCL_INLINE_NAMESPACE(cl)

sycl/source/detail/event_impl.hpp

+4-1
Original file line numberDiff line numberDiff line change
@@ -193,9 +193,12 @@ class event_impl {
193193
/// to the device yet.
194194
void flushIfNeeded(const QueueImplPtr &UserQueue);
195195

196-
/// Cleans dependencies of this event_impl
196+
/// Cleans dependencies of this event_impl.
197197
void cleanupDependencyEvents();
198198

199+
/// Cleans dependencies of this event's dependencies.
200+
void cleanDepEventsThroughOneLevel();
201+
199202
/// Checks if this event is discarded by SYCL implementation.
200203
///
201204
/// \return true if this event is discarded.

sycl/source/detail/scheduler/commands.hpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ class Command {
189189
return nullptr;
190190
}
191191

192-
virtual ~Command() { MEvent->cleanupDependencyEvents(); }
192+
virtual ~Command() { MEvent->cleanDepEventsThroughOneLevel(); }
193193

194194
const char *getBlockReason() const;
195195

sycl/unittests/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,4 @@ add_subdirectory(program_manager)
3333
add_subdirectory(assert)
3434
add_subdirectory(Extensions)
3535
add_subdirectory(windows)
36+
add_subdirectory(event)

sycl/unittests/event/CMakeLists.txt

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
add_sycl_unittest(EventTests OBJECT
2+
EventDestruction.cpp
3+
)
+208
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
//==------- EventDestruction.cpp --- Check correct event destruction -------==//
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 <helpers/CommonRedefinitions.hpp>
10+
#include <helpers/PiMock.hpp>
11+
#include <helpers/TestKernel.hpp>
12+
13+
#include <gtest/gtest.h>
14+
15+
#include <iostream>
16+
17+
using namespace sycl;
18+
19+
static int ReleaseCounter = 0;
20+
static pi_result redefinedEventRelease(pi_event event) {
21+
++ReleaseCounter;
22+
return PI_SUCCESS;
23+
}
24+
25+
pi_result redefinedMemBufferCreate(pi_context, pi_mem_flags, size_t size,
26+
void *, pi_mem *,
27+
const pi_mem_properties *) {
28+
return PI_SUCCESS;
29+
}
30+
31+
class EventDestructionTest : public ::testing::Test {
32+
public:
33+
EventDestructionTest() : Plt{default_selector()} {}
34+
35+
protected:
36+
void SetUp() override {
37+
if (Plt.is_host() || Plt.get_backend() != backend::opencl) {
38+
std::cout << "This test is only supported on OpenCL backend\n";
39+
std::cout << "Current platform is "
40+
<< Plt.get_info<sycl::info::platform::name>();
41+
return;
42+
}
43+
44+
Mock = std::make_unique<unittest::PiMock>(Plt);
45+
46+
setupDefaultMockAPIs(*Mock);
47+
Mock->redefine<detail::PiApiKind::piEventRelease>(redefinedEventRelease);
48+
Mock->redefine<sycl::detail::PiApiKind::piMemBufferCreate>(
49+
redefinedMemBufferCreate);
50+
}
51+
52+
protected:
53+
std::unique_ptr<unittest::PiMock> Mock;
54+
platform Plt;
55+
};
56+
57+
// Test that events are destructed in correct time
58+
TEST_F(EventDestructionTest, EventDestruction) {
59+
if (Plt.is_host() || Plt.get_backend() != backend::opencl) {
60+
return;
61+
}
62+
sycl::context Context{Plt};
63+
sycl::queue Queue{Context, sycl::default_selector{}};
64+
65+
{
66+
ReleaseCounter = 0;
67+
sycl::event E1{};
68+
69+
{
70+
sycl::event E0 = Queue.submit([&](cl::sycl::handler &cgh) {
71+
cgh.single_task<TestKernel>([]() {});
72+
});
73+
E1 = Queue.submit([&](cl::sycl::handler &cgh) {
74+
cgh.depends_on(E0);
75+
cgh.single_task<TestKernel>([]() {});
76+
});
77+
E1.wait();
78+
}
79+
// When a command is cleared we clear now only dependencies of the
80+
// dependencies of the associated event. So, when the command
81+
// associated with E0 event is destroyed, this event is still in
82+
// E1 dependencies, which will not be cleared.
83+
// Therefore no event release should be called until here.
84+
EXPECT_EQ(ReleaseCounter, 0);
85+
86+
sycl::event E2 = Queue.submit([&](cl::sycl::handler &cgh) {
87+
cgh.depends_on(E1);
88+
cgh.single_task<TestKernel>([]() {});
89+
});
90+
E2.wait();
91+
// Dependencies of E1 should be cleared here. It depends on E0.
92+
EXPECT_EQ(ReleaseCounter, 1);
93+
94+
sycl::event E3 = Queue.submit([&](cl::sycl::handler &cgh) {
95+
cgh.depends_on({E1, E2});
96+
cgh.single_task<TestKernel>([]() {});
97+
});
98+
E3.wait();
99+
// Dependency of E1 has already cleared. E2 depends on E1 that
100+
// can't be cleared yet.
101+
EXPECT_EQ(ReleaseCounter, 1);
102+
}
103+
104+
{
105+
ReleaseCounter = 0;
106+
int data[2] = {0, 1};
107+
sycl::buffer<int, 1> Buf(&data[0], sycl::range<1>(2));
108+
Queue.submit([&](cl::sycl::handler &cgh) {
109+
auto Acc = Buf.get_access<sycl::access::mode::read_write>(cgh);
110+
cgh.single_task<TestKernel>([=]() {});
111+
});
112+
113+
Queue.submit([&](cl::sycl::handler &cgh) {
114+
auto Acc = Buf.get_access<sycl::access::mode::read_write>(cgh);
115+
cgh.single_task<TestKernel>([=]() {});
116+
});
117+
sycl::event E1 = Queue.submit([&](cl::sycl::handler &cgh) {
118+
auto Acc = Buf.get_access<sycl::access::mode::read_write>(cgh);
119+
cgh.single_task<TestKernel>([=]() {});
120+
});
121+
sycl::event E2 = Queue.submit([&](cl::sycl::handler &cgh) {
122+
auto Acc = Buf.get_access<sycl::access::mode::read_write>(cgh);
123+
cgh.single_task<TestKernel>([=]() {});
124+
});
125+
E2.wait();
126+
// Dependencies are deleted through one level of dependencies. When
127+
// fourth command group is submitted the destructor of third command
128+
// is called. It depends on second command, so dependencies of second
129+
// command will be cleared. It leads to release event associated with
130+
// first command
131+
EXPECT_EQ(ReleaseCounter, 1);
132+
}
133+
}
134+
135+
// Test for event::get_wait_list
136+
TEST_F(EventDestructionTest, GetWaitList) {
137+
if (Plt.is_host() || Plt.get_backend() != backend::opencl) {
138+
return;
139+
}
140+
ReleaseCounter = 0;
141+
sycl::context Context{Plt};
142+
sycl::queue Queue{Context, sycl::default_selector{}};
143+
// Test for get_wait_list with host_task
144+
{
145+
sycl::event eA =
146+
Queue.submit([&](sycl::handler &cgh) { cgh.host_task([]() {}); });
147+
sycl::event eB = Queue.submit([&](sycl::handler &cgh) {
148+
cgh.depends_on(eA);
149+
cgh.host_task([]() {});
150+
});
151+
152+
auto wait_list = eB.get_wait_list();
153+
ASSERT_EQ(wait_list.size(), (size_t)1);
154+
ASSERT_EQ(wait_list[0], eA);
155+
156+
sycl::event eC = Queue.submit([&](sycl::handler &cgh) {
157+
cgh.depends_on({eA, eB});
158+
cgh.host_task([]() {});
159+
});
160+
161+
wait_list = eC.get_wait_list();
162+
ASSERT_EQ(wait_list.size(), (size_t)2);
163+
ASSERT_EQ(wait_list[0], eA);
164+
ASSERT_EQ(wait_list[1], eB);
165+
166+
eC.wait();
167+
}
168+
169+
// Test for get_wait_list with single_task
170+
{
171+
sycl::event E1{};
172+
173+
{
174+
sycl::event E0 = Queue.submit([&](cl::sycl::handler &cgh) {
175+
cgh.single_task<TestKernel>([]() {});
176+
});
177+
E1 = Queue.submit([&](cl::sycl::handler &cgh) {
178+
cgh.depends_on(E0);
179+
cgh.single_task<TestKernel>([]() {});
180+
});
181+
E1.wait();
182+
auto wait_list = E1.get_wait_list();
183+
ASSERT_EQ(wait_list.size(), (size_t)1);
184+
ASSERT_EQ(wait_list[0], E0);
185+
}
186+
187+
auto wait_list = E1.get_wait_list();
188+
ASSERT_EQ(wait_list.size(), (size_t)1);
189+
EXPECT_EQ(ReleaseCounter, 0);
190+
191+
sycl::event E2 = Queue.submit([&](cl::sycl::handler &cgh) {
192+
cgh.depends_on(E1);
193+
cgh.single_task<TestKernel>([]() {});
194+
});
195+
E2.wait();
196+
197+
sycl::event E3 = Queue.submit([&](cl::sycl::handler &cgh) {
198+
cgh.depends_on({E1, E2});
199+
cgh.single_task<TestKernel>([]() {});
200+
});
201+
E3.wait();
202+
203+
wait_list = E3.get_wait_list();
204+
ASSERT_EQ(wait_list.size(), (size_t)2);
205+
ASSERT_EQ(wait_list[0], E1);
206+
ASSERT_EQ(wait_list[1], E2);
207+
}
208+
}

sycl/unittests/queue/Wait.cpp

-25
Original file line numberDiff line numberDiff line change
@@ -177,31 +177,6 @@ TEST(QueueWait, QueueWaitTest) {
177177
ASSERT_TRUE(TestContext.PiQueueFinishCalled);
178178
}
179179

180-
// Test for event::get_wait_list
181-
{
182-
sycl::event eA =
183-
Q.submit([&](sycl::handler &cgh) { cgh.host_task([]() {}); });
184-
sycl::event eB = Q.submit([&](sycl::handler &cgh) {
185-
cgh.depends_on(eA);
186-
cgh.host_task([]() {});
187-
});
188-
189-
auto res = eB.get_wait_list();
190-
assert(res.size() == 1);
191-
ASSERT_EQ(res[0], eA);
192-
193-
sycl::event eC = Q.submit([&](sycl::handler &cgh) {
194-
cgh.depends_on({eA, eB});
195-
cgh.host_task([]() {});
196-
});
197-
198-
res = eC.get_wait_list();
199-
assert(res.size() == 2);
200-
ASSERT_EQ(res[0], eA);
201-
ASSERT_EQ(res[1], eB);
202-
203-
eC.wait();
204-
}
205180
// Test behaviour for emulating an OOO queue with multiple in-order ones.
206181
TestContext = {};
207182
TestContext.SupportOOO = false;

0 commit comments

Comments
 (0)