Skip to content

[SYCL] Fix deadlock in Scheduler on Windows #1703

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
May 20, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 27 additions & 4 deletions sycl/source/detail/scheduler/scheduler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ EventImplPtr Scheduler::addCG(std::unique_ptr<detail::CG> CommandGroup,
Command *NewCmd = nullptr;
const bool IsKernel = CommandGroup->getType() == CG::KERNEL;
{
std::lock_guard<std::shared_timed_mutex> Lock(MGraphLock);
std::unique_lock<std::shared_timed_mutex> Lock(MGraphLock, std::defer_lock);
lockSharedTimedMutex(Lock);

switch (CommandGroup->getType()) {
case CG::UPDATE_HOST:
Expand Down Expand Up @@ -98,7 +99,8 @@ EventImplPtr Scheduler::addCG(std::unique_ptr<detail::CG> CommandGroup,
}

EventImplPtr Scheduler::addCopyBack(Requirement *Req) {
std::lock_guard<std::shared_timed_mutex> Lock(MGraphLock);
std::unique_lock<std::shared_timed_mutex> Lock(MGraphLock, std::defer_lock);
lockSharedTimedMutex(Lock);
Command *NewCmd = MGraphBuilder.addCopyBack(Req);
// Command was not creted because there were no operations with
// buffer.
Expand Down Expand Up @@ -154,7 +156,8 @@ void Scheduler::cleanupFinishedCommands(EventImplPtr FinishedEvent) {
}

void Scheduler::removeMemoryObject(detail::SYCLMemObjI *MemObj) {
std::lock_guard<std::shared_timed_mutex> Lock(MGraphLock);
std::unique_lock<std::shared_timed_mutex> Lock(MGraphLock, std::defer_lock);
lockSharedTimedMutex(Lock);

MemObjRecord *Record = MGraphBuilder.getMemObjRecord(MemObj);
if (!Record)
Expand All @@ -169,7 +172,8 @@ void Scheduler::removeMemoryObject(detail::SYCLMemObjI *MemObj) {

EventImplPtr Scheduler::addHostAccessor(Requirement *Req,
const bool destructor) {
std::lock_guard<std::shared_timed_mutex> Lock(MGraphLock);
std::unique_lock<std::shared_timed_mutex> Lock(MGraphLock, std::defer_lock);
lockSharedTimedMutex(Lock);

Command *NewCmd = MGraphBuilder.addHostAccessor(Req, destructor);

Expand Down Expand Up @@ -216,6 +220,25 @@ Scheduler::Scheduler() {
QueueOrder::Ordered, /*PropList=*/{}));
}

void Scheduler::lockSharedTimedMutex(
std::unique_lock<std::shared_timed_mutex> &Lock) {
#ifdef _WIN32
// Avoiding deadlock situation for MSVC. std::shared_timed_mutex specification
// does not specify a priority for shared and exclusive accesses. It will be a
// deadlock in MSVC's std::shared_timed_mutex implementation, if exclusive
// access occurs after shared access.
// TODO: after switching to C++17, change std::shared_timed_mutex to
// std::shared_mutex and use std::lock_guard here both for Windows and Linux.
while (!Lock.owns_lock()) {
Lock.try_lock();
}
#else
// It is a deadlock on UNIX in implementation of lock and lock_shared, if
// try_lock in the loop above will be executed, so using a single lock here
Lock.lock();
#endif // _WIN32
}

} // namespace detail
} // namespace sycl
} // __SYCL_INLINE_NAMESPACE(cl)
9 changes: 9 additions & 0 deletions sycl/source/detail/scheduler/scheduler.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,13 @@ class Scheduler {
Scheduler();
static Scheduler instance;

/// Provides exclusive access to std::shared_timed_mutex object with deadlock
/// avoidance
///
/// \param Lock is an instance of std::unique_lock<std::shared_timed_mutex>
/// class
void lockSharedTimedMutex(std::unique_lock<std::shared_timed_mutex> &Lock);

static void enqueueLeavesOfReqUnlocked(const Requirement *const Req);

/// Graph builder class.
Expand Down Expand Up @@ -687,6 +694,8 @@ class Scheduler {
void waitForRecordToFinish(MemObjRecord *Record);

GraphBuilder MGraphBuilder;
// TODO: after switching to C++17, change std::shared_timed_mutex to
// std::shared_mutex
std::shared_timed_mutex MGraphLock;

QueueImplPtr DefaultHostQueue;
Expand Down