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.
This commit is contained in:
moneromooo-monero 2017-12-22 11:27:52 +00:00
parent 7a9a4a6669
commit c70e8daa91
No known key found for this signature in database
GPG Key ID: 686F07454D6CEFC3
1 changed files with 7 additions and 1 deletions

View File

@ -34,6 +34,8 @@
#include "cryptonote_config.h" #include "cryptonote_config.h"
#include "common/util.h" #include "common/util.h"
static __thread int depth = 0;
namespace tools namespace tools
{ {
threadpool::threadpool() : running(true), active(0) { threadpool::threadpool() : running(true), active(0) {
@ -60,11 +62,13 @@ threadpool::~threadpool() {
void threadpool::submit(waiter *obj, std::function<void()> f) { void threadpool::submit(waiter *obj, std::function<void()> f) {
entry e = {obj, f}; entry e = {obj, f};
boost::unique_lock<boost::mutex> lock(mutex); boost::unique_lock<boost::mutex> lock(mutex);
if (active == max && !queue.empty()) { if ((active == max && !queue.empty()) || depth > 0) {
// if all available threads are already running // if all available threads are already running
// and there's work waiting, just run in current thread // and there's work waiting, just run in current thread
lock.unlock(); lock.unlock();
++depth;
f(); f();
--depth;
} else { } else {
if (obj) if (obj)
obj->inc(); obj->inc();
@ -106,7 +110,9 @@ void threadpool::run() {
e = queue.front(); e = queue.front();
queue.pop_front(); queue.pop_front();
lock.unlock(); lock.unlock();
++depth;
e.f(); e.f();
--depth;
if (e.wo) if (e.wo)
e.wo->dec(); e.wo->dec();