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 <tdelocale.h>
#include <tqfile.h> #include <tqfile.h>
#include <tqtimer.h> #include <tqtimer.h>
#include <tqeventloop.h>
#include <dcopclient.h> #include <dcopclient.h>
#include <assert.h> #include <assert.h>
@ -49,13 +50,13 @@ SaverEngine* m_masterSaverEngine = NULL;
static void sigusr1_handler(int) static void sigusr1_handler(int)
{ {
if (m_masterSaverEngine) { if (m_masterSaverEngine) {
m_masterSaverEngine->slotLockProcessWaiting(); m_masterSaverEngine->m_threadHelperObject->slotLockProcessWaiting();
} }
} }
static void sigusr2_handler(int) static void sigusr2_handler(int)
{ {
if (m_masterSaverEngine) { if (m_masterSaverEngine) {
m_masterSaverEngine->slotLockProcessFullyActivated(); m_masterSaverEngine->m_threadHelperObject->slotLockProcessFullyActivated();
} }
} }
static void sigttin_handler(int) static void sigttin_handler(int)
@ -114,6 +115,14 @@ SaverEngine::SaverEngine()
mXAutoLock = 0; mXAutoLock = 0;
mEnabled = false; 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 *)), connect(&mLockProcess, TQT_SIGNAL(processExited(TDEProcess *)),
TQT_SLOT(lockProcessExited())); TQT_SLOT(lockProcessExited()));
@ -138,6 +147,13 @@ SaverEngine::SaverEngine()
kdDebug( 1204 ) << "Failed to start kdesktop_lock!" << endl; 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(); dBusConnect();
} }
@ -159,6 +175,11 @@ SaverEngine::~SaverEngine()
// Restore X screensaver parameters // Restore X screensaver parameters
XSetScreenSaver(tqt_xdisplay(), mXTimeout, mXInterval, mXBlanking, XSetScreenSaver(tqt_xdisplay(), mXTimeout, mXInterval, mXBlanking,
mXExposures); 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 // 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 // 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; mState = Saving;
@ -795,7 +821,8 @@ void SaverEngine::handleDBusSignal(const TQT_DBusMessage& msg) {
bool SaverEngine::waitForLockProcessStart() { bool SaverEngine::waitForLockProcessStart() {
sigset_t new_mask; sigset_t new_mask;
sigset_t orig_mask; sigset_t empty_mask;
sigemptyset(&empty_mask);
// wait for SIGUSR1, SIGUSR2, SIGTTIN, SIGCHLD // wait for SIGUSR1, SIGUSR2, SIGTTIN, SIGCHLD
sigemptyset(&new_mask); sigemptyset(&new_mask);
@ -804,30 +831,32 @@ bool SaverEngine::waitForLockProcessStart() {
sigaddset(&new_mask, SIGTTIN); sigaddset(&new_mask, SIGTTIN);
sigaddset(&new_mask, SIGCHLD); sigaddset(&new_mask, SIGCHLD);
sigprocmask(SIG_BLOCK, &new_mask, &orig_mask); pthread_sigmask(SIG_BLOCK, &new_mask, NULL);
while ((mLockProcess.isRunning()) && (!mSaverProcessReady)) { 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(); return mLockProcess.isRunning();
} }
bool SaverEngine::waitForLockEngage() { bool SaverEngine::waitForLockEngage() {
sigset_t new_mask; sigset_t empty_mask;
sigset_t orig_mask; sigemptyset(&empty_mask);
// wait for SIGUSR1, SIGUSR2, SIGTTIN // wait for SIGUSR1, SIGUSR2, SIGTTIN
sigemptyset(&new_mask); pthread_sigmask(SIG_BLOCK, &mThreadBlockSet, NULL);
sigaddset(&new_mask, SIGUSR1);
sigaddset(&new_mask, SIGUSR2);
sigaddset(&new_mask, SIGTTIN);
sigprocmask(SIG_BLOCK, &new_mask, &orig_mask);
while ((mLockProcess.isRunning()) && (mState != Waiting) && (mState != Saving)) { 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(); return mLockProcess.isRunning();
}
void SaverEngineThreadHelperObject::terminateThread() {
TQEventLoop* eventLoop = TQApplication::eventLoop();
if (eventLoop) {
eventLoop->exit(0);
}
} }

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

Loading…
Cancel
Save