Skip to content

Commit 3da5473

Browse files
[SYCL] Fix command cleanup invoked from multiple threads (#1214)
This patch fixes a sporadic bug where one thread attempted to clean up a command already deleted by another. Signed-off-by: Sergey Semenov <[email protected]>
1 parent b3a9426 commit 3da5473

File tree

5 files changed

+53
-8
lines changed

5 files changed

+53
-8
lines changed

sycl/source/detail/event_impl.cpp

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -95,16 +95,14 @@ event_impl::event_impl(QueueImplPtr Queue) : MQueue(Queue) {
9595

9696
void event_impl::wait(
9797
std::shared_ptr<cl::sycl::detail::event_impl> Self) const {
98-
9998
if (MEvent)
10099
// presence of MEvent means the command has been enqueued, so no need to
101100
// go via the slow path event waiting in the scheduler
102101
waitInternal();
103102
else if (MCommand)
104-
detail::Scheduler::getInstance().waitForEvent(std::move(Self));
103+
detail::Scheduler::getInstance().waitForEvent(Self);
105104
if (MCommand && !SYCLConfig<SYCL_DISABLE_EXECUTION_GRAPH_CLEANUP>::get())
106-
detail::Scheduler::getInstance().cleanupFinishedCommands(
107-
static_cast<Command *>(MCommand));
105+
detail::Scheduler::getInstance().cleanupFinishedCommands(std::move(Self));
108106
}
109107

110108
void event_impl::wait_and_throw(

sycl/source/detail/scheduler/scheduler.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,9 +123,13 @@ void Scheduler::waitForEvent(EventImplPtr Event) {
123123
GraphProcessor::waitForEvent(std::move(Event));
124124
}
125125

126-
void Scheduler::cleanupFinishedCommands(Command *FinishedCmd) {
126+
void Scheduler::cleanupFinishedCommands(EventImplPtr FinishedEvent) {
127127
std::lock_guard<std::mutex> lock(MGraphLock);
128-
MGraphBuilder.cleanupFinishedCommands(FinishedCmd);
128+
Command *FinishedCmd = static_cast<Command *>(FinishedEvent->getCommand());
129+
// The command might have been cleaned up (and set to nullptr) by another
130+
// thread
131+
if (FinishedCmd)
132+
MGraphBuilder.cleanupFinishedCommands(FinishedCmd);
129133
}
130134

131135
void Scheduler::removeMemoryObject(detail::SYCLMemObjI *MemObj) {

sycl/source/detail/scheduler/scheduler.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ class Scheduler {
7979

8080
// Removes finished non-leaf non-alloca commands from the subgraph (assuming
8181
// that all its commands have been waited for).
82-
void cleanupFinishedCommands(Command *FinishedCmd);
82+
void cleanupFinishedCommands(EventImplPtr FinishedEvent);
8383

8484
// Creates nodes in the graph, that update Req with the pointer to the host
8585
// memory which contains the latest data of the memory object. New
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// UNSUPPORTED: windows
2+
// RUN: %clangxx -fsycl %s -o %t.out -lpthread
3+
// RUN: %CPU_RUN_PLACEHOLDER %t.out
4+
#include <CL/sycl.hpp>
5+
6+
#include <cassert>
7+
#include <cstddef>
8+
#include <thread>
9+
#include <vector>
10+
11+
// This test checks that the command graph cleanup works properly when
12+
// invoked from multiple threads.
13+
using namespace cl::sycl;
14+
15+
class Foo;
16+
17+
event submitTask(queue &Q, buffer<int, 1> &Buf) {
18+
return Q.submit([&](handler &Cgh) {
19+
auto Acc = Buf.get_access<access::mode::read_write>(Cgh);
20+
Cgh.single_task<Foo>([=]() { Acc[0] = 42; });
21+
});
22+
}
23+
24+
int main() {
25+
queue Q;
26+
buffer<int, 1> Buf(range<1>(1));
27+
28+
// Create multiple commands, each one dependent on the previous
29+
std::vector<event> Events;
30+
const std::size_t NTasks = 16;
31+
for (std::size_t I = 0; I < NTasks; ++I)
32+
Events.push_back(submitTask(Q, Buf));
33+
34+
// Initiate cleanup from multiple threads
35+
std::vector<std::thread> Threads;
36+
for (event &E : Events)
37+
Threads.emplace_back([&]() { E.wait(); });
38+
for (std::thread &T : Threads)
39+
T.join();
40+
}

sycl/test/scheduler/FinishedCmdCleanup.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// RUN: %clangxx -fsycl -I %sycl_source_dir %s -o %t.out
22
// RUN: %t.out
33
#include <CL/sycl.hpp>
4+
#include <detail/event_impl.hpp>
45
#include <detail/scheduler/scheduler.hpp>
56

67
#include <algorithm>
@@ -76,7 +77,9 @@ int main() {
7677
addEdge(InnerA, &LeafA, &AllocaA);
7778
addEdge(InnerA, InnerB, &AllocaB);
7879

79-
TS.cleanupFinishedCommands(InnerA);
80+
std::shared_ptr<detail::event_impl> Event{new detail::event_impl{}};
81+
Event->setCommand(InnerA);
82+
TS.cleanupFinishedCommands(Event);
8083
TS.removeRecordForMemObj(detail::getSyclObjImpl(BufC).get());
8184

8285
assert(NInnerCommandsAlive == 0);

0 commit comments

Comments
 (0)