From f6a6290908dfcf4df6284c6d3eb94bb762e587fb Mon Sep 17 00:00:00 2001 From: Fangrui Song Date: Thu, 25 Apr 2019 11:33:30 +0000 Subject: [PATCH] Parallel: only allow the first TaskGroup to run tasks parallelly Summary: Concurrent (e.g. nested) llvm::parallel::for_each() may lead to dead locks. See PR35788 (fixed by rLLD322041) and PR41508 (fixed by D60757). When parallel_for_each() is about to return, in ~Latch() called by ~TaskGroup(), a thread (in the default executor) may block in Latch::sync() waiting for Count to become zero. If all threads in the default executor are blocked, it is a dead lock. To fix this, force serial execution if the current TaskGroup is not the first one. For a nested llvm::parallel::for_each(), this parallelizes the outermost loop and serializes inner loops. Differential Revision: https://reviews.llvm.org/D61115 llvm-svn: 359182 --- llvm/include/llvm/Support/Parallel.h | 4 ++++ llvm/lib/Support/Parallel.cpp | 31 +++++++++++++++++++++++++------ 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/llvm/include/llvm/Support/Parallel.h b/llvm/include/llvm/Support/Parallel.h index 9843b95..eab9b49 100644 --- a/llvm/include/llvm/Support/Parallel.h +++ b/llvm/include/llvm/Support/Parallel.h @@ -73,8 +73,12 @@ public: class TaskGroup { Latch L; + bool Parallel; public: + TaskGroup(); + ~TaskGroup(); + void spawn(std::function f); void sync() const { L.sync(); } diff --git a/llvm/lib/Support/Parallel.cpp b/llvm/lib/Support/Parallel.cpp index d40fce6..621bccb 100644 --- a/llvm/lib/Support/Parallel.cpp +++ b/llvm/lib/Support/Parallel.cpp @@ -17,7 +17,9 @@ #include #include -using namespace llvm; +namespace llvm { +namespace parallel { +namespace detail { namespace { @@ -118,11 +120,28 @@ Executor *Executor::getDefaultExecutor() { #endif } -void parallel::detail::TaskGroup::spawn(std::function F) { - L.inc(); - Executor::getDefaultExecutor()->add([&, F] { +static std::atomic TaskGroupInstances; + +// Latch::sync() called by the dtor may cause one thread to block. If is a dead +// lock if all threads in the default executor are blocked. To prevent the dead +// lock, only allow the first TaskGroup to run tasks parallelly. In the scenario +// of nested parallel_for_each(), only the outermost one runs parallelly. +TaskGroup::TaskGroup() : Parallel(TaskGroupInstances++ == 0) {} +TaskGroup::~TaskGroup() { --TaskGroupInstances; } + +void TaskGroup::spawn(std::function F) { + if (Parallel) { + L.inc(); + Executor::getDefaultExecutor()->add([&, F] { + F(); + L.dec(); + }); + } else { F(); - L.dec(); - }); + } } + +} // namespace detail +} // namespace parallel +} // namespace llvm #endif // LLVM_ENABLE_THREADS -- 2.7.4