VirtualBox

source: vbox/trunk/src/VBox/Frontends/VirtualBox/src/globals/UIThreadPool.cpp@ 82781

Last change on this file since 82781 was 76606, checked in by vboxsync, 5 years ago

FE/Qt: Cleaning out old precompiled header experiment.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 9.8 KB
Line 
1/* $Id: UIThreadPool.cpp 76606 2019-01-02 05:40:39Z vboxsync $ */
2/** @file
3 * VBox Qt GUI - UIThreadPool and UITask classes implementation.
4 */
5
6/*
7 * Copyright (C) 2013-2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Qt includes: */
19#include <QThread>
20
21/* GUI includes: */
22#include "COMDefs.h"
23#include "UIDefs.h"
24#include "UIThreadPool.h"
25
26/* Other VBox includes: */
27#include <iprt/assert.h>
28
29
30/** QThread extension used as worker-thread.
31 * Capable of executing COM-related tasks. */
32class UIThreadWorker : public QThread
33{
34 Q_OBJECT;
35
36signals:
37
38 /** Notifies listeners about @a pWorker finished. */
39 void sigFinished(UIThreadWorker *pWorker);
40
41public:
42
43 /** Constructs worker-thread for parent worker-thread @a pPool.
44 * @param iIndex Brings worker-thread index within the worker-thread pool registry. */
45 UIThreadWorker(UIThreadPool *pPool, int iIndex);
46
47 /** Returns worker-thread index within the worker-thread pool registry. */
48 int index() const { return m_iIndex; }
49
50 /** Disables sigFinished signal, for optimizing worker-thread pool termination. */
51 void setNoFinishedSignal() { m_fNoFinishedSignal = true; }
52
53private:
54
55 /** Contains the worker-thread body. */
56 void run();
57
58 /** Holds the worker-thread pool reference. */
59 UIThreadPool *m_pPool;
60
61 /** Holds the worker-thread index within the worker-thread pool registry. */
62 int m_iIndex;
63
64 /** Holds whether sigFinished signal should be emitted or not. */
65 bool m_fNoFinishedSignal;
66};
67
68
69/*********************************************************************************************************************************
70* Class UIThreadPool implementation. *
71*********************************************************************************************************************************/
72
73UIThreadPool::UIThreadPool(ulong cMaxWorkers /* = 3 */, ulong cMsWorkerIdleTimeout /* = 5000 */)
74 : m_cMsIdleTimeout(cMsWorkerIdleTimeout)
75 , m_workers(cMaxWorkers)
76 , m_cWorkers(0)
77 , m_cIdleWorkers(0)
78 , m_fTerminating(false)
79{
80}
81
82UIThreadPool::~UIThreadPool()
83{
84 /* Set termination status: */
85 setTerminating();
86
87 /* Lock initially: */
88 m_everythingLocker.lock();
89
90 /* Cleanup all the workers: */
91 for (int idxWorker = 0; idxWorker < m_workers.size(); ++idxWorker)
92 {
93 /* Acquire the worker: */
94 UIThreadWorker *pWorker = m_workers.at(idxWorker);
95 /* Remove it from the registry: */
96 m_workers[idxWorker] = 0;
97
98 /* Clean up the worker, if there was one: */
99 if (pWorker)
100 {
101 /* Decrease the number of workers: */
102 --m_cWorkers;
103 /* Unlock temporary to let the worker finish: */
104 m_everythingLocker.unlock();
105 /* Wait for the worker to finish: */
106 pWorker->wait();
107 /* Lock again: */
108 m_everythingLocker.lock();
109 /* Delete the worker finally: */
110 delete pWorker;
111 }
112 }
113
114 /* Cleanup all the tasks: */
115 qDeleteAll(m_pendingTasks);
116 qDeleteAll(m_executingTasks);
117 m_pendingTasks.clear();
118 m_executingTasks.clear();
119
120 /* Unlock finally: */
121 m_everythingLocker.unlock();
122}
123
124bool UIThreadPool::isTerminating() const
125{
126 /* Lock initially: */
127 m_everythingLocker.lock();
128
129 /* Acquire termination-flag: */
130 bool fTerminating = m_fTerminating;
131
132 /* Unlock finally: */
133 m_everythingLocker.unlock();
134
135 /* Return termination-flag: */
136 return fTerminating;
137}
138
139void UIThreadPool::setTerminating()
140{
141 /* Lock initially: */
142 m_everythingLocker.lock();
143
144 /* Assign termination-flag: */
145 m_fTerminating = true;
146
147 /* Tell all threads to NOT queue any termination signals: */
148 for (int idxWorker = 0; idxWorker < m_workers.size(); ++idxWorker)
149 {
150 UIThreadWorker *pWorker = m_workers.at(idxWorker);
151 if (pWorker)
152 pWorker->setNoFinishedSignal();
153 }
154
155 /* Wake up all idle worker threads: */
156 m_taskCondition.wakeAll();
157
158 /* Unlock finally: */
159 m_everythingLocker.unlock();
160}
161
162void UIThreadPool::enqueueTask(UITask *pTask)
163{
164 /* Do nothing if terminating: */
165 AssertReturnVoid(!isTerminating());
166
167 /* Prepare task: */
168 connect(pTask, &UITask::sigComplete,
169 this, &UIThreadPool::sltHandleTaskComplete, Qt::QueuedConnection);
170
171 /* Lock initially: */
172 m_everythingLocker.lock();
173
174 /* Put the task into the queue: */
175 m_pendingTasks.enqueue(pTask);
176
177 /* Wake up an idle worker if we got one: */
178 if (m_cIdleWorkers > 0)
179 {
180 m_taskCondition.wakeOne();
181 }
182 /* No idle worker threads, should we create a new one? */
183 else if (m_cWorkers < m_workers.size())
184 {
185 /* Find free slot: */
186 int idxFirstUnused = m_workers.size();
187 while (idxFirstUnused-- > 0)
188 if (m_workers.at(idxFirstUnused) == 0)
189 {
190 /* Prepare the new worker: */
191 UIThreadWorker *pWorker = new UIThreadWorker(this, idxFirstUnused);
192 connect(pWorker, &UIThreadWorker::sigFinished,
193 this, &UIThreadPool::sltHandleWorkerFinished, Qt::QueuedConnection);
194 m_workers[idxFirstUnused] = pWorker;
195 ++m_cWorkers;
196
197 /* And start it: */
198 pWorker->start();
199 break;
200 }
201 }
202 /* else: wait for some worker to complete
203 * whatever it's busy with and jump to it. */
204
205 /* Unlock finally: */
206 m_everythingLocker.unlock();
207}
208
209UITask *UIThreadPool::dequeueTask(UIThreadWorker *pWorker)
210{
211 /* Lock initially: */
212 m_everythingLocker.lock();
213
214 /* Dequeue a task, watching out for terminations.
215 * For optimal efficiency in enqueueTask() we keep count of idle threads.
216 * If the wait times out, we'll return 0 and terminate the thread. */
217 bool fIdleTimedOut = false;
218 while (!m_fTerminating)
219 {
220 /* Make sure that worker has proper index: */
221 Assert(m_workers.at(pWorker->index()) == pWorker);
222
223 /* Dequeue task if there is one: */
224 if (!m_pendingTasks.isEmpty())
225 {
226 UITask *pTask = m_pendingTasks.dequeue();
227 if (pTask)
228 {
229 /* Put into the set of executing tasks: */
230 m_executingTasks << pTask;
231
232 /* Unlock finally: */
233 m_everythingLocker.unlock();
234
235 /* Return dequeued task: */
236 return pTask;
237 }
238 }
239
240 /* If we timed out already, then quit the worker thread. To prevent a
241 * race between enqueueTask and the queue removal of the thread from
242 * the workers vector, we remove it here already. (This does not apply
243 * to the termination scenario.) */
244 if (fIdleTimedOut)
245 {
246 m_workers[pWorker->index()] = 0;
247 --m_cWorkers;
248 break;
249 }
250
251 /* Wait for a task or timeout: */
252 ++m_cIdleWorkers;
253 fIdleTimedOut = !m_taskCondition.wait(&m_everythingLocker, m_cMsIdleTimeout);
254 --m_cIdleWorkers;
255 }
256
257 /* Unlock finally: */
258 m_everythingLocker.unlock();
259
260 /* Return 0 by default: */
261 return 0;
262}
263
264void UIThreadPool::sltHandleTaskComplete(UITask *pTask)
265{
266 /* Skip on termination: */
267 if (isTerminating())
268 return;
269
270 /* Notify listeners: */
271 emit sigTaskComplete(pTask);
272
273 /* Lock initially: */
274 m_everythingLocker.lock();
275
276 /* Delete task finally: */
277 if ( !m_executingTasks.contains(pTask)
278 || !m_executingTasks.remove(pTask))
279 AssertMsgFailed(("Unable to find or remove complete task!"));
280 delete pTask;
281
282 /* Unlock finally: */
283 m_everythingLocker.unlock();
284}
285
286void UIThreadPool::sltHandleWorkerFinished(UIThreadWorker *pWorker)
287{
288 /* Wait for the thread to finish completely, then delete the thread
289 * object. We have already removed the thread from the workers vector.
290 * Note! We don't want to use 'this' here, in case it's invalid. */
291 pWorker->wait();
292 delete pWorker;
293}
294
295void UITask::start()
296{
297 /* Run task: */
298 run();
299 /* Notify listeners: */
300 emit sigComplete(this);
301}
302
303
304/*********************************************************************************************************************************
305* Class UIThreadWorker implementation. *
306*********************************************************************************************************************************/
307
308UIThreadWorker::UIThreadWorker(UIThreadPool *pPool, int iIndex)
309 : m_pPool(pPool)
310 , m_iIndex(iIndex)
311 , m_fNoFinishedSignal(false)
312{
313}
314
315void UIThreadWorker::run()
316{
317 /* Initialize COM: */
318 COMBase::InitializeCOM(false);
319
320 /* Try get a task from the pool queue: */
321 while (UITask *pTask = m_pPool->dequeueTask(this))
322 {
323 /* Process the task if we are not terminating.
324 * Please take into account tasks are cleared by the UIThreadPool
325 * after all listeners notified about task is complete and handled it. */
326 if (!m_pPool->isTerminating())
327 pTask->start();
328 }
329
330 /* Cleanup COM: */
331 COMBase::CleanupCOM();
332
333 /* Queue a signal for the pool to do thread cleanup, unless the pool is
334 already terminating and doesn't need the signal. */
335 if (!m_fNoFinishedSignal)
336 emit sigFinished(this);
337}
338
339
340#include "UIThreadPool.moc"
341
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use