1 /*
2  * Cantata
3  *
4  * Copyright (c) 2011-2020 Craig Drummond <craig.p.drummond@gmail.com>
5  *
6  * ----
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; see the file COPYING.  If not, write to
20  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21  * Boston, MA 02110-1301, USA.
22  */
23 
24 #include "thread.h"
25 #include "globalstatic.h"
26 #include <QCoreApplication>
27 #include <QtGlobal>
28 #include <QTimer>
29 #include <QDebug>
30 #include <signal.h>
31 #ifndef _MSC_VER
32 #include <unistd.h>
33 #endif
34 
35 static bool debugEnabled=false;
36 #define DBUG if (debugEnabled) qWarning() << metaObject()->className() << __FUNCTION__
enableDebug()37 void ThreadCleaner::enableDebug()
38 {
39     debugEnabled=true;
40 }
41 
segvHandler(int i)42 static void segvHandler(int i)
43 {
44     if (debugEnabled) qWarning() << "SEGV handler called";
45     _exit(i);
46 }
47 
GLOBAL_STATIC(ThreadCleaner,instance)48 GLOBAL_STATIC(ThreadCleaner, instance)
49 
50 void ThreadCleaner::stopAll()
51 {
52     DBUG << "Remaining threads:" << threads.count();
53     for (Thread *thread: threads) {
54         DBUG << "Cleanup" << thread->objectName();
55         disconnect(thread, SIGNAL(finished()), this, SLOT(threadFinished()));
56     }
57 
58     for (Thread *thread: threads) {
59         thread->stop();
60     }
61 
62     QList<Thread *> stillRunning;
63     for (Thread *thread: threads) {
64         if (thread->wait(250)) {
65             delete thread;
66         } else {
67             stillRunning.append(thread);
68             DBUG << "Failed to close" << thread->objectName();
69         }
70     }
71 
72     // Terminate any still running threads...
73     signal(SIGSEGV, segvHandler); // Ignore SEGV in case a thread throws an error...
74     for (Thread *thread: stillRunning) {
75         thread->terminate();
76     }
77 }
78 
threadFinished()79 void ThreadCleaner::threadFinished()
80 {
81     Thread *thread=qobject_cast<Thread *>(sender());
82     if (thread) {
83         thread->deleteLater();
84         threads.removeAll(thread);
85         DBUG << "Thread finished" << thread->objectName() << "Total threads:" << threads.count();
86     }
87 }
88 
add(Thread * thread)89 void ThreadCleaner::add(Thread *thread)
90 {
91     threads.append(thread);
92     connect(thread, SIGNAL(finished()), this, SLOT(threadFinished()));
93     DBUG << "Thread created" << thread->objectName() << "Total threads:" << threads.count();
94 }
95 
Thread(const QString & name,QObject * p)96 Thread::Thread(const QString &name, QObject *p)
97     : QThread(p)
98 {
99     setObjectName(name);
100     ThreadCleaner::self()->add(this);
101 }
102 
~Thread()103 Thread::~Thread()
104 {
105     DBUG << objectName() << "destroyed";
106 }
107 
run()108 void Thread::run()
109 {
110     QThread::run();
111 }
112 
createTimer(QObject * parent)113 QTimer * Thread::createTimer(QObject *parent)
114 {
115     QTimer *timer=new QTimer(parent ? parent : this);
116     connect(this, SIGNAL(finished()), timer, SLOT(stop()));
117     return timer;
118 }
119 
deleteTimer(QTimer * timer)120 void Thread::deleteTimer(QTimer *timer)
121 {
122     if (timer) {
123         disconnect(this, SIGNAL(finished()), timer, SLOT(stop()));
124         timer->deleteLater();
125     }
126 }
127 
128 #include "moc_thread.cpp"
129