From c70e8daa91fdcb17791f1c2b6e4119b1ab8074ab Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 22 Dec 2017 11:27:52 +0000 Subject: [PATCH] threadpool: fix deadlock in recursive waiter usage If a queued job uses a waiter, then we want to run that waiter's jobs in the current thread if all threads are busy, even if the queue is empty, since there is no guarantee that any thread will free up to take care of that new job, since all the threads might be running a job which spawns such a recursive job and will block till that recursive job is done, which it will never be since it relies on the queue being polled by one of those blocked threads. --- src/common/threadpool.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/common/threadpool.cpp b/src/common/threadpool.cpp index 20c5765b0..5d749e08e 100644 --- a/src/common/threadpool.cpp +++ b/src/common/threadpool.cpp @@ -34,6 +34,8 @@ #include "cryptonote_config.h" #include "common/util.h" +static __thread int depth = 0; + namespace tools { threadpool::threadpool() : running(true), active(0) { @@ -60,11 +62,13 @@ threadpool::~threadpool() { void threadpool::submit(waiter *obj, std::function f) { entry e = {obj, f}; boost::unique_lock lock(mutex); - if (active == max && !queue.empty()) { + if ((active == max && !queue.empty()) || depth > 0) { // if all available threads are already running // and there's work waiting, just run in current thread lock.unlock(); + ++depth; f(); + --depth; } else { if (obj) obj->inc(); @@ -106,7 +110,9 @@ void threadpool::run() { e = queue.front(); queue.pop_front(); lock.unlock(); + ++depth; e.f(); + --depth; if (e.wo) e.wo->dec();