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 
47 bool 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 
59 class 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 
74 class 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  {
83  DBPurgeHandler ph;
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 
96 static QMutex s_all_threads_lock;
97 static QSet<MThread*> s_all_threads;
98 
99 MThread::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 
107 MThread::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 : qAsConst(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 : qAsConst(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 
179 void MThread::GetAllThreadNames(QStringList &list)
180 {
181  QMutexLocker locker(&s_all_threads_lock);
182  for (auto *thread : qAsConst(s_all_threads))
183  list.push_back(thread->objectName());
184 }
185 
186 void MThread::GetAllRunningThreadNames(QStringList &list)
187 {
188  QMutexLocker locker(&s_all_threads_lock);
189  for (auto *thread : qAsConst(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  }
204  setTerminationEnabled(false);
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  }
217  ThreadCleanup();
218  m_epilogExecuted = true;
219 }
220 
221 void MThread::ThreadSetup(const QString &name)
222 {
223  loggingRegisterThread(name);
224 #if QT_VERSION < QT_VERSION_CHECK(5,10,0)
225  qsrand(MythDate::current().toSecsSinceEpoch() ^ QTime::currentTime().msec());
226 #endif
227 }
228 
230 {
231  if (GetMythDB() && GetMythDB()->GetDBManager())
232  GetMythDB()->GetDBManager()->CloseDatabases();
234 }
235 
236 QThread *MThread::qthread(void)
237 {
238  return m_thread;
239 }
240 
241 void MThread::setObjectName(const QString &name)
242 {
243  m_thread->setObjectName(name);
244 }
245 
246 QString MThread::objectName(void) const
247 {
248  return m_thread->objectName();
249 }
250 
251 void MThread::setPriority(QThread::Priority priority)
252 {
253  m_thread->setPriority(priority);
254 }
255 
256 QThread::Priority MThread::priority(void) const
257 {
258  return m_thread->priority();
259 }
260 
261 bool MThread::isFinished(void) const
262 {
263  return m_thread->isFinished();
264 }
265 
266 bool MThread::isRunning(void) const
267 {
268  return m_thread->isRunning();
269 }
270 
272 {
273  m_thread->setStackSize(stackSize);
274 }
275 
277 {
278  return m_thread->stackSize();
279 }
280 
281 void MThread::exit(int retcode)
282 {
283  m_thread->exit(retcode);
284 }
285 
286 void MThread::start(QThread::Priority p)
287 {
288  m_prologExecuted = false;
289  m_epilogExecuted = false;
290  m_thread->start(p);
291 }
292 
294 {
295  m_thread->terminate();
296 }
297 
298 void MThread::quit(void)
299 {
300  m_thread->quit();
301 }
302 
303 bool MThread::wait(std::chrono::milliseconds time)
304 {
305  if (m_thread->isRunning())
306  {
307  if (time == std::chrono::milliseconds::max())
308  return m_thread->wait(ULONG_MAX); // Magic number in recent Qt5.
309  if (time >= 0ms)
310  return m_thread->wait(time.count());
311  LOG(VB_GENERAL, LOG_CRIT,
312  QString("'%1': MThread::wait called for %1 ms!").arg(time.count()));
313  return m_thread->wait(1);
314  }
315  return true;
316 }
317 
318 void MThread::run(void)
319 {
320  RunProlog();
321  if (m_runnable)
322  m_runnable->run();
323  else
324  m_thread->QThreadRun();
325  RunEpilog();
326 }
327 
328 int MThread::exec(void)
329 {
330  return m_thread->exec();
331 }
332 
334 {
336 }
337 
338 void MThread::usleep(std::chrono::microseconds time)
339 {
341 }
342 
343 /* vim: set expandtab tabstop=4 shiftwidth=4: */
MThread::exec
int exec(void)
Enters the qt event loop. call exit or quit to exit thread.
Definition: mthread.cpp:328
loggingDeregisterThread
void loggingDeregisterThread(void)
Deregister the current thread's name.
Definition: logging.cpp:748
MThreadInternal::exec
int exec(void)
Definition: mthread.cpp:81
MThread::MThread
MThread(const QString &objectName)
Standard constructor.
Definition: mthread.cpp:99
MThread::start
void start(QThread::Priority p=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
Definition: mthread.cpp:286
MThread::quit
void quit(void)
calls exit(0)
Definition: mthread.cpp:298
mythdb.h
false
VERBOSE_PREAMBLE false
Definition: verbosedefs.h:85
MythTimer
A QElapsedTimer based timer to replace use of QTime as a timer.
Definition: mythtimer.h:13
MThreadInternal::run
void run(void) override
Definition: mthread.cpp:78
MThread::~MThread
virtual ~MThread()
Definition: mthread.cpp:116
MThread::wait
bool wait(std::chrono::milliseconds time=std::chrono::milliseconds::max())
Wait for the MThread to exit, with a maximum timeout.
Definition: mthread.cpp:303
DBPurgeHandler::timerEvent
void timerEvent(QTimerEvent *event) override
Definition: mthread.cpp:66
MThreadInternal::USleep
static void USleep(std::chrono::microseconds time)
Definition: mthread.cpp:90
MThread::setTerminationEnabled
static void setTerminationEnabled(bool enabled=true)
Definition: mthread.cpp:333
MThread::terminate
void terminate(void)
Kill a thread unsafely.
Definition: mthread.cpp:293
MThread::m_prologExecuted
bool m_prologExecuted
Definition: mthread.h:137
MThread::usleep
static void usleep(std::chrono::microseconds time)
Definition: mthread.cpp:338
MThread::setObjectName
void setObjectName(const QString &name)
Definition: mthread.cpp:241
mythdbcon.h
MThreadInternal::SetTerminationEnabled
static void SetTerminationEnabled(bool enabled=true)
Definition: mthread.cpp:87
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:23
MThread::RunProlog
void RunProlog(void)
Sets up a thread, call this if you reimplement run().
Definition: mthread.cpp:196
is_current_thread
bool is_current_thread(MThread *thread)
Use this to determine if you are in the named thread.
Definition: mthread.cpp:40
GetMythDB
MythDB * GetMythDB(void)
Definition: mythdb.cpp:46
MythDate::current
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:10
mythdate.h
mythlogging.h
MThreadInternal
Definition: mthread.cpp:74
DBPurgeHandler::DBPurgeHandler
DBPurgeHandler()
Definition: mthread.cpp:62
hardwareprofile.config.p
p
Definition: config.py:33
loggingRegisterThread
void loggingRegisterThread(const QString &name)
Register the current thread with the given name.
Definition: logging.cpp:729
MThread::setPriority
void setPriority(QThread::Priority priority)
Definition: mthread.cpp:251
hardwareprofile.i18n.t
t
Definition: i18n.py:36
MThreadInternal::MThreadInternal
MThreadInternal(MThread &parent)
Definition: mthread.cpp:77
MThread::qthread
QThread * qthread(void)
Returns the thread, this will always return the same pointer no matter how often you restart the thre...
Definition: mthread.cpp:236
MThread::RunEpilog
void RunEpilog(void)
Cleans up a thread's resources, call this if you reimplement run().
Definition: mthread.cpp:209
MThread::stackSize
uint stackSize(void) const
Definition: mthread.cpp:276
MThread::isFinished
bool isFinished(void) const
Definition: mthread.cpp:261
MThreadInternal::QThreadRun
void QThreadRun(void)
Definition: mthread.cpp:80
uint
unsigned int uint
Definition: compat.h:144
MThread::GetAllThreadNames
static void GetAllThreadNames(QStringList &list)
Definition: mthread.cpp:179
MThread::run
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:318
MThread::m_epilogExecuted
bool m_epilogExecuted
Definition: mthread.h:138
MThread::priority
QThread::Priority priority(void) const
Definition: mthread.cpp:256
MThread::setStackSize
void setStackSize(uint stackSize)
Definition: mthread.cpp:271
s_all_threads_lock
static QMutex s_all_threads_lock
Definition: mthread.cpp:96
logging.h
MThread
This is a wrapper around QThread that does several additional things.
Definition: mthread.h:48
mthread.h
MThread::GetAllRunningThreadNames
static void GetAllRunningThreadNames(QStringList &list)
Definition: mthread.cpp:186
MThread::exit
void exit(int retcode=0)
Use this to exit from the thread if you are using a Qt event loop.
Definition: mthread.cpp:281
MThread::isRunning
bool isRunning(void) const
Definition: mthread.cpp:266
MThread::m_thread
MThreadInternal * m_thread
Definition: mthread.h:135
MThread::Cleanup
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
MThread::objectName
QString objectName(void) const
Definition: mthread.cpp:246
mythtimer.h
MThread::ThreadCleanup
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:229
MThread::ThreadSetup
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
s_all_threads
static QSet< MThread * > s_all_threads
Definition: mthread.cpp:97
MThread::m_runnable
QRunnable * m_runnable
Definition: mthread.h:136
DBPurgeHandler::m_purgeTimer
int m_purgeTimer
Definition: mthread.cpp:71
MThreadInternal::m_parent
MThread & m_parent
Definition: mthread.cpp:93
DBPurgeHandler
Definition: mthread.cpp:59