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 DBPurgeHandler : public QObject
60{
61 public:
63 {
64 m_purgeTimer = startTimer(5min);
65 }
66 void timerEvent(QTimerEvent *event) override // QObject
67 {
68 if (event->timerId() == m_purgeTimer)
69 GetMythDB()->GetDBManager()->PurgeIdleConnections(false);
70 }
72};
73
74class MThreadInternal : public QThread
75{
76 public:
77 explicit MThreadInternal(MThread &parent) : m_parent(parent) {}
78 void run(void) override { m_parent.run(); } // QThread
79
80 void QThreadRun(void) { QThread::run(); }
81 int exec(void)
82 {
84 return QThread::exec();
85 }
86
87 static void SetTerminationEnabled(bool enabled = true)
88 { QThread::setTerminationEnabled(enabled); }
89
90 static void USleep(std::chrono::microseconds time) { QThread::usleep(time.count()); }
91
92 private:
94};
95
96static QMutex s_all_threads_lock;
97static QSet<MThread*> s_all_threads;
98
99MThread::MThread(const QString &objectName) :
100 m_thread(new MThreadInternal(*this))
101{
102 m_thread->setObjectName(objectName);
103 QMutexLocker locker(&s_all_threads_lock);
104 s_all_threads.insert(this);
105}
106
107MThread::MThread(const QString &objectName, QRunnable *runnable) :
108 m_thread(new MThreadInternal(*this)), m_runnable(runnable),
109 m_prologExecuted(false), m_epilogExecuted(false)
110{
111 m_thread->setObjectName(objectName);
112 QMutexLocker locker(&s_all_threads_lock);
113 s_all_threads.insert(this);
114}
115
117{
118 if (!m_prologExecuted)
119 {
120 LOG(VB_GENERAL, LOG_CRIT, QString("'%1': MThread prolog was never run!").arg(objectName()));
121 }
122 if (!m_epilogExecuted)
123 {
124 LOG(VB_GENERAL, LOG_CRIT, QString("'%1': MThread epilog was never run! (%1)").arg(objectName()));
125 }
126 if (m_thread->isRunning())
127 {
128 LOG(VB_GENERAL, LOG_CRIT, QString("'%1': MThread destructor called while thread still running! (%1)")
129 .arg(objectName()));
130 m_thread->wait();
131 }
132
133 {
134 QMutexLocker locker(&s_all_threads_lock);
135 s_all_threads.remove(this);
136 }
137
138 delete m_thread;
139 m_thread = nullptr;
140}
141
143{
144 QMutexLocker locker(&s_all_threads_lock);
145 QSet<MThread*> badGuys;
146 for (auto *thread : std::as_const(s_all_threads))
147 {
148 if (thread->isRunning())
149 {
150 badGuys.insert(thread);
151 thread->exit(1);
152 }
153 }
154
155 if (badGuys.empty())
156 return;
157
158 // logging has been stopped so we need to use iostream...
159 std::cerr<<"Error: Not all threads were shut down properly: "<<std::endl;
160 for (auto *thread : std::as_const(badGuys))
161 {
162 std::cerr<<"Thread "<<qPrintable(thread->objectName())
163 <<" is still running"<<std::endl;
164 }
165 std::cerr<<std::endl;
166
167 static constexpr std::chrono::milliseconds kTimeout { 5s };
168 MythTimer t;
169 t.start();
170 for (auto it = badGuys.cbegin();
171 it != badGuys.cend() && t.elapsed() < kTimeout; ++it)
172 {
173 auto left = kTimeout - t.elapsed();
174 if (left > 0ms)
175 (*it)->wait(left);
176 }
177}
178
179void MThread::GetAllThreadNames(QStringList &list)
180{
181 QMutexLocker locker(&s_all_threads_lock);
182 for (auto *thread : std::as_const(s_all_threads))
183 list.push_back(thread->objectName());
184}
185
187{
188 QMutexLocker locker(&s_all_threads_lock);
189 for (auto *thread : std::as_const(s_all_threads))
190 {
191 if (thread->isRunning())
192 list.push_back(thread->objectName());
193 }
194}
195
197{
198 if (QThread::currentThread() != m_thread)
199 {
200 LOG(VB_GENERAL, LOG_CRIT,
201 "RunProlog can only be executed in the run() method of a thread.");
202 return;
203 }
205 ThreadSetup(m_thread->objectName());
206 m_prologExecuted = true;
207}
208
210{
211 if (QThread::currentThread() != m_thread)
212 {
213 LOG(VB_GENERAL, LOG_CRIT,
214 "RunEpilog can only be executed in the run() method of a thread.");
215 return;
216 }
218 m_epilogExecuted = true;
219}
220
221void MThread::ThreadSetup(const QString &name)
222{
224}
225
227{
228 if (GetMythDB() && GetMythDB()->GetDBManager())
229 GetMythDB()->GetDBManager()->CloseDatabases();
231}
232
233QThread *MThread::qthread(void)
234{
235 return m_thread;
236}
237
238void MThread::setObjectName(const QString &name)
239{
240 m_thread->setObjectName(name);
241}
242
243QString MThread::objectName(void) const
244{
245 return m_thread->objectName();
246}
247
248void MThread::setPriority(QThread::Priority priority)
249{
250 m_thread->setPriority(priority);
251}
252
253QThread::Priority MThread::priority(void) const
254{
255 return m_thread->priority();
256}
257
258bool MThread::isFinished(void) const
259{
260 return m_thread->isFinished();
261}
262
263bool MThread::isRunning(void) const
264{
265 return m_thread->isRunning();
266}
267
269{
270 m_thread->setStackSize(stackSize);
271}
272
274{
275 return m_thread->stackSize();
276}
277
278void MThread::exit(int retcode)
279{
280 m_thread->exit(retcode);
281}
282
283void MThread::start(QThread::Priority p)
284{
285 m_prologExecuted = false;
286 m_epilogExecuted = false;
287 m_thread->start(p);
288}
289
291{
292 m_thread->terminate();
293}
294
296{
297 m_thread->quit();
298}
299
300bool MThread::wait(std::chrono::milliseconds time)
301{
302 if (m_thread->isRunning())
303 {
304 if (time == std::chrono::milliseconds::max())
305 return m_thread->wait(ULONG_MAX); // Magic number in recent Qt5.
306 if (time >= 0ms)
307 return m_thread->wait(time.count());
308 LOG(VB_GENERAL, LOG_CRIT,
309 QString("'%1': MThread::wait called for %1 ms!").arg(time.count()));
310 return m_thread->wait(1);
311 }
312 return true;
313}
314
315void MThread::run(void)
316{
317 RunProlog();
318 if (m_runnable)
319 m_runnable->run();
320 else
322 RunEpilog();
323}
324
326{
327 return m_thread->exec();
328}
329
331{
333}
334
335void MThread::usleep(std::chrono::microseconds time)
336{
338}
339
340/* vim: set expandtab tabstop=4 shiftwidth=4: */
int m_purgeTimer
Definition: mthread.cpp:71
void timerEvent(QTimerEvent *event) override
Definition: mthread.cpp:66
MThread & m_parent
Definition: mthread.cpp:93
void run(void) override
Definition: mthread.cpp:78
MThreadInternal(MThread &parent)
Definition: mthread.cpp:77
static void USleep(std::chrono::microseconds time)
Definition: mthread.cpp:90
int exec(void)
Definition: mthread.cpp:81
void QThreadRun(void)
Definition: mthread.cpp:80
static void SetTerminationEnabled(bool enabled=true)
Definition: mthread.cpp:87
This is a wrapper around QThread that does several additional things.
Definition: mthread.h:49
MThreadInternal * m_thread
Definition: mthread.h:135
bool isRunning(void) const
Definition: mthread.cpp:263
void RunProlog(void)
Sets up a thread, call this if you reimplement run().
Definition: mthread.cpp:196
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:142
static void GetAllRunningThreadNames(QStringList &list)
Definition: mthread.cpp:186
int exec(void)
Enters the qt event loop. call exit or quit to exit thread.
Definition: mthread.cpp:325
void terminate(void)
Kill a thread unsafely.
Definition: mthread.cpp:290
bool isFinished(void) const
Definition: mthread.cpp:258
static void setTerminationEnabled(bool enabled=true)
Definition: mthread.cpp:330
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:226
static void usleep(std::chrono::microseconds time)
Definition: mthread.cpp:335
void start(QThread::Priority p=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
Definition: mthread.cpp:283
void quit(void)
calls exit(0)
Definition: mthread.cpp:295
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:315
uint stackSize(void) const
Definition: mthread.cpp:273
void RunEpilog(void)
Cleans up a thread's resources, call this if you reimplement run().
Definition: mthread.cpp:209
void setStackSize(uint stackSize)
Definition: mthread.cpp:268
bool m_prologExecuted
Definition: mthread.h:137
QRunnable * m_runnable
Definition: mthread.h:136
static void GetAllThreadNames(QStringList &list)
Definition: mthread.cpp:179
virtual ~MThread()
Definition: mthread.cpp:116
bool wait(std::chrono::milliseconds time=std::chrono::milliseconds::max())
Wait for the MThread to exit, with a maximum timeout.
Definition: mthread.cpp:300
void exit(int retcode=0)
Use this to exit from the thread if you are using a Qt event loop.
Definition: mthread.cpp:278
QThread * qthread(void)
Returns the thread, this will always return the same pointer no matter how often you restart the thre...
Definition: mthread.cpp:233
QThread::Priority priority(void) const
Definition: mthread.cpp:253
MThread(const QString &objectName)
Standard constructor.
Definition: mthread.cpp:99
QString objectName(void) const
Definition: mthread.cpp:243
void setPriority(QThread::Priority priority)
Definition: mthread.cpp:248
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:221
void setObjectName(const QString &name)
Definition: mthread.cpp:238
bool m_epilogExecuted
Definition: mthread.h:138
A QElapsedTimer based timer to replace use of QTime as a timer.
Definition: mythtimer.h:14
unsigned int uint
Definition: freesurround.h:24
void loggingDeregisterThread(void)
Deregister the current thread's name.
Definition: logging.cpp:723
void loggingRegisterThread(const QString &name)
Register the current thread with the given name.
Definition: logging.cpp:704
static QSet< MThread * > s_all_threads
Definition: mthread.cpp:97
static QMutex s_all_threads_lock
Definition: mthread.cpp:96
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:51
#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:89