1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
|
/***************************************************************************
libkwave/WorkerThread.cpp - worker thread for Kwave
-------------------
begin : Sun Apr 06 2008
copyright : (C) 2008 by Thomas Eschenbacher
email : Thomas.Eschenbacher@gmx.de
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "config.h"
#include <signal.h>
#include <QApplication>
#include <QtGlobal> // for qWarning()
#undef DEBUG_FIND_DEADLOCKS
#ifdef DEBUG_FIND_DEADLOCKS
#include <execinfo.h> // for backtrace()
#endif
#include <errno.h>
#include "libkwave/Runnable.h"
#include "libkwave/WorkerThread.h"
/** maximum number of attempts to stop the worker thread */
#define MAX_ATTEMPTS_TO_STOP 8
/** set to true once a handler for SIGHUP is in place */
static bool g_signal_handler_is_in_place = false;
//***************************************************************************
extern "C" void _dummy_SIGHUP_handler(int);
//***************************************************************************
void _dummy_SIGHUP_handler(int)
{
qWarning("\r\n--- SIGHUP ---\r\n");
}
//***************************************************************************
Kwave::WorkerThread::WorkerThread(Kwave::Runnable *runnable, QVariant params)
:QThread(nullptr),
m_runnable(runnable),
m_params(params),
m_lock(), m_lock_sighup(),
m_tid(pthread_self()),
m_owner_tid(pthread_self())
{
/* NOTE: we assume that this gets called from the GUI thread only */
Q_ASSERT(this->thread() == QThread::currentThread());
Q_ASSERT(this->thread() == qApp->thread());
/* install handler for SIGHUP */
if (!g_signal_handler_is_in_place) {
/*
* NOTE: the old signal handler is lost. But as long as we do not use
* SIGHUP in any other place of the application and we need to
* have a dummy handler only, that should not matter
*/
signal(SIGHUP, _dummy_SIGHUP_handler);
g_signal_handler_is_in_place = true;
}
}
//***************************************************************************
Kwave::WorkerThread::~WorkerThread()
{
if (isRunning()) {
qDebug("WorkerThread::~WorkerThread(): waiting for normal shutdown");
wait(2000);
qDebug("WorkerThread::~WorkerThread(): stopping");
stop(2000);
}
Q_ASSERT(!isRunning());
}
//***************************************************************************
void Kwave::WorkerThread::cancel()
{
requestInterruption();
emit sigCancel();
}
//***************************************************************************
int Kwave::WorkerThread::stop(unsigned int timeout)
{
QMutexLocker lock(&m_lock);
if (!isRunning()) return 0; // already down
if (timeout < 1000) timeout = 1000;
// set the "should stop" flag
requestInterruption();
// send one SIGHUP in advance
{
QMutexLocker _lock(&m_lock_sighup);
if (!pthread_equal(m_tid, m_owner_tid))
pthread_kill(m_tid, SIGHUP);
if (!isRunning()) return 0;
}
// try to stop cooperatively
if (!isRunning()) return 0;
wait(timeout/10);
if (!isRunning()) return 0;
// try to interrupt by HUP signal
qWarning("WorkerThread::stop(): sending SIGHUP");
for (unsigned int i = 0; i < MAX_ATTEMPTS_TO_STOP; i++) {
{
QMutexLocker _lock(&m_lock_sighup);
if (!isRunning()) return 0;
if (!pthread_equal(m_tid, m_owner_tid))
pthread_kill(m_tid, SIGHUP);
}
if (!isRunning()) return 0;
wait(timeout/10);
if (!isRunning()) return 0;
}
#ifdef DEBUG_FIND_DEADLOCKS
if (running()) {
qDebug("WorkerThread::stop(): pthread_self()=%08X",
(unsigned int)pthread_self());
void *buf[256];
size_t n = backtrace(buf, 256);
backtrace_symbols_fd(buf, n, 2);
}
#endif
qDebug("WorkerThread::stop(): canceling thread");
terminate();
return -1;
}
//***************************************************************************
void Kwave::WorkerThread::run()
{
Q_ASSERT(m_runnable);
if (!m_runnable) return;
/* get the POSIX thread ID, needed for sending SIGHUP */
{
QMutexLocker _lock(&m_lock_sighup);
m_tid = pthread_self();
}
/* call the run(...) function */
m_runnable->run_wrapper(m_params);
/* avoid sending any SIGHUP by setting the m_tid to "invalid" */
{
QMutexLocker _lock(&m_lock_sighup);
m_tid = m_owner_tid;
}
}
//***************************************************************************
//***************************************************************************
#include "moc_WorkerThread.cpp"
|