diff --git a/libs/MyThreadPool/include/MyThreadPool.h b/libs/MyThreadPool/include/MyThreadPool.h index 36d7705..cc2732a 100644 --- a/libs/MyThreadPool/include/MyThreadPool.h +++ b/libs/MyThreadPool/include/MyThreadPool.h @@ -119,9 +119,13 @@ private: return; } - auto callable = std::move(queue.pop()); - if (callable.has_value()) { + // Prevent possible race condition in isIdle(). + // Perhaps can be reworked, e. g. by having each worker track its progress individually? + auto callable = std::move(queue.pop([&] { threadsWorking++; + })); + + if (callable.has_value()) { callable.value()(); threadsWorking--; poolCond.notify_all(); diff --git a/libs/MyThreadPool/include/SafePriorityQueue.h b/libs/MyThreadPool/include/SafePriorityQueue.h index 1bf967f..8a2d8ce 100644 --- a/libs/MyThreadPool/include/SafePriorityQueue.h +++ b/libs/MyThreadPool/include/SafePriorityQueue.h @@ -17,6 +17,13 @@ template class SafePriorityQueue { public: + /* + * Default value for functor-taking methods. + */ + struct no_op { + constexpr void operator()() const { } + }; + void push(int priority, T& obj) { push(priority, std::forward(obj)); @@ -41,7 +48,15 @@ public: queueLength++; } - std::optional pop() + /** + * Pop (and return) the top-most item in the stack. + * @tparam F + * @param callback Will be executed after (and only after) an element is popped off the queue, + * but before the mutex is released -- good for count-keeping. + * @return If the queue is empty, std::nullopt is returned. + */ + template + std::optional pop(F&& callback = no_op()) { std::unique_lock lock(mtx); @@ -51,10 +66,12 @@ public: queueLength--; - auto ret = std::move(queue.front()); + auto ret = std::move(queue.front().second); queue.pop_front(); - return ret.second; + callback(); + + return ret; } std::optional peek() const