From e5a4e57dbcbe3db8bab6d5dc23ed3566da661c76 Mon Sep 17 00:00:00 2001 From: WSAL Evan Date: Wed, 3 Dec 2025 09:40:45 -0500 Subject: [PATCH] Better thread pool --- include/wv/threading/ThreadPool.h | 26 +++++++---- src/threading/ThreadPool.cpp | 75 +++++++++++++++++-------------- 2 files changed, 59 insertions(+), 42 deletions(-) diff --git a/include/wv/threading/ThreadPool.h b/include/wv/threading/ThreadPool.h index 92352ac..80991bf 100644 --- a/include/wv/threading/ThreadPool.h +++ b/include/wv/threading/ThreadPool.h @@ -1,27 +1,37 @@ #pragma once #include +#include #include #include +#include namespace WillowVox { + enum class Priority { + High = 0, + Medium = 1, + Low = 2, + Count = 3 + }; + class ThreadPool { public: - ThreadPool(int initialJobQueueCapacity); - ThreadPool() = default; + ThreadPool(); + ~ThreadPool(); void Start(int numThreads); - void QueueJob(const std::function& job, bool highPriority = false); - void Stop(); + void Enqueue(const std::function& job, Priority priority = Priority::Medium); private: void ThreadLoop(); - std::atomic m_shouldTerminate; - std::condition_variable m_mutexCondition; + // Queues for each priority + std::array>, static_cast(Priority::Count)> m_queues; + // Queue to wake up threads when a job is queued + moodycamel::BlockingConcurrentQueue m_signal; + std::vector m_threads; - moodycamel::ConcurrentQueue> m_lowPriorityJobs; - moodycamel::ConcurrentQueue> m_highPriorityJobs; + std::atomic m_shouldTerminate; }; } \ No newline at end of file diff --git a/src/threading/ThreadPool.cpp b/src/threading/ThreadPool.cpp index 2beb757..5a8b249 100644 --- a/src/threading/ThreadPool.cpp +++ b/src/threading/ThreadPool.cpp @@ -4,8 +4,23 @@ namespace WillowVox { - ThreadPool::ThreadPool(int initialJobQueueCapacity = 0) - : m_shouldTerminate(false), m_highPriorityJobs(initialJobQueueCapacity), m_lowPriorityJobs(initialJobQueueCapacity) {} + ThreadPool::ThreadPool() + : m_shouldTerminate(false) {} + + ThreadPool::~ThreadPool() + { + m_shouldTerminate = true; + + // Make sure all threads stop + for (int i = 0; i < m_threads.size() * 2; i++) + m_signal.enqueue(true); + + // Join all threads + for (std::thread& activeThread : m_threads) + activeThread.join(); + + m_threads.clear(); + } void ThreadPool::Start(int numThreads) { @@ -13,51 +28,43 @@ namespace WillowVox m_threads.emplace_back(std::thread(&ThreadPool::ThreadLoop, this)); } - void ThreadPool::QueueJob(const std::function& job, bool highPriority) + void ThreadPool::Enqueue(const std::function& job, Priority priority) { - { - if (highPriority) - m_highPriorityJobs.enqueue(job); - else - m_lowPriorityJobs.enqueue(job); - } - //m_mutexCondition.notify_one(); - } - - void ThreadPool::Stop() - { - m_shouldTerminate = true; - - m_mutexCondition.notify_all(); - for (std::thread& activeThread : m_threads) - activeThread.join(); - - m_threads.clear(); + // Enqueue the job + m_queues[static_cast(priority)].enqueue(job); + // Wake up a worker thread to run the job + m_signal.enqueue(true); } void ThreadPool::ThreadLoop() { + bool token; while (true) { - std::this_thread::sleep_for(std::chrono::milliseconds(5)); + // Wait for job to be enqueued + m_signal.wait_dequeue(token); + Logger::Log("Worker thread %d checking for jobs", std::this_thread::get_id()); + // Stop early if necessary + if (m_shouldTerminate) + return; + + // Get job to run std::function job; - { - /*m_mutexCondition.wait(lock, [this] { - return !m_highPriorityJobs.() || !m_lowPriorityJobs.empty() || m_shouldTerminate; - });*/ - if (m_shouldTerminate) - return; + bool found = false; - bool jobFound = m_highPriorityJobs.try_dequeue(job); - if (!jobFound) + for (int i = 0; i < static_cast(Priority::Count); i++) + { + if (m_queues[static_cast(Priority::High)].try_dequeue(job)) { - jobFound = m_lowPriorityJobs.try_dequeue(job); - if (!jobFound) - continue; + found = true; + break; } } - job(); + + // Run job if found + if (found && job) + job(); } } } \ No newline at end of file