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  m_purgeTimer = startTimer(5 * 60000);
66  }
67  void timerEvent(QTimerEvent *event) override // QObject
68  {
69  if (event->timerId() == m_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))
104 {
105  m_thread->setObjectName(objectName);
106  QMutexLocker locker(&s_all_threads_lock);
107  s_all_threads.insert(this);
108 }
109 
110 MThread::MThread(const QString &objectName, QRunnable *runnable) :
111  m_thread(new MThreadInternal(*this)), m_runnable(runnable),
112  m_prologExecuted(false), m_epilogExecuted(false)
113 {
114  m_thread->setObjectName(objectName);
115  QMutexLocker locker(&s_all_threads_lock);
116  s_all_threads.insert(this);
117 }
118 
120 {
121  if (!m_prologExecuted)
122  {
123  LOG(VB_GENERAL, LOG_CRIT, QString("'%1': MThread prolog was never run!").arg(objectName()));
124  }
125  if (!m_epilogExecuted)
126  {
127  LOG(VB_GENERAL, LOG_CRIT, QString("'%1': MThread epilog was never run! (%1)").arg(objectName()));
128  }
129  if (m_thread->isRunning())
130  {
131  LOG(VB_GENERAL, LOG_CRIT, QString("'%1': MThread destructor called while thread still running! (%1)")
132  .arg(objectName()));
133  m_thread->wait();
134  }
135 
136  {
137  QMutexLocker locker(&s_all_threads_lock);
138  s_all_threads.remove(this);
139  }
140 
141  delete m_thread;
142  m_thread = nullptr;
143 }
144 
146 {
147  QMutexLocker locker(&s_all_threads_lock);
148  QSet<MThread*> badGuys;
149  QSet<MThread*>::const_iterator it;
150  for (it = s_all_threads.begin(); it != s_all_threads.end(); ++it)
151  {
152  if ((*it)->isRunning())
153  {
154  badGuys.insert(*it);
155  (*it)->exit(1);
156  }
157  }
158 
159  if (badGuys.empty())
160  return;
161 
162  // logging has been stopped so we need to use iostream...
163  cerr<<"Error: Not all threads were shut down properly: "<<endl;
164  for (it = badGuys.begin(); it != badGuys.end(); ++it)
165  {
166  cerr<<"Thread "<<qPrintable((*it)->objectName())
167  <<" is still running"<<endl;
168  }
169  cerr<<endl;
170 
171  static const int kTimeout = 5000;
172  MythTimer t;
173  t.start();
174  for (it = badGuys.begin();
175  it != badGuys.end() && t.elapsed() < kTimeout; ++it)
176  {
177  int left = kTimeout - t.elapsed();
178  if (left > 0)
179  (*it)->wait(left);
180  }
181 }
182 
183 void MThread::GetAllThreadNames(QStringList &list)
184 {
185  QMutexLocker locker(&s_all_threads_lock);
186  QSet<MThread*>::const_iterator it;
187  for (it = s_all_threads.begin(); it != s_all_threads.end(); ++it)
188  list.push_back((*it)->objectName());
189 }
190 
191 void MThread::GetAllRunningThreadNames(QStringList &list)
192 {
193  QMutexLocker locker(&s_all_threads_lock);
194  QSet<MThread*>::const_iterator it;
195  for (it = s_all_threads.begin(); it != s_all_threads.end(); ++it)
196  {
197  if ((*it)->isRunning())
198  list.push_back((*it)->objectName());
199  }
200 }
201 
203 {
204  if (QThread::currentThread() != m_thread)
205  {
206  LOG(VB_GENERAL, LOG_CRIT,
207  "RunProlog can only be executed in the run() method of a thread.");
208  return;
209  }
210  setTerminationEnabled(false);
211  ThreadSetup(m_thread->objectName());
212  m_prologExecuted = true;
213 }
214 
216 {
217  if (QThread::currentThread() != m_thread)
218  {
219  LOG(VB_GENERAL, LOG_CRIT,
220  "RunEpilog can only be executed in the run() method of a thread.");
221  return;
222  }
223  ThreadCleanup();
224  m_epilogExecuted = true;
225 }
226 
227 void MThread::ThreadSetup(const QString &name)
228 {
229  loggingRegisterThread(name);
230 #if QT_VERSION < QT_VERSION_CHECK(5,10,0)
231  qsrand(MythDate::current().toSecsSinceEpoch() ^ QTime::currentTime().msec());
232 #endif
233 }
234 
236 {
237  if (GetMythDB() && GetMythDB()->GetDBManager())
238  GetMythDB()->GetDBManager()->CloseDatabases();
240 }
241 
242 QThread *MThread::qthread(void)
243 {
244  return m_thread;
245 }
246 
247 void MThread::setObjectName(const QString &name)
248 {
249  m_thread->setObjectName(name);
250 }
251 
252 QString MThread::objectName(void) const
253 {
254  return m_thread->objectName();
255 }
256 
257 void MThread::setPriority(QThread::Priority priority)
258 {
259  m_thread->setPriority(priority);
260 }
261 
262 QThread::Priority MThread::priority(void) const
263 {
264  return m_thread->priority();
265 }
266 
267 bool MThread::isFinished(void) const
268 {
269  return m_thread->isFinished();
270 }
271 
272 bool MThread::isRunning(void) const
273 {
274  return m_thread->isRunning();
275 }
276 
278 {
279  m_thread->setStackSize(stackSize);
280 }
281 
283 {
284  return m_thread->stackSize();
285 }
286 
287 void MThread::exit(int retcode)
288 {
289  m_thread->exit(retcode);
290 }
291 
292 void MThread::start(QThread::Priority p)
293 {
294  m_prologExecuted = false;
295  m_epilogExecuted = false;
296  m_thread->start(p);
297 }
298 
300 {
301  m_thread->terminate();
302 }
303 
304 void MThread::quit(void)
305 {
306  m_thread->quit();
307 }
308 
309 bool MThread::wait(unsigned long time)
310 {
311  if (m_thread->isRunning())
312  return m_thread->wait(time);
313  return true;
314 }
315 
316 void MThread::run(void)
317 {
318  RunProlog();
319  if (m_runnable)
320  m_runnable->run();
321  else
322  m_thread->QThreadRun();
323  RunEpilog();
324 }
325 
326 int MThread::exec(void)
327 {
328  return m_thread->exec();
329 }
330 
332 {
334 }
335 
336 void MThread::sleep(unsigned long time)
337 {
339 }
340 
341 void MThread::msleep(unsigned long time)
342 {
344 }
345 
346 void MThread::usleep(unsigned long time)
347 {
349 }
350 
351 /* 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:326
loggingDeregisterThread
void loggingDeregisterThread(void)
Deregister the current thread's name.
Definition: logging.cpp:789
MThread::msleep
static void msleep(unsigned long time)
Definition: mthread.cpp:341
MThreadInternal::exec
int exec(void)
Definition: mthread.cpp:82
MThread::MThread
MThread(const QString &objectName)
Standard constructor.
Definition: mthread.cpp:102
MThread::start
void start(QThread::Priority p=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
Definition: mthread.cpp:292
MThread::quit
void quit(void)
calls exit(0)
Definition: mthread.cpp:304
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:79
MThread::~MThread
virtual ~MThread()
Definition: mthread.cpp:119
DBPurgeHandler::timerEvent
void timerEvent(QTimerEvent *event) override
Definition: mthread.cpp:67
MThread::setTerminationEnabled
static void setTerminationEnabled(bool enabled=true)
Definition: mthread.cpp:331
MThread::terminate
void terminate(void)
Kill a thread unsafely.
Definition: mthread.cpp:299
MThread::m_prologExecuted
bool m_prologExecuted
Definition: mthread.h:138
MThread::setObjectName
void setObjectName(const QString &name)
Definition: mthread.cpp:247
arg
arg(title).arg(filename).arg(doDelete))
mythdbcon.h
MThreadInternal::SetTerminationEnabled
static void SetTerminationEnabled(bool enabled=true)
Definition: mthread.cpp:88
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:202
is_current_thread
bool is_current_thread(MThread *thread)
Use this to determine if you are in the named thread.
Definition: mthread.cpp:41
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
sleep
unsigned sleep(unsigned int x)
Definition: compat.h:159
mythdate.h
mythlogging.h
MThreadInternal
Definition: mthread.cpp:75
DBPurgeHandler::DBPurgeHandler
DBPurgeHandler()
Definition: mthread.cpp:63
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:770
MThread::setPriority
void setPriority(QThread::Priority priority)
Definition: mthread.cpp:257
hardwareprofile.i18n.t
t
Definition: i18n.py:36
MThreadInternal::MThreadInternal
MThreadInternal(MThread &parent)
Definition: mthread.cpp:78
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:242
MThread::RunEpilog
void RunEpilog(void)
Cleans up a thread's resources, call this if you reimplement run().
Definition: mthread.cpp:215
MThread::stackSize
uint stackSize(void) const
Definition: mthread.cpp:282
MThread::isFinished
bool isFinished(void) const
Definition: mthread.cpp:267
MThreadInternal::QThreadRun
void QThreadRun(void)
Definition: mthread.cpp:81
uint
unsigned int uint
Definition: compat.h:140
MThread::GetAllThreadNames
static void GetAllThreadNames(QStringList &list)
Definition: mthread.cpp:183
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:316
MThread::m_epilogExecuted
bool m_epilogExecuted
Definition: mthread.h:139
MThread::priority
QThread::Priority priority(void) const
Definition: mthread.cpp:262
MThread::setStackSize
void setStackSize(uint stackSize)
Definition: mthread.cpp:277
MThreadInternal::MSleep
static void MSleep(unsigned long time)
Definition: mthread.cpp:92
MThreadInternal::USleep
static void USleep(unsigned long time)
Definition: mthread.cpp:93
s_all_threads_lock
static QMutex s_all_threads_lock
Definition: mthread.cpp:99
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:191
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:287
MThread::isRunning
bool isRunning(void) const
Definition: mthread.cpp:272
MThread::m_thread
MThreadInternal * m_thread
Definition: mthread.h:136
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:145
MThread::objectName
QString objectName(void) const
Definition: mthread.cpp:252
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:235
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:227
s_all_threads
static QSet< MThread * > s_all_threads
Definition: mthread.cpp:100
MThread::wait
bool wait(unsigned long time=ULONG_MAX)
Wait for the MThread to exit, with a maximum timeout.
Definition: mthread.cpp:309
MThread::sleep
static void sleep(unsigned long time)
Definition: mthread.cpp:336
MThread::m_runnable
QRunnable * m_runnable
Definition: mthread.h:137
DBPurgeHandler::m_purgeTimer
int m_purgeTimer
Definition: mthread.cpp:72
MThreadInternal::m_parent
MThread & m_parent
Definition: mthread.cpp:96
MThreadInternal::Sleep
static void Sleep(unsigned long time)
Definition: mthread.cpp:91
DBPurgeHandler
Definition: mthread.cpp:60
MThread::usleep
static void usleep(unsigned long time)
Definition: mthread.cpp:346