Skip to content

Commit bb423be

Browse files
author
Ivan Karachun
committed
[SYCL] Removed mutex leading to deadlock
The deadlock appeared under following circumstances: 1) thread1: adds nodes to the graph for host accessor A1 to the buffer B; 2) thread2: adds nodes to the graph for host accessor A2 to the buffer B; 3) thread2: waits for host accessor A2 nodes to complete; 4) thread1: waits for host accessor A1 nodes to complete. On step 3 thread2 locks a mutex in `Scheduler::waitForEvent` and waits for destruction of host accessor A1. Actions on step 4 cannot be completed because thread1 waits for the mutex to be unlocked. Signed-off-by: Ivan Karachun <[email protected]>
1 parent e6accbb commit bb423be

File tree

6 files changed

+148
-1
lines changed

6 files changed

+148
-1
lines changed

sycl/source/detail/scheduler/scheduler.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,6 @@ std::vector<EventImplPtr> Scheduler::getWaitList(EventImplPtr Event) {
109109
}
110110

111111
void Scheduler::waitForEvent(EventImplPtr Event) {
112-
std::lock_guard<std::mutex> lock(MGraphLock);
113112
GraphProcessor::waitForEvent(std::move(Event));
114113
}
115114

sycl/unittests/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,4 @@ endfunction()
3434

3535
add_subdirectory(pi)
3636
add_subdirectory(misc)
37+
add_subdirectory(thread_safety)
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
add_sycl_unittest(ThreadSafetyTests
2+
ThreadUtils.cpp
3+
HostAccessorDeadLock.cpp
4+
)
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
//==---- SchedulerThreadSafety.cpp --- Thread Safety 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 "ThreadUtils.h"
10+
#include <CL/sycl.hpp>
11+
#include <gtest/gtest.h>
12+
#include <mutex>
13+
#include <vector>
14+
15+
namespace {
16+
constexpr auto sycl_read_write = cl::sycl::access::mode::read_write;
17+
18+
template <typename T, int Dim> class TestDeadLock : public ParallelTask {
19+
public:
20+
TestDeadLock(T *Data, std::size_t Size)
21+
: MBuffer(Data, cl::sycl::range<Dim>(Size)), MBufferSize(Size) {}
22+
23+
void taskBody(size_t ThreadId) {
24+
auto acc = MBuffer.template get_access<sycl_read_write>();
25+
for (std::size_t i = 0; i < MBufferSize; ++i) {
26+
acc[i] = ThreadId;
27+
if (i == 0) {
28+
MMutex.lock();
29+
MThreadOrder.push_back(ThreadId);
30+
MMutex.unlock();
31+
}
32+
}
33+
}
34+
35+
std::size_t getLastWorkingThread() { return MThreadOrder.back(); }
36+
37+
private:
38+
std::vector<std::size_t> MThreadOrder;
39+
cl::sycl::buffer<T, Dim> MBuffer;
40+
std::size_t MBufferSize;
41+
std::mutex MMutex;
42+
};
43+
44+
class HostAccessorDeadLockTest : public ::testing::Test {};
45+
46+
TEST_F(HostAccessorDeadLockTest, CheckThreadOrder) {
47+
constexpr size_t size = 1024;
48+
constexpr size_t threadCount = 4;
49+
std::size_t data[size];
50+
TestDeadLock<std::size_t, 1> Task(data, size);
51+
Task.execute(threadCount);
52+
EXPECT_EQ(data[size - 1], Task.getLastWorkingThread());
53+
}
54+
} // namespace
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#include "ThreadUtils.h"
2+
#include <iostream>
3+
4+
static void *threadMainBody(Thread *thread, size_t id) {
5+
thread->body(id);
6+
return NULL;
7+
}
8+
9+
void Thread::start(size_t id) {
10+
MThread = std::move(std::thread(threadMainBody, this, id));
11+
}
12+
13+
void Thread::wait() { MThread.join(); }
14+
15+
void Thread::body(size_t id) { MTask->taskBody(id); }
16+
17+
ThreadPool::ThreadPool(ParallelTask *p) : MTask(p) {}
18+
19+
void ThreadPool::initialize(int size) {
20+
for (int i = 0; i < size; ++i) {
21+
MThreadPool.push_back(new Thread(MTask));
22+
}
23+
}
24+
25+
void ThreadPool::start() {
26+
for (std::size_t i = 0; i < MThreadPool.size(); ++i) {
27+
MThreadPool[i]->start(i);
28+
}
29+
}
30+
31+
void ThreadPool::wait() {
32+
for (auto it : MThreadPool) {
33+
it->wait();
34+
}
35+
}
36+
37+
void ParallelTask::execute(int threadCount) {
38+
try {
39+
MPool.initialize(threadCount);
40+
MPool.start();
41+
MPool.wait();
42+
} catch (std::exception &ex) {
43+
std::cerr << ex.what();
44+
} catch (...) {
45+
std::cerr << "Unknown exception";
46+
}
47+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
#include <thread>
2+
#include <vector>
3+
4+
class ParallelTask;
5+
6+
class Thread {
7+
public:
8+
Thread(ParallelTask *Ptr) : MTask(Ptr) {}
9+
void start(size_t id);
10+
void wait();
11+
void body(size_t id);
12+
13+
private:
14+
std::thread MThread;
15+
ParallelTask *MTask;
16+
};
17+
18+
class ThreadPool {
19+
public:
20+
ThreadPool(ParallelTask *p);
21+
void initialize(int size);
22+
void start();
23+
void wait();
24+
25+
private:
26+
std::vector<Thread *> MThreadPool;
27+
ParallelTask *MTask;
28+
};
29+
30+
class ParallelTask {
31+
friend class ThreadPool;
32+
33+
public:
34+
ParallelTask() : MPool(this) {}
35+
36+
void execute(int threadCount);
37+
38+
virtual void taskBody(std::size_t id) = 0;
39+
40+
private:
41+
ThreadPool MPool;
42+
};

0 commit comments

Comments
 (0)