Fix sporadic kdesktop hang due to unsafe usage of asynchronous POSIX signals in the main GUI thread

pull/2/head
Timothy Pearson 9 years ago
parent 751c96f9b1
commit 85126bf580

@ -18,6 +18,7 @@
#include <tdelocale.h>
#include <tqfile.h>
#include <tqtimer.h>
#include <tqeventloop.h>
#include <dcopclient.h>
#include <assert.h>
@ -49,13 +50,13 @@ SaverEngine* m_masterSaverEngine = NULL;
static void sigusr1_handler(int)
{
if (m_masterSaverEngine) {
m_masterSaverEngine->slotLockProcessWaiting();
m_masterSaverEngine->m_threadHelperObject->slotLockProcessWaiting();
}
}
static void sigusr2_handler(int)
{
if (m_masterSaverEngine) {
m_masterSaverEngine->slotLockProcessFullyActivated();
m_masterSaverEngine->m_threadHelperObject->slotLockProcessFullyActivated();
}
}
static void sigttin_handler(int)
@ -114,6 +115,14 @@ SaverEngine::SaverEngine()
mXAutoLock = 0;
mEnabled = false;
m_helperThread = new TQEventLoopThread;
m_helperThread->start();
m_threadHelperObject = new SaverEngineThreadHelperObject;
m_threadHelperObject->moveToThread(m_helperThread);
connect(this, TQT_SIGNAL(terminateHelperThread()), m_threadHelperObject, TQT_SLOT(terminateThread()));
connect(m_threadHelperObject, TQT_SIGNAL(lockProcessWaiting()), this, TQT_SLOT(lockProcessWaiting()));
connect(m_threadHelperObject, TQT_SIGNAL(lockProcessFullyActivated()), this, TQT_SLOT(lockProcessFullyActivated()));
connect(&mLockProcess, TQT_SIGNAL(processExited(TDEProcess *)),
TQT_SLOT(lockProcessExited()));
@ -138,6 +147,13 @@ SaverEngine::SaverEngine()
kdDebug( 1204 ) << "Failed to start kdesktop_lock!" << endl;
}
// Prevent kdesktop_lock signals from being handled by the wrong (GUI) thread
sigemptyset(&mThreadBlockSet);
sigaddset(&mThreadBlockSet, SIGUSR1);
sigaddset(&mThreadBlockSet, SIGUSR2);
sigaddset(&mThreadBlockSet, SIGTTIN);
pthread_sigmask(SIG_BLOCK, &mThreadBlockSet, NULL);
dBusConnect();
}
@ -159,6 +175,11 @@ SaverEngine::~SaverEngine()
// Restore X screensaver parameters
XSetScreenSaver(tqt_xdisplay(), mXTimeout, mXInterval, mXBlanking,
mXExposures);
terminateHelperThread();
m_helperThread->wait();
delete m_threadHelperObject;
delete m_helperThread;
}
//---------------------------------------------------------------------------
@ -557,14 +578,19 @@ void SaverEngine::lockProcessExited()
}
}
void SaverEngine::slotLockProcessWaiting()
void SaverEngineThreadHelperObject::slotLockProcessWaiting()
{
// lockProcessWaiting cannot be called directly from a signal handler, as it will hang in certain obscure circumstances
// Instead we use a single-shot timer to immediately call lockProcessWaiting once control has returned to the Qt main loop
TQTimer::singleShot(0, this, SLOT(lockProcessWaiting()));
lockProcessWaiting();
}
void SaverEngineThreadHelperObject::slotLockProcessFullyActivated()
{
lockProcessFullyActivated();
}
void SaverEngine::slotLockProcessFullyActivated()
void SaverEngine::lockProcessFullyActivated()
{
mState = Saving;
@ -795,7 +821,8 @@ void SaverEngine::handleDBusSignal(const TQT_DBusMessage& msg) {
bool SaverEngine::waitForLockProcessStart() {
sigset_t new_mask;
sigset_t orig_mask;
sigset_t empty_mask;
sigemptyset(&empty_mask);
// wait for SIGUSR1, SIGUSR2, SIGTTIN, SIGCHLD
sigemptyset(&new_mask);
@ -804,30 +831,32 @@ bool SaverEngine::waitForLockProcessStart() {
sigaddset(&new_mask, SIGTTIN);
sigaddset(&new_mask, SIGCHLD);
sigprocmask(SIG_BLOCK, &new_mask, &orig_mask);
pthread_sigmask(SIG_BLOCK, &new_mask, NULL);
while ((mLockProcess.isRunning()) && (!mSaverProcessReady)) {
sigsuspend(&orig_mask);
sigsuspend(&empty_mask);
}
sigprocmask(SIG_UNBLOCK, &new_mask, NULL);
pthread_sigmask(SIG_UNBLOCK, &new_mask, NULL);
return mLockProcess.isRunning();
}
bool SaverEngine::waitForLockEngage() {
sigset_t new_mask;
sigset_t orig_mask;
sigset_t empty_mask;
sigemptyset(&empty_mask);
// wait for SIGUSR1, SIGUSR2, SIGTTIN
sigemptyset(&new_mask);
sigaddset(&new_mask, SIGUSR1);
sigaddset(&new_mask, SIGUSR2);
sigaddset(&new_mask, SIGTTIN);
sigprocmask(SIG_BLOCK, &new_mask, &orig_mask);
pthread_sigmask(SIG_BLOCK, &mThreadBlockSet, NULL);
while ((mLockProcess.isRunning()) && (mState != Waiting) && (mState != Saving)) {
sigsuspend(&orig_mask);
sigsuspend(&empty_mask);
}
sigprocmask(SIG_UNBLOCK, &new_mask, NULL);
pthread_sigmask(SIG_UNBLOCK, &mThreadBlockSet, NULL);
return mLockProcess.isRunning();
}
void SaverEngineThreadHelperObject::terminateThread() {
TQEventLoop* eventLoop = TQApplication::eventLoop();
if (eventLoop) {
eventLoop->exit(0);
}
}

@ -9,6 +9,7 @@
#define __LOCKENG_H__
#include <tqwidget.h>
#include <tqthread.h>
#include <kprocess.h>
#include <tqvaluevector.h>
#include "KScreensaverIface.h"
@ -21,6 +22,20 @@ class DCOPClientTransaction;
class TQT_DBusMessage;
class TQT_DBusProxy;
class SaverEngineThreadHelperObject : public TQObject
{
Q_OBJECT
public slots:
void terminateThread();
void slotLockProcessWaiting();
void slotLockProcessFullyActivated();
signals:
void lockProcessWaiting();
void lockProcessFullyActivated();
};
//===========================================================================
/**
* Screen saver engine. Handles screensaver window, starting screensaver
@ -91,16 +106,19 @@ public:
*/
bool waitForLockEngage();
signals:
void terminateHelperThread();
void asyncLock();
public slots:
void slotLockProcessWaiting();
void slotLockProcessFullyActivated();
void slotLockProcessReady();
void lockProcessWaiting();
void lockProcessFullyActivated();
void handleDBusSignal(const TQT_DBusMessage&);
protected slots:
void idleTimeout();
void lockProcessExited();
void lockProcessWaiting();
private slots:
void handleSecureDialog();
@ -148,7 +166,12 @@ protected:
bool mBlankOnly; // only use the blanker, not the defined saver
TQValueVector< DCOPClientTransaction* > mLockTransactions;
public:
SaverEngineThreadHelperObject* m_threadHelperObject;
private:
TQEventLoopThread* m_helperThread;
sigset_t mThreadBlockSet;
TDEProcess* mSAKProcess;
bool mTerminationRequested;
bool mSaverProcessReady;

Loading…
Cancel
Save