CPPMyth
Library to interoperate with MythTV server
Loading...
Searching...
No Matches
threadpool.cpp
1/*
2 * Copyright (C) 2015-2026 Jean-Luc Barriere
3 *
4 * This library is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published
6 * by the Free Software Foundation; either version 3, or (at your option)
7 * any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this library; see the file COPYING. If not, write to
16 * the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston,
17 * MA 02110-1301 USA
18 * http://www.gnu.org/copyleft/gpl.html
19 *
20 */
21
22#include "threadpool.h"
23
24#include <cassert>
25
26#define WTH_KEEPALIVE 5000
27
28#ifdef NSROOT
29using namespace NSROOT::OS;
30#else
31using namespace OS;
32#endif
33
34ThreadPool::ThreadPool()
35: m_size(1)
36, m_keepAlive(WTH_KEEPALIVE)
37, m_poolSize(0)
38, m_waitingCount(0)
39, m_stopped(false)
40, m_suspended(false)
41, m_empty(false)
42{
43}
44
45ThreadPool::ThreadPool(unsigned size)
46: m_size(size)
47, m_keepAlive(WTH_KEEPALIVE)
48, m_poolSize(0)
49, m_waitingCount(0)
50, m_stopped(false)
51, m_suspended(false)
52, m_empty(false)
53{
54}
55
56ThreadPool::~ThreadPool()
57{
58 m_mutex.lock();
59 // Reject new runs
60 m_stopped = true;
61 // Destroy all queued workers
62 while (!m_queue.empty())
63 {
64 delete m_queue.front();
65 m_queue.pop();
66 }
67 // Finalize all running
68 if (!m_pool.empty())
69 {
70 m_empty = false;
71 // Signal stop
72 for (std::set<WorkerThread*>::iterator it = m_pool.begin(); it != m_pool.end(); ++it)
73 (*it)->stop_thread(false);
74 // Wake sleeper
75 m_queueFill.notify_all();
76 // Waiting all finalized
77 m_condition.wait(m_mutex, m_empty);
78 }
79}
80
81bool ThreadPool::enqueue(Worker* worker)
82{
83 assert(worker->m_queued != true);
84 LockGuard lock(m_mutex);
85 if (!m_stopped)
86 {
87 worker->m_queued = true;
88 m_queue.push(worker);
89 if (!m_suspended)
90 {
91 if (m_waitingCount)
92 {
93 // Wake a thread
94 m_queueFill.notify_one();
95 return true;
96 }
97 else
98 {
99 __resize();
100 return true;
101 }
102 }
103 // Delayed work
104 return true;
105 }
106 return false;
107}
108
109void ThreadPool::set_max_size(unsigned size)
110{
111 LockGuard lock(m_mutex);
112 m_size = size;
113 if (!m_suspended)
114 __resize();
115}
116
117void ThreadPool::set_keep_alive(unsigned millisec)
118{
119 LockGuard lock(m_mutex);
120 m_keepAlive = millisec;
121}
122
123unsigned ThreadPool::size() const
124{
125 LockGuard lock(m_mutex);
126 return m_poolSize;
127}
128
129unsigned ThreadPool::queue_size() const
130{
131 LockGuard lock(m_mutex);
132 return static_cast<unsigned>(m_queue.size());
133}
134
135bool ThreadPool::is_queue_empty() const
136{
137 LockGuard lock(m_mutex);
138 return m_queue.empty();
139}
140
141bool ThreadPool::wait_empty()
142{
143 return is_queue_empty() || m_queueEmpty.wait();
144}
145
146bool ThreadPool::wait_empty_for(unsigned millisec)
147{
148 return is_queue_empty() || m_queueEmpty.wait_for(millisec);
149}
150
151void ThreadPool::suspend()
152{
153 LockGuard lock(m_mutex);
154 m_suspended = true;
155}
156
157void ThreadPool::resume()
158{
159 LockGuard lock(m_mutex);
160 m_suspended = false;
161 __resize();
162}
163
164bool ThreadPool::is_suspended() const
165{
166 LockGuard lock(m_mutex);
167 return m_suspended;
168}
169
170void ThreadPool::reset()
171{
172 LockGuard lock(m_mutex);
173 m_stopped = true;
174 // Destroy all queued workers
175 while (!m_queue.empty())
176 {
177 delete m_queue.front();
178 m_queue.pop();
179 }
180}
181
182void ThreadPool::stop()
183{
184 LockGuard lock(m_mutex);
185 m_stopped = true;
186}
187
188void ThreadPool::start()
189{
190 LockGuard lock(m_mutex);
191 m_stopped = false;
192}
193
194bool ThreadPool::is_stopped() const
195{
196 LockGuard lock(m_mutex);
197 return m_stopped;
198}
199
200Worker* ThreadPool::pop_queue(WorkerThread* _thread)
201{
202 (void)_thread;
203 LockGuard lock(m_mutex);
204 if (!m_suspended)
205 {
206 m_queueEmpty.notify_one();
207 if (!m_queue.empty())
208 {
209 Worker* worker = m_queue.front();
210 m_queue.pop();
211 return worker;
212 }
213 }
214 return nullptr;
215}
216
217void ThreadPool::wait_queue(WorkerThread* _thread)
218{
219 (void)_thread;
220 m_mutex.lock();
221 ++m_waitingCount;
222 unsigned millisec = m_keepAlive;
223 m_mutex.unlock();
224 m_queueFill.wait_for(millisec);
225 m_mutex.lock();
226 --m_waitingCount;
227 m_mutex.unlock();
228}
229
230void ThreadPool::start_thread(WorkerThread* _thread)
231{
232 ++m_poolSize;
233 m_pool.insert(_thread);
234 if (!_thread->start_thread(false))
235 finalize_thread(_thread);
236}
237
238void ThreadPool::finalize_thread(WorkerThread* _thread)
239{
240 LockGuard lock(m_mutex);
241 if (m_pool.erase(_thread))
242 {
243 --m_poolSize;
244 delete _thread;
245 }
246 if (m_pool.empty())
247 {
248 m_empty = true;
249 m_condition.notify_all();
250 }
251}
252
253void ThreadPool::__resize()
254{
255 if (m_poolSize < m_size && !m_queue.empty())
256 {
257 for (unsigned i = m_queue.size(); i > 0; --i)
258 {
259 if (m_poolSize >= m_size)
260 break;
261 WorkerThread* _thread = new WorkerThread(*this);
262 // The new thread will check the queue
263 start_thread(_thread);
264 }
265 }
266 else if (m_poolSize > m_size)
267 {
268 std::set<WorkerThread*>::iterator it = m_pool.begin();
269 for (unsigned i = m_poolSize - m_size; i > 0; --i)
270 {
271 if (it == m_pool.end())
272 break;
273 (*it)->stop_thread(false);
274 ++it;
275 }
276 // Wake up the waiting threads to stop
277 if (m_waitingCount)
278 m_queueFill.notify_all();
279 }
280}