Line data Source code
1 : // 2 : // Copyright 2024 OpenModelViewer Authors 3 : // 4 : // Licensed under the Apache License, Version 2.0 (the "License"); 5 : // you may not use this file except in compliance with the License. 6 : // You may obtain a copy of the License at 7 : // 8 : // http://www.apache.org/licenses/LICENSE-2.0 9 : // 10 : // Unless required by applicable law or agreed to in writing, software 11 : // distributed under the License is distributed on an "AS IS" BASIS, 12 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 : // See the License for the specific language governing permissions and 14 : // limitations under the License. 15 : // 16 : 17 : #include "openmodelviewer/core/async/thread_pool.hpp" 18 : 19 : #include <algorithm> 20 : 21 : namespace openmodelviewer::core::async 22 : { 23 63 : ThreadPool::ThreadPool(size_t poolSize) : 24 63 : m_poolSize(poolSize) 25 : { 26 63 : m_running = false; 27 63 : } 28 : 29 124 : ThreadPool::~ThreadPool() 30 : { 31 63 : this->stop(); 32 124 : } 33 : 34 49 : bool ThreadPool::start() 35 : { 36 49 : if (m_running.load() || m_poolSize == 0) 37 : { 38 0 : return false; 39 : } 40 : 41 49 : m_running = true; 42 : 43 146 : for (size_t i = 0; i < m_poolSize; i++) 44 : { 45 97 : m_workers.emplace_back( 46 97 : [this]() 47 : { 48 97 : this->workLoop(); 49 97 : } 50 : ); 51 : } 52 : 53 49 : return true; 54 : } 55 : 56 112 : void ThreadPool::stop() 57 : { 58 112 : if (!m_running.load()) 59 : { 60 63 : return; 61 : } 62 : 63 49 : m_running = false; 64 : 65 : { 66 49 : std::lock_guard<std::mutex> lock(m_mutex); 67 49 : m_condition.notify_all(); 68 49 : } 69 : 70 146 : for (auto& worker : m_workers) 71 : { 72 97 : if (worker.joinable()) 73 : { 74 97 : worker.join(); 75 : } 76 : } 77 : 78 49 : m_workers.clear(); 79 : } 80 : 81 181 : bool ThreadPool::isRunning() const noexcept 82 : { 83 181 : return m_running.load(); 84 : } 85 : 86 59 : size_t ThreadPool::computeOptimalPoolSize(size_t reserved) 87 : { 88 59 : const auto hc = std::thread::hardware_concurrency(); 89 : 90 59 : if (hc <= (2 + reserved)) 91 : { 92 3 : return 1; 93 : } 94 : 95 56 : return static_cast<size_t>(hc - (2 + reserved)); 96 : } 97 : 98 97 : void ThreadPool::workLoop() 99 : { 100 186 : while (m_running.load()) 101 : { 102 162 : std::unique_lock<std::mutex> ulock(m_mutex); 103 168 : m_condition.wait(ulock, 104 476 : [this]() 105 : { 106 283 : return (!this->m_tasks.empty() || !this->m_running.load()); 107 : } 108 : ); 109 : 110 168 : if (!m_running.load() && m_tasks.empty()) 111 : { 112 78 : return; 113 : } 114 : 115 90 : std::unique_ptr<ITask> task = std::move(m_tasks.front()); 116 : 117 90 : m_tasks.pop(); 118 90 : ulock.unlock(); 119 : 120 90 : if (task) 121 : { 122 90 : m_activeTasks.fetch_add(1, std::memory_order_relaxed); 123 90 : task->execute(); 124 83 : m_activeTasks.fetch_sub(1, std::memory_order_relaxed); 125 : 126 166 : if (m_activeTasks.load() == 0 && m_tasks.empty()) 127 : { 128 38 : m_idleCondition.notify_all(); 129 : } 130 : } 131 162 : } 132 : } 133 : 134 0 : void ThreadPool::waitIdle() 135 : { 136 0 : std::unique_lock<std::mutex> lock(m_mutex); 137 0 : m_idleCondition.wait(lock, 138 0 : [this] 139 : { 140 0 : return m_tasks.empty() && m_activeTasks.load() == 0; 141 : } 142 : ); 143 0 : } 144 : }