diff options
Diffstat (limited to 'src/buildtool/multithreading/task_system.cpp')
-rw-r--r-- | src/buildtool/multithreading/task_system.cpp | 56 |
1 files changed, 56 insertions, 0 deletions
diff --git a/src/buildtool/multithreading/task_system.cpp b/src/buildtool/multithreading/task_system.cpp new file mode 100644 index 00000000..8c976a2f --- /dev/null +++ b/src/buildtool/multithreading/task_system.cpp @@ -0,0 +1,56 @@ +#include "src/buildtool/multithreading/task_system.hpp" + +#include "gsl-lite/gsl-lite.hpp" +#include "src/buildtool/multithreading/task.hpp" + +TaskSystem::TaskSystem() : TaskSystem(std::thread::hardware_concurrency()) {} + +TaskSystem::TaskSystem(std::size_t number_of_threads) + : thread_count_{std::max(1UL, number_of_threads)}, + num_threads_running_{thread_count_} { + for (std::size_t index = 0; index < thread_count_; ++index) { + queues_.emplace_back(&queues_read_, &num_threads_running_); + } + for (std::size_t index = 0; index < thread_count_; ++index) { + threads_.emplace_back([&, index]() { Run(index); }); + } +} + +TaskSystem::~TaskSystem() { + // When starting a new task system all spawned threads will immediately go + // to sleep and wait for tasks. Even after adding some tasks, it can take a + // while until the first thread wakes up. Therefore, we first need to wait + // for the queues being read, before we can wait for all threads to become + // idle. + queues_read_.WaitForSet(); + num_threads_running_.WaitForZero(); + for (auto& q : queues_) { + q.done(); + } + for (auto& t : threads_) { + t.join(); + } +} + +void TaskSystem::Run(std::size_t idx) { + gsl_Expects(thread_count_ > 0); + + while (true) { + std::optional<Task> t{}; + for (std::size_t i = 0; i < thread_count_; ++i) { + t = queues_[(idx + i) % thread_count_].try_pop(); + if (t) { + break; + } + } + + // NOLINTNEXTLINE(clang-analyzer-core.DivideZero) + t = t ? t : queues_[idx % thread_count_].pop(); + + if (!t) { + break; + } + + (*t)(); + } +} |