diff options
author | Fangrui Song <maskray@google.com> | 2019-04-25 11:33:30 +0000 |
---|---|---|
committer | Fangrui Song <maskray@google.com> | 2019-04-25 11:33:30 +0000 |
commit | f6a6290908dfcf4df6284c6d3eb94bb762e587fb (patch) | |
tree | d219644bd02a6fd4813bb02f415d5e0d728948df | |
parent | 9e8d0d981b0cb1e3cbd0260af4e3b1f44ddf7be2 (diff) | |
download | bcm5719-llvm-f6a6290908dfcf4df6284c6d3eb94bb762e587fb.tar.gz bcm5719-llvm-f6a6290908dfcf4df6284c6d3eb94bb762e587fb.zip |
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
-rw-r--r-- | llvm/include/llvm/Support/Parallel.h | 4 | ||||
-rw-r--r-- | 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 9843b9518cd..eab9b492c4a 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<void()> f); void sync() const { L.sync(); } diff --git a/llvm/lib/Support/Parallel.cpp b/llvm/lib/Support/Parallel.cpp index d40fce67e9a..621bccbf2a4 100644 --- a/llvm/lib/Support/Parallel.cpp +++ b/llvm/lib/Support/Parallel.cpp @@ -17,7 +17,9 @@ #include <stack> #include <thread> -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<void()> F) { - L.inc(); - Executor::getDefaultExecutor()->add([&, F] { +static std::atomic<int> 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<void()> 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 |