MythTV  0.26-pre
mthread.cpp
Go to the documentation of this file.
00001 /*  -*- Mode: c++ -*-
00002  *
00003  *   Class MThread
00004  *
00005  *   Copyright (C) Daniel Kristjansson 2011
00006  *
00007  *   This program is free software; you can redistribute it and/or modify
00008  *   it under the terms of the GNU General Public License as published by
00009  *   the Free Software Foundation; either version 2 of the License, or
00010  *   (at your option) any later version.
00011  *
00012  *   This program is distributed in the hope that it will be useful,
00013  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  *   GNU General Public License for more details.
00016  *
00017  *   You should have received a copy of the GNU General Public License
00018  *   along with this program; if not, write to the Free Software
00019  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00020  */
00021 
00022 #include <iostream>
00023 using namespace std;
00024 
00025 // Qt headers
00026 #include <QStringList>
00027 #include <QTimerEvent>
00028 #include <QRunnable>
00029 #include <QtGlobal>
00030 #include <QSet>
00031 
00032 // MythTV headers
00033 #include "mythlogging.h"
00034 #include "mythdbcon.h"
00035 #include "mthread.h"
00036 #include "mythtimer.h"
00037 #include "logging.h"
00038 #include "mythdb.h"
00039 
00040 bool is_current_thread(MThread *thread)
00041 {
00042     if (!thread)
00043         return false;
00044     return QThread::currentThread() == thread->qthread();
00045 }
00046 
00047 bool is_current_thread(QThread *thread)
00048 {
00049     if (!thread)
00050         return false;
00051     return QThread::currentThread() == thread;
00052 }
00053 
00054 bool is_current_thread(MThread &thread)
00055 {
00056     return QThread::currentThread() == thread.qthread();
00057 }
00058 
00059 class DBPurgeHandler : public QObject
00060 {
00061   public:
00062     DBPurgeHandler()
00063     {
00064         purgeTimer = startTimer(5 * 60000);
00065     }
00066     void timerEvent(QTimerEvent *event)
00067     {
00068         if (event->timerId() == purgeTimer)
00069             GetMythDB()->GetDBManager()->PurgeIdleConnections(false);
00070     }
00071     int purgeTimer;
00072 };
00073 
00074 class MThreadInternal : public QThread
00075 {
00076   public:
00077     MThreadInternal(MThread &parent) : m_parent(parent) {}
00078     virtual void run(void) { m_parent.run(); }
00079 
00080     void QThreadRun(void) { QThread::run(); }
00081     int exec(void)
00082     {
00083         DBPurgeHandler ph();
00084         return QThread::exec();
00085     }
00086 
00087     static void SetTerminationEnabled(bool enabled = true)
00088     { QThread::setTerminationEnabled(enabled); }
00089 
00090     static void Sleep(unsigned long time) { QThread::sleep(time); }
00091     static void MSleep(unsigned long time) { QThread::msleep(time); }
00092     static void USleep(unsigned long time) { QThread::usleep(time); }
00093 
00094   private:
00095     MThread &m_parent;
00096 };
00097 
00098 static QMutex s_all_threads_lock;
00099 static QSet<MThread*> s_all_threads;
00100 
00101 MThread::MThread(const QString &objectName) :
00102     m_thread(new MThreadInternal(*this)), m_runnable(NULL),
00103     m_prolog_executed(true), m_epilog_executed(true)
00104 {
00105     m_thread->setObjectName(objectName);
00106     QMutexLocker locker(&s_all_threads_lock);
00107     s_all_threads.insert(this);
00108 }
00109 
00110 MThread::MThread(const QString &objectName, QRunnable *runnable) :
00111     m_thread(new MThreadInternal(*this)), m_runnable(runnable),
00112     m_prolog_executed(false), m_epilog_executed(false)
00113 {
00114     m_thread->setObjectName(objectName);
00115     QMutexLocker locker(&s_all_threads_lock);
00116     s_all_threads.insert(this);
00117 }
00118 
00119 MThread::~MThread()
00120 {
00121     if (!m_prolog_executed)
00122     {
00123         LOG(VB_GENERAL, LOG_CRIT, "MThread prolog was never run!");
00124     }
00125     if (!m_epilog_executed)
00126     {
00127         LOG(VB_GENERAL, LOG_CRIT, "MThread epilog was never run!");
00128     }
00129     if (m_thread->isRunning())
00130     {
00131         LOG(VB_GENERAL, LOG_CRIT,
00132             "MThread destructor called while thread still running!");
00133         m_thread->wait();
00134     }
00135 
00136     {
00137         QMutexLocker locker(&s_all_threads_lock);
00138         s_all_threads.remove(this);
00139     }
00140 
00141     delete m_thread;
00142     m_thread = NULL;
00143 }
00144 
00145 void MThread::Cleanup(void)
00146 {
00147     QMutexLocker locker(&s_all_threads_lock);
00148     QSet<MThread*> badGuys;
00149     QSet<MThread*>::const_iterator it;
00150     for (it = s_all_threads.begin(); it != s_all_threads.end(); ++it)
00151     {
00152         if ((*it)->isRunning())
00153         {
00154             badGuys.insert(*it);
00155             (*it)->exit(1);
00156         }
00157     }
00158 
00159     if (badGuys.empty())
00160         return;
00161 
00162     // logging has been stopped so we need to use iostream...
00163     cerr<<"Error: Not all threads were shut down properly: "<<endl;
00164     for (it = badGuys.begin(); it != badGuys.end(); ++it)
00165     {
00166         cerr<<"Thread "<<qPrintable((*it)->objectName())
00167             <<" is still running"<<endl;
00168     }
00169     cerr<<endl;
00170 
00171     static const int kTimeout = 5000;
00172     MythTimer t;
00173     t.start();
00174     for (it = badGuys.begin();
00175          it != badGuys.end() && t.elapsed() < kTimeout; ++it)
00176     {
00177         int left = kTimeout - t.elapsed();
00178         if (left > 0)
00179             (*it)->wait(left);
00180     }
00181 }
00182 
00183 void MThread::GetAllThreadNames(QStringList &list)
00184 {
00185     QMutexLocker locker(&s_all_threads_lock);
00186     QSet<MThread*>::const_iterator it;
00187     for (it = s_all_threads.begin(); it != s_all_threads.end(); ++it)
00188         list.push_back((*it)->objectName());
00189 }
00190 
00191 void MThread::GetAllRunningThreadNames(QStringList &list)
00192 {
00193     QMutexLocker locker(&s_all_threads_lock);
00194     QSet<MThread*>::const_iterator it;
00195     for (it = s_all_threads.begin(); it != s_all_threads.end(); ++it)
00196     {
00197         if ((*it)->isRunning())
00198             list.push_back((*it)->objectName());
00199     }
00200 }
00201 
00202 void MThread::RunProlog(void)
00203 {
00204     if (QThread::currentThread() != m_thread)
00205     {
00206         LOG(VB_GENERAL, LOG_CRIT,
00207             "RunProlog can only be executed in the run() method of a thread.");
00208         return;
00209     }
00210     setTerminationEnabled(false);
00211     ThreadSetup(m_thread->objectName());
00212     m_prolog_executed = true;
00213 }
00214 
00215 void MThread::RunEpilog(void)
00216 {
00217     if (QThread::currentThread() != m_thread)
00218     {
00219         LOG(VB_GENERAL, LOG_CRIT,
00220             "RunEpilog can only be executed in the run() method of a thread.");
00221         return;
00222     }
00223     ThreadCleanup();
00224     m_epilog_executed = true;
00225 }
00226 
00227 void MThread::ThreadSetup(const QString &name)
00228 {
00229     loggingRegisterThread(name);
00230     qsrand(QDateTime::currentDateTime().toTime_t() ^
00231            QTime::currentTime().msec());
00232 }
00233 
00234 void MThread::ThreadCleanup(void)
00235 {
00236     if (GetMythDB() && GetMythDB()->GetDBManager())
00237         GetMythDB()->GetDBManager()->CloseDatabases();
00238     loggingDeregisterThread();
00239 }
00240 
00241 QThread *MThread::qthread(void)
00242 {
00243     return m_thread;
00244 }
00245 
00246 void MThread::setObjectName(const QString &name)
00247 {
00248     m_thread->setObjectName(name);
00249 }
00250 
00251 QString MThread::objectName(void) const
00252 {
00253     return m_thread->objectName();
00254 }
00255 
00256 void MThread::setPriority(QThread::Priority priority)
00257 {
00258     m_thread->setPriority(priority);
00259 }
00260 
00261 QThread::Priority MThread::priority(void) const
00262 {
00263     return m_thread->priority();
00264 }
00265 
00266 bool MThread::isFinished(void) const
00267 {
00268     return m_thread->isFinished();
00269 }
00270 
00271 bool MThread::isRunning(void) const
00272 {
00273     return m_thread->isRunning();
00274 }
00275 
00276 void MThread::setStackSize(uint stackSize)
00277 {
00278     m_thread->setStackSize(stackSize);
00279 }
00280 
00281 uint MThread::stackSize(void) const
00282 {
00283     return m_thread->stackSize();
00284 }
00285 
00286 void MThread::exit(int retcode)
00287 {
00288     m_thread->exit(retcode);
00289 }
00290 
00291 void MThread::start(QThread::Priority p)
00292 {
00293     m_prolog_executed = false;
00294     m_epilog_executed = false;
00295     m_thread->start(p);
00296 }
00297 
00298 void MThread::terminate(void)
00299 {
00300     m_thread->terminate();
00301 }
00302 
00303 void MThread::quit(void)
00304 {
00305     m_thread->quit();
00306 }
00307 
00308 bool MThread::wait(unsigned long time)
00309 {
00310     if (m_thread->isRunning())
00311         return m_thread->wait(time);
00312     return true;
00313 }
00314 
00315 void MThread::run(void)
00316 {
00317     RunProlog();
00318     if (m_runnable)
00319         m_runnable->run();
00320     else
00321         m_thread->QThreadRun();
00322     RunEpilog();
00323 }
00324 
00325 int MThread::exec(void)
00326 {
00327     return m_thread->exec();
00328 }
00329 
00330 void MThread::setTerminationEnabled(bool enabled)
00331 {
00332     MThreadInternal::SetTerminationEnabled(enabled);
00333 }
00334 
00335 void MThread::sleep(unsigned long time)
00336 {
00337     MThreadInternal::Sleep(time);
00338 }
00339 
00340 void MThread::msleep(unsigned long time)
00341 {
00342     MThreadInternal::MSleep(time);
00343 }
00344 
00345 void MThread::usleep(unsigned long time)
00346 {
00347     MThreadInternal::USleep(time);
00348 }
00349 
00350 /* vim: set expandtab tabstop=4 shiftwidth=4: */
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends