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