2023-08-16 11:28:46 +02:00
|
|
|
#include "threadpool.h"
|
|
|
|
|
|
|
|
/*
|
2023-12-25 16:54:35 +01:00
|
|
|
* This is the idle loop each thread will be running until a job is ready
|
|
|
|
* for execution
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
global::ThreadPool::ThreadLoop()
|
2023-08-16 11:28:46 +02:00
|
|
|
{
|
2023-12-25 16:54:35 +01:00
|
|
|
while (true)
|
|
|
|
{
|
|
|
|
std::function<void()> job;
|
|
|
|
{
|
|
|
|
std::unique_lock<std::mutex> lock(this->queue_mutex);
|
2023-08-16 11:28:46 +02:00
|
|
|
|
2023-12-25 16:54:35 +01:00
|
|
|
/*
|
|
|
|
* This is equivalent to :
|
|
|
|
*
|
|
|
|
* while (!this->jobs.empty() || should_terminate)
|
|
|
|
* mutex_condition.wait(lock);
|
|
|
|
*
|
|
|
|
* we are essentially waiting for a job to be queued up or the terminate
|
|
|
|
*flag to be set. Another piece of useful information is that the predicate
|
|
|
|
*is checked under the lock as the precondition for .wait() is that the
|
|
|
|
*calling thread owns the lock.
|
|
|
|
*
|
|
|
|
* Now, when .wait() is run, the lock is unlocked the the executing thread
|
|
|
|
*is blocked and is added to a list of threads current waiting on the
|
|
|
|
*predicate. In our case whether there are new jobs available for the
|
|
|
|
*terminate flag is set. Once the condition variables are true i.e there are
|
|
|
|
*new jobs or we are terminating, the lock is reacquired by the thread and
|
|
|
|
*the thread is unblocked.
|
|
|
|
*/
|
|
|
|
mutex_condition.wait(
|
|
|
|
lock, [this] { return !this->jobs.empty() || this->should_terminate; });
|
2023-08-16 11:28:46 +02:00
|
|
|
|
2023-12-25 16:54:35 +01:00
|
|
|
if (this->should_terminate)
|
|
|
|
return;
|
2023-08-16 11:28:46 +02:00
|
|
|
|
2023-12-25 16:54:35 +01:00
|
|
|
/* get the first job in the queue*/
|
|
|
|
job = jobs.front();
|
|
|
|
jobs.pop();
|
|
|
|
}
|
|
|
|
/* run the job */
|
|
|
|
job();
|
|
|
|
}
|
2023-08-16 11:28:46 +02:00
|
|
|
}
|
|
|
|
|
2023-08-17 10:45:50 +02:00
|
|
|
global::ThreadPool::ThreadPool(int ThreadCount)
|
2023-08-16 11:28:46 +02:00
|
|
|
{
|
2023-12-25 16:54:35 +01:00
|
|
|
this->thread_count = ThreadCount;
|
|
|
|
this->should_terminate = false;
|
2023-08-16 11:28:46 +02:00
|
|
|
|
2023-12-25 16:54:35 +01:00
|
|
|
/* Initiate our threads and store them in our threads vector */
|
|
|
|
for (int i = 0; i < this->thread_count; i++)
|
|
|
|
{
|
|
|
|
this->threads.emplace_back(std::thread(&ThreadPool::ThreadLoop, this));
|
|
|
|
}
|
2023-08-16 11:28:46 +02:00
|
|
|
}
|
|
|
|
|
2023-12-25 16:54:35 +01:00
|
|
|
void
|
|
|
|
global::ThreadPool::QueueJob(const std::function<void()>& job)
|
2023-08-16 11:28:46 +02:00
|
|
|
{
|
2023-12-25 16:54:35 +01:00
|
|
|
/* push a job into our job queue safely by holding our queue lock */
|
|
|
|
std::unique_lock<std::mutex> lock(this->queue_mutex);
|
2023-08-17 14:44:28 +02:00
|
|
|
|
2023-12-25 16:54:35 +01:00
|
|
|
this->jobs.push(job);
|
|
|
|
lock.unlock();
|
2023-08-17 14:44:28 +02:00
|
|
|
|
2023-12-25 16:54:35 +01:00
|
|
|
mutex_condition.notify_one();
|
2023-08-16 11:28:46 +02:00
|
|
|
}
|
|
|
|
|
2023-12-25 16:54:35 +01:00
|
|
|
void
|
|
|
|
global::ThreadPool::Stop()
|
2023-08-16 11:28:46 +02:00
|
|
|
{
|
2023-12-25 16:54:35 +01:00
|
|
|
/* safely set our termination flag to true */
|
|
|
|
std::unique_lock<std::mutex> lock(this->queue_mutex);
|
|
|
|
should_terminate = true;
|
|
|
|
lock.unlock();
|
2023-08-17 14:44:28 +02:00
|
|
|
|
2023-12-25 16:54:35 +01:00
|
|
|
/* unlock all threads waiting on our condition */
|
|
|
|
mutex_condition.notify_all();
|
2023-08-17 14:44:28 +02:00
|
|
|
|
2023-12-25 16:54:35 +01:00
|
|
|
/* join the threads and clear our threads vector */
|
|
|
|
for (std::thread& thread : threads)
|
|
|
|
{
|
|
|
|
thread.join();
|
|
|
|
}
|
|
|
|
threads.clear();
|
2023-08-16 11:28:46 +02:00
|
|
|
}
|
|
|
|
|
2023-12-25 16:54:35 +01:00
|
|
|
bool
|
|
|
|
global::ThreadPool::Busy()
|
2023-08-16 11:28:46 +02:00
|
|
|
{
|
2023-12-25 16:54:35 +01:00
|
|
|
/* allows us to wait for when the job queue is empty allowing us to safely call the
|
|
|
|
* destructor */
|
|
|
|
std::unique_lock<std::mutex> lock(this->queue_mutex);
|
2023-08-17 14:44:28 +02:00
|
|
|
|
2023-12-25 16:54:35 +01:00
|
|
|
bool pool_busy = !jobs.empty();
|
|
|
|
this->queue_mutex.unlock();
|
2023-08-17 14:44:28 +02:00
|
|
|
|
2023-12-25 16:54:35 +01:00
|
|
|
return pool_busy;
|
2023-08-16 11:28:46 +02:00
|
|
|
}
|