Add experimental sys::threadpool.

This commit is contained in:
J-D-K 2025-09-11 19:24:01 -04:00
parent 773acbabc8
commit 27f3ea2e49
4 changed files with 118 additions and 1 deletions

View File

@ -3,3 +3,4 @@
#include "sys/Task.hpp"
#include "sys/Timer.hpp"
#include "sys/defines.hpp"
#include "sys/threadpool.hpp"

View File

@ -0,0 +1,26 @@
#pragma once
#include <functional>
#include <memory>
namespace sys::threadpool
{
// clang-format off
struct DataStruct{};
// clang-format on
/// @brief Makes things easier to read and type later.
using JobData = std::shared_ptr<sys::threadpool::DataStruct>;
/// @brief Function definition.
using JobFunction = std::function<void(JobData)>;
/// @brief Initializes and starts the threads.
void initialize();
/// @brief Signals the threads to terminate and closes them.
void exit();
/// @brief Pushes a job to the queue.
void push_job(sys::threadpool::JobFunction function, sys::threadpool::JobData);
}

View File

@ -16,7 +16,7 @@
#include "sdl.hpp"
#include "strings/strings.hpp"
#include "stringutil.hpp"
#include "sys/OpTimer.hpp"
#include "sys/sys.hpp"
#include "ui/PopMessageManager.hpp"
#include <chrono>
@ -88,6 +88,8 @@ JKSV::JKSV()
// This needs the config init'd or read to work.
JKSV::create_directories();
sys::threadpool::initialize();
data::launch_initialization(false, finish_initialization);
m_isRunning = true;
@ -95,6 +97,7 @@ JKSV::JKSV()
JKSV::~JKSV()
{
sys::threadpool::exit();
config::save();
curl::exit();
JKSV::exit_services();

87
source/sys/threadpool.cpp Normal file
View File

@ -0,0 +1,87 @@
#include "sys/threadpool.hpp"
#include "error.hpp"
#include <array>
#include <atomic>
#include <condition_variable>
#include <mutex>
#include <queue>
#include <switch.h>
namespace
{
/// @brief Number of threads to pool.
constexpr size_t COUNT_THREADS = 2;
/// @brief Size of the stack for the threads.
constexpr size_t SIZE_THREAD_STACK = 0x40000;
/// @brief Another time/hand saver.
using QueuePair = std::pair<sys::threadpool::JobFunction, sys::threadpool::JobData>;
/// @brief Array of threads.
constinit std::array<Thread, COUNT_THREADS> s_threads{};
/// @brief The queue of jobs.
std::queue<QueuePair> s_jobQueue{};
/// @brief Mutex to prevent job corruption.
std::mutex s_jobMutex{};
/// @brief Conditional.
std::condition_variable s_jobCondition{};
/// @brief So exit can signal.
std::atomic_bool s_exitFlag{};
}
/// @brief Defined at bottom.
static void thread_pool_function(void *);
void sys::threadpool::initialize()
{
for (size_t i = 0; i < COUNT_THREADS; i++)
{
// NOTE: If pool size increases, i + 1 isn't going to work anymore.
error::libnx(threadCreate(&s_threads[i], thread_pool_function, nullptr, nullptr, SIZE_THREAD_STACK, 0x2B, i + 1));
error::libnx(threadStart(&s_threads[i]));
}
}
void sys::threadpool::exit()
{
s_exitFlag.store(true);
s_jobCondition.notify_all();
for (size_t i = 0; i < COUNT_THREADS; i++)
{
error::libnx(threadWaitForExit(&s_threads[i]));
error::libnx(threadClose(&s_threads[i]));
}
}
void sys::threadpool::push_job(sys::threadpool::JobFunction function, sys::threadpool::JobData data)
{
std::lock_guard jobGuard{s_jobMutex};
s_jobQueue.push(std::make_pair(function, data));
s_jobCondition.notify_all();
}
static void thread_pool_function(void *)
{
auto condition = []() { return s_exitFlag.load() || !s_jobQueue.empty(); };
while (true)
{
std::unique_lock jobGuard{s_jobMutex};
s_jobCondition.wait(jobGuard, condition);
if (s_exitFlag.load()) { break; }
auto [function, data] = s_jobQueue.front();
s_jobQueue.pop();
jobGuard.unlock();
function(data);
}
}