MythTV master
mthread.cpp
Go to the documentation of this file.
1/* -*- Mode: c++ -*-
2 *
3 * Class MThread
4 *
5 * Copyright (C) Daniel Kristjansson 2011
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22#include <iostream>
23
24// Qt headers
25#include <QStringList>
26#include <QTimerEvent>
27#include <QRunnable>
28#include <QtGlobal>
29#include <QSet>
30
31// MythTV headers
32#include "mythlogging.h"
33#include "mythdbcon.h"
34#include "mythtimer.h"
35#include "mythdate.h"
36#include "logging.h"
37#include "mthread.h"
38#include "mythdb.h"
39
41{
42 if (!thread)
43 return false;
44 return QThread::currentThread() == thread->qthread();
45}
46
47bool is_current_thread(QThread *thread)
48{
49 if (!thread)
50 return false;
51 return QThread::currentThread() == thread;
52}
53
55{
56 return QThread::currentThread() == thread.qthread();
57}
58
59class MThreadInternal : public QThread
60{
61 // Allow MThread::exec to directly call the inherited
62 // QThread::exec function (which is a protected function).
63 friend int MThread::exec(void);
64
65 protected:
66 void run(void) override { m_parent.run(); } // QThread
67
68 public:
69 explicit MThreadInternal(MThread &parent) : m_parent(parent) {}
70
71 void QThreadRun(void) { QThread::run(); }
72
73 static void SetTerminationEnabled(bool enabled = true)
74 { QThread::setTerminationEnabled(enabled); }
75
76 private:
78};
79
80static QMutex s_all_threads_lock;
81static QSet<MThread*> s_all_threads;
82
83MThread::MThread(const QString &objectName) :
84 m_thread(new MThreadInternal(*this))
85{
86 m_thread->setObjectName(objectName);
87 QMutexLocker locker(&s_all_threads_lock);
88 s_all_threads.insert(this);
89}
90
91MThread::MThread(const QString &objectName, QRunnable *runnable) :
92 m_thread(new MThreadInternal(*this)), m_runnable(runnable),
93 m_prologExecuted(false), m_epilogExecuted(false)
94{
95 m_thread->setObjectName(objectName);
96 QMutexLocker locker(&s_all_threads_lock);
97 s_all_threads.insert(this);
98}
99
101{
102 if (!m_prologExecuted)
103 {
104 LOG(VB_GENERAL, LOG_CRIT, QString("'%1': MThread prolog was never run!").arg(objectName()));
105 }
106 if (!m_epilogExecuted)
107 {
108 LOG(VB_GENERAL, LOG_CRIT, QString("'%1': MThread epilog was never run! (%1)").arg(objectName()));
109 }
110 if (m_thread->isRunning())
111 {
112 LOG(VB_GENERAL, LOG_CRIT, QString("'%1': MThread destructor called while thread still running! (%1)")
113 .arg(objectName()));
114 m_thread->wait();
115 }
116
117 {
118 QMutexLocker locker(&s_all_threads_lock);
119 s_all_threads.remove(this);
120 }
121
122 delete m_thread;
123 m_thread = nullptr;
124}
125
127{
128 QMutexLocker locker(&s_all_threads_lock);
129 QSet<MThread*> badGuys;
130 for (auto *thread : std::as_const(s_all_threads))
131 {
132 if (thread->isRunning())
133 {
134 badGuys.insert(thread);
135 thread->exit(1);
136 }
137 }
138
139 if (badGuys.empty())
140 return;
141
142 // logging has been stopped so we need to use iostream...
143 std::cerr<<"Error: Not all threads were shut down properly:\n";
144 for (auto *thread : std::as_const(badGuys))
145 {
146 std::cerr<<"Thread "<<qPrintable(thread->objectName())
147 <<" is still running\n";
148 }
149 std::cerr<<'\n';
150
151 static constexpr std::chrono::milliseconds kTimeout { 5s };
152 MythTimer t;
153 t.start();
154 for (auto it = badGuys.cbegin();
155 it != badGuys.cend() && t.elapsed() < kTimeout; ++it)
156 {
157 auto left = kTimeout - t.elapsed();
158 if (left > 0ms)
159 (*it)->wait(left);
160 }
161}
162
163void MThread::GetAllThreadNames(QStringList &list)
164{
165 QMutexLocker locker(&s_all_threads_lock);
166 for (auto *thread : std::as_const(s_all_threads))
167 list.push_back(thread->objectName());
168}
169
171{
172 QMutexLocker locker(&s_all_threads_lock);
173 for (auto *thread : std::as_const(s_all_threads))
174 {
175 if (thread->isRunning())
176 list.push_back(thread->objectName());
177 }
178}
179
181{
182 if (QThread::currentThread() != m_thread)
183 {
184 LOG(VB_GENERAL, LOG_CRIT,
185 "RunProlog can only be executed in the run() method of a thread.");
186 return;
187 }
189 ThreadSetup(m_thread->objectName());
190 m_prologExecuted = true;
191}
192
194{
195 if (QThread::currentThread() != m_thread)
196 {
197 LOG(VB_GENERAL, LOG_CRIT,
198 "RunEpilog can only be executed in the run() method of a thread.");
199 return;
200 }
202 m_epilogExecuted = true;
203}
204
205void MThread::ThreadSetup(const QString &name)
206{
208}
209
211{
212 if (GetMythDB() && GetMythDB()->GetDBManager())
213 GetMythDB()->GetDBManager()->CloseDatabases();
215}
216
217QThread *MThread::qthread(void)
218{
219 return m_thread;
220}
221
222void MThread::setObjectName(const QString &name)
223{
224 m_thread->setObjectName(name);
225}
226
227QString MThread::objectName(void) const
228{
229 return m_thread->objectName();
230}
231
232void MThread::setPriority(QThread::Priority priority)
233{
234 m_thread->setPriority(priority);
235}
236
237QThread::Priority MThread::priority(void) const
238{
239 return m_thread->priority();
240}
241
242bool MThread::isFinished(void) const
243{
244 return m_thread->isFinished();
245}
246
247bool MThread::isRunning(void) const
248{
249 return m_thread->isRunning();
250}
251
253{
254 m_thread->setStackSize(stackSize);
255}
256
258{
259 return m_thread->stackSize();
260}
261
262void MThread::exit(int retcode)
263{
264 m_thread->exit(retcode);
265}
266
267void MThread::start(QThread::Priority p)
268{
269 m_prologExecuted = false;
270 m_epilogExecuted = false;
271 m_thread->start(p);
272}
273
275{
276 m_thread->terminate();
277}
278
280{
281 m_thread->quit();
282}
283
284bool MThread::wait(std::chrono::milliseconds time)
285{
286 if (m_thread->isRunning())
287 {
288 if (time == std::chrono::milliseconds::max())
289 return m_thread->wait(ULONG_MAX); // Magic number in recent Qt5.
290 if (time >= 0ms)
291 return m_thread->wait(time.count());
292 LOG(VB_GENERAL, LOG_CRIT,
293 QString("'%1': MThread::wait called for %1 ms!").arg(time.count()));
294 return m_thread->wait(1);
295 }
296 return true;
297}
298
299void MThread::run(void)
300{
301 RunProlog();
302 if (m_runnable)
303 m_runnable->run();
304 else
306 RunEpilog();
307}
308
310{
311 return m_thread->exec();
312}
313
315{
317}
318
319/* vim: set expandtab tabstop=4 shiftwidth=4: */
MThread & m_parent
Definition: mthread.cpp:77
void run(void) override
Definition: mthread.cpp:66
MThreadInternal(MThread &parent)
Definition: mthread.cpp:69
void QThreadRun(void)
Definition: mthread.cpp:71
static void SetTerminationEnabled(bool enabled=true)
Definition: mthread.cpp:73
This is a wrapper around QThread that does several additional things.
Definition: mthread.h:49
MThreadInternal * m_thread
Definition: mthread.h:131
bool isRunning(void) const
Definition: mthread.cpp:247
void RunProlog(void)
Sets up a thread, call this if you reimplement run().
Definition: mthread.cpp:180
static void Cleanup(void)
This will print out all the running threads, call exit(1) on each and then wait up to 5 seconds total...
Definition: mthread.cpp:126
static void GetAllRunningThreadNames(QStringList &list)
Definition: mthread.cpp:170
int exec(void)
Enters the qt event loop. call exit or quit to exit thread.
Definition: mthread.cpp:309
void terminate(void)
Kill a thread unsafely.
Definition: mthread.cpp:274
bool isFinished(void) const
Definition: mthread.cpp:242
static void setTerminationEnabled(bool enabled=true)
Definition: mthread.cpp:314
static void ThreadCleanup(void)
This is to be called on exit in those few threads that haven't been ported to MThread.
Definition: mthread.cpp:210
void start(QThread::Priority p=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
Definition: mthread.cpp:267
void quit(void)
calls exit(0)
Definition: mthread.cpp:279
virtual void run(void)
Runs the Qt event loop unless we have a QRunnable, in which case we run the runnable run instead.
Definition: mthread.cpp:299
uint stackSize(void) const
Definition: mthread.cpp:257
void RunEpilog(void)
Cleans up a thread's resources, call this if you reimplement run().
Definition: mthread.cpp:193
void setStackSize(uint stackSize)
Definition: mthread.cpp:252
bool m_prologExecuted
Definition: mthread.h:133
QRunnable * m_runnable
Definition: mthread.h:132
static void GetAllThreadNames(QStringList &list)
Definition: mthread.cpp:163
virtual ~MThread()
Definition: mthread.cpp:100
bool wait(std::chrono::milliseconds time=std::chrono::milliseconds::max())
Wait for the MThread to exit, with a maximum timeout.
Definition: mthread.cpp:284
void exit(int retcode=0)
Use this to exit from the thread if you are using a Qt event loop.
Definition: mthread.cpp:262
QThread * qthread(void)
Returns the thread, this will always return the same pointer no matter how often you restart the thre...
Definition: mthread.cpp:217
QThread::Priority priority(void) const
Definition: mthread.cpp:237
MThread(const QString &objectName)
Standard constructor.
Definition: mthread.cpp:83
QString objectName(void) const
Definition: mthread.cpp:227
void setPriority(QThread::Priority priority)
Definition: mthread.cpp:232
static void ThreadSetup(const QString &name)
This is to be called on startup in those few threads that haven't been ported to MThread.
Definition: mthread.cpp:205
void setObjectName(const QString &name)
Definition: mthread.cpp:222
bool m_epilogExecuted
Definition: mthread.h:134
A QElapsedTimer based timer to replace use of QTime as a timer.
Definition: mythtimer.h:14
unsigned int uint
Definition: compat.h:60
void loggingDeregisterThread(void)
Deregister the current thread's name.
Definition: logging.cpp:721
void loggingRegisterThread(const QString &name)
Register the current thread with the given name.
Definition: logging.cpp:702
static QSet< MThread * > s_all_threads
Definition: mthread.cpp:81
static QMutex s_all_threads_lock
Definition: mthread.cpp:80
bool is_current_thread(MThread *thread)
Use this to determine if you are in the named thread.
Definition: mthread.cpp:40
MythDB * GetMythDB(void)
Definition: mythdb.cpp:50
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
void run(const QString &name, Class *object, void(Class::*fn)())
Definition: mconcurrent.h:137
VERBOSE_PREAMBLE false
Definition: verbosedefs.h:80