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 using namespace std;
24 
25 // Qt headers
26 #include <QStringList>
27 #include <QTimerEvent>
28 #include <QRunnable>
29 #include <QtGlobal>
30 #include <QSet>
31 
32 // MythTV headers
33 #include "mythlogging.h"
34 #include "mythdbcon.h"
35 #include "mythtimer.h"
36 #include "mythdate.h"
37 #include "logging.h"
38 #include "mthread.h"
39 #include "mythdb.h"
40 
42 {
43  if (!thread)
44  return false;
45  return QThread::currentThread() == thread->qthread();
46 }
47 
48 bool is_current_thread(QThread *thread)
49 {
50  if (!thread)
51  return false;
52  return QThread::currentThread() == thread;
53 }
54 
56 {
57  return QThread::currentThread() == thread.qthread();
58 }
59 
60 class DBPurgeHandler : public QObject
61 {
62  public:
64  {
65  purgeTimer = startTimer(5 * 60000);
66  }
67  void timerEvent(QTimerEvent *event) override // QObject
68  {
69  if (event->timerId() == purgeTimer)
70  GetMythDB()->GetDBManager()->PurgeIdleConnections(false);
71  }
73 };
74 
75 class MThreadInternal : public QThread
76 {
77  public:
78  explicit MThreadInternal(MThread &parent) : m_parent(parent) {}
79  void run(void) override { m_parent.run(); } // QThread
80 
81  void QThreadRun(void) { QThread::run(); }
82  int exec(void)
83  {
84  DBPurgeHandler ph;
85  return QThread::exec();
86  }
87 
88  static void SetTerminationEnabled(bool enabled = true)
89  { QThread::setTerminationEnabled(enabled); }
90 
91  static void Sleep(unsigned long time) { QThread::sleep(time); }
92  static void MSleep(unsigned long time) { QThread::msleep(time); }
93  static void USleep(unsigned long time) { QThread::usleep(time); }
94 
95  private:
97 };
98 
99 static QMutex s_all_threads_lock;
100 static QSet<MThread*> s_all_threads;
101 
102 MThread::MThread(const QString &objectName) :
103  m_thread(new MThreadInternal(*this)), m_runnable(nullptr),
104  m_prolog_executed(true), m_epilog_executed(true)
105 {
106  m_thread->setObjectName(objectName);
107  QMutexLocker locker(&s_all_threads_lock);
108  s_all_threads.insert(this);
109 }
110 
111 MThread::MThread(const QString &objectName, QRunnable *runnable) :
112  m_thread(new MThreadInternal(*this)), m_runnable(runnable),
113  m_prolog_executed(false), m_epilog_executed(false)
114 {
115  m_thread->setObjectName(objectName);
116  QMutexLocker locker(&s_all_threads_lock);
117  s_all_threads.insert(this);
118 }
119 
121 {
122  if (!m_prolog_executed)
123  {
124  LOG(VB_GENERAL, LOG_CRIT, "MThread prolog was never run!");
125  }
126  if (!m_epilog_executed)
127  {
128  LOG(VB_GENERAL, LOG_CRIT, "MThread epilog was never run!");
129  }
130  if (m_thread->isRunning())
131  {
132  LOG(VB_GENERAL, LOG_CRIT,
133  "MThread destructor called while thread still running!");
134  m_thread->wait();
135  }
136 
137  {
138  QMutexLocker locker(&s_all_threads_lock);
139  s_all_threads.remove(this);
140  }
141 
142  delete m_thread;
143  m_thread = nullptr;
144 }
145 
147 {
148  QMutexLocker locker(&s_all_threads_lock);
149  QSet<MThread*> badGuys;
150  QSet<MThread*>::const_iterator it;
151  for (it = s_all_threads.begin(); it != s_all_threads.end(); ++it)
152  {
153  if ((*it)->isRunning())
154  {
155  badGuys.insert(*it);
156  (*it)->exit(1);
157  }
158  }
159 
160  if (badGuys.empty())
161  return;
162 
163  // logging has been stopped so we need to use iostream...
164  cerr<<"Error: Not all threads were shut down properly: "<<endl;
165  for (it = badGuys.begin(); it != badGuys.end(); ++it)
166  {
167  cerr<<"Thread "<<qPrintable((*it)->objectName())
168  <<" is still running"<<endl;
169  }
170  cerr<<endl;
171 
172  static const int kTimeout = 5000;
173  MythTimer t;
174  t.start();
175  for (it = badGuys.begin();
176  it != badGuys.end() && t.elapsed() < kTimeout; ++it)
177  {
178  int left = kTimeout - t.elapsed();
179  if (left > 0)
180  (*it)->wait(left);
181  }
182 }
183 
184 void MThread::GetAllThreadNames(QStringList &list)
185 {
186  QMutexLocker locker(&s_all_threads_lock);
187  QSet<MThread*>::const_iterator it;
188  for (it = s_all_threads.begin(); it != s_all_threads.end(); ++it)
189  list.push_back((*it)->objectName());
190 }
191 
192 void MThread::GetAllRunningThreadNames(QStringList &list)
193 {
194  QMutexLocker locker(&s_all_threads_lock);
195  QSet<MThread*>::const_iterator it;
196  for (it = s_all_threads.begin(); it != s_all_threads.end(); ++it)
197  {
198  if ((*it)->isRunning())
199  list.push_back((*it)->objectName());
200  }
201 }
202 
204 {
205  if (QThread::currentThread() != m_thread)
206  {
207  LOG(VB_GENERAL, LOG_CRIT,
208  "RunProlog can only be executed in the run() method of a thread.");
209  return;
210  }
211  setTerminationEnabled(false);
212  ThreadSetup(m_thread->objectName());
213  m_prolog_executed = true;
214 }
215 
217 {
218  if (QThread::currentThread() != m_thread)
219  {
220  LOG(VB_GENERAL, LOG_CRIT,
221  "RunEpilog can only be executed in the run() method of a thread.");
222  return;
223  }
224  ThreadCleanup();
225  m_epilog_executed = true;
226 }
227 
228 void MThread::ThreadSetup(const QString &name)
229 {
231 #if QT_VERSION < QT_VERSION_CHECK(5,8,0)
232  qsrand(MythDate::current().toTime_t() ^ QTime::currentTime().msec());
233 #else
234  qsrand(MythDate::current().toSecsSinceEpoch() ^ QTime::currentTime().msec());
235 #endif
236 }
237 
239 {
240  if (GetMythDB() && GetMythDB()->GetDBManager())
241  GetMythDB()->GetDBManager()->CloseDatabases();
243 }
244 
245 QThread *MThread::qthread(void)
246 {
247  return m_thread;
248 }
249 
250 void MThread::setObjectName(const QString &name)
251 {
252  m_thread->setObjectName(name);
253 }
254 
255 QString MThread::objectName(void) const
256 {
257  return m_thread->objectName();
258 }
259 
260 void MThread::setPriority(QThread::Priority priority)
261 {
262  m_thread->setPriority(priority);
263 }
264 
265 QThread::Priority MThread::priority(void) const
266 {
267  return m_thread->priority();
268 }
269 
270 bool MThread::isFinished(void) const
271 {
272  return m_thread->isFinished();
273 }
274 
275 bool MThread::isRunning(void) const
276 {
277  return m_thread->isRunning();
278 }
279 
281 {
282  m_thread->setStackSize(stackSize);
283 }
284 
286 {
287  return m_thread->stackSize();
288 }
289 
290 void MThread::exit(int retcode)
291 {
292  m_thread->exit(retcode);
293 }
294 
295 void MThread::start(QThread::Priority p)
296 {
297  m_prolog_executed = false;
298  m_epilog_executed = false;
299  m_thread->start(p);
300 }
301 
303 {
304  m_thread->terminate();
305 }
306 
307 void MThread::quit(void)
308 {
309  m_thread->quit();
310 }
311 
312 bool MThread::wait(unsigned long time)
313 {
314  if (m_thread->isRunning())
315  return m_thread->wait(time);
316  return true;
317 }
318 
319 void MThread::run(void)
320 {
321  RunProlog();
322  if (m_runnable)
323  m_runnable->run();
324  else
325  m_thread->QThreadRun();
326  RunEpilog();
327 }
328 
329 int MThread::exec(void)
330 {
331  return m_thread->exec();
332 }
333 
335 {
337 }
338 
339 void MThread::sleep(unsigned long time)
340 {
342 }
343 
344 void MThread::msleep(unsigned long time)
345 {
347 }
348 
349 void MThread::usleep(unsigned long time)
350 {
352 }
353 
354 /* vim: set expandtab tabstop=4 shiftwidth=4: */
void RunEpilog(void)
Cleans up a thread's resources, call this if you reimplement run().
Definition: mthread.cpp:216
void run(void) override
Definition: mthread.cpp:79
static QMutex s_all_threads_lock
Definition: mthread.cpp:99
void start(QThread::Priority=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
Definition: mthread.cpp:295
This is a wrapper around QThread that does several additional things.
Definition: mthread.h:46
A QElapsedTimer based timer to replace use of QTime as a timer.
Definition: mythtimer.h:13
void terminate(void)
Kill a thread unsafely.
Definition: mthread.cpp:302
void QThreadRun(void)
Definition: mthread.cpp:81
VERBOSE_PREAMBLE Most true
Definition: verbosedefs.h:91
MThreadInternal * m_thread
Definition: mthread.h:130
QString objectName(void) const
Definition: mthread.cpp:255
static void SetTerminationEnabled(bool enabled=true)
Definition: mthread.cpp:88
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:319
static void USleep(unsigned long time)
Definition: mthread.cpp:93
bool wait(unsigned long time=ULONG_MAX)
Wait for the MThread to exit, with a maximum timeout.
Definition: mthread.cpp:312
static void sleep(unsigned long time)
Definition: mthread.cpp:339
unsigned int uint
Definition: compat.h:140
bool m_epilog_executed
Definition: mthread.h:133
void loggingRegisterThread(const QString &name)
Register the current thread with the given name.
Definition: logging.cpp:774
void setObjectName(const QString &name)
Definition: mthread.cpp:250
static void msleep(unsigned long time)
Definition: mthread.cpp:344
void timerEvent(QTimerEvent *event) override
Definition: mthread.cpp:67
MThread(const QString &objectName)
Standard constructor.
Definition: mthread.cpp:102
int exec(void)
Definition: mthread.cpp:82
unsigned sleep(unsigned int x)
Definition: compat.h:152
void setStackSize(uint stackSize)
Definition: mthread.cpp:280
QRunnable * m_runnable
Definition: mthread.h:131
static int run(MythMediaDevice *dev=nullptr, bool startRandomShow=false)
VERBOSE_PREAMBLE false
Definition: verbosedefs.h:85
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:10
unsigned char t
Definition: ParseText.cpp:340
static void GetAllRunningThreadNames(QStringList &list)
Definition: mthread.cpp:192
void setPriority(QThread::Priority priority)
Definition: mthread.cpp:260
void exit(int retcode=0)
Use this to exit from the thread if you are using a Qt event loop.
Definition: mthread.cpp:290
bool isRunning(void) const
Definition: mthread.cpp:275
static QSet< MThread * > s_all_threads
Definition: mthread.cpp:100
MThreadInternal(MThread &parent)
Definition: mthread.cpp:78
bool m_prolog_executed
Definition: mthread.h:132
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:238
void loggingDeregisterThread(void)
Deregister the current thread's name.
Definition: logging.cpp:793
const char * name
Definition: ParseText.cpp:339
static void Sleep(unsigned long time)
Definition: mthread.cpp:91
static void GetAllThreadNames(QStringList &list)
Definition: mthread.cpp:184
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
QThread::Priority priority(void) const
Definition: mthread.cpp:265
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:146
static void MSleep(unsigned long time)
Definition: mthread.cpp:92
void RunProlog(void)
Sets up a thread, call this if you reimplement run().
Definition: mthread.cpp:203
QThread * qthread(void)
Returns the thread, this will always return the same pointer no matter how often you restart the thre...
Definition: mthread.cpp:245
static void ThreadSetup(const QString &)
This is to be called on startup in those few threads that haven't been ported to MThread.
Definition: mthread.cpp:228
uint stackSize(void) const
Definition: mthread.cpp:285
bool isFinished(void) const
Definition: mthread.cpp:270
bool is_current_thread(MThread *thread)
Use this to determine if you are in the named thread.
Definition: mthread.cpp:41
MythDB * GetMythDB(void)
Definition: mythdb.cpp:46
virtual ~MThread()
Definition: mthread.cpp:120
static void usleep(unsigned long time)
Definition: mthread.cpp:349
int exec(void)
Enters the qt event loop. call exit or quit to exit thread.
Definition: mthread.cpp:329
static void setTerminationEnabled(bool enabled=true)
Definition: mthread.cpp:334
MThread & m_parent
Definition: mthread.cpp:96
void quit(void)
calls exit(0)
Definition: mthread.cpp:307