Branch: master

85126bf5 2015-04-12 16:34:00 Timothy Pearson
Fix sporadic kdesktop hang due to unsafe usage of asynchronous POSIX signals in the main GUI thread
M kdesktop/lockeng.cc
M kdesktop/lockeng.h
diff --git a/kdesktop/lockeng.cc b/kdesktop/lockeng.cc
index 8e59b9e..6ce26cf 100644
--- a/kdesktop/lockeng.cc
+++ b/kdesktop/lockeng.cc
@@ -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 @@
 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 @@
 	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 @@
 		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 @@
 	// 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::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 SaverEngine::slotLockProcessFullyActivated()
+void SaverEngineThreadHelperObject::slotLockProcessFullyActivated()
+{
+	lockProcessFullyActivated();
+}
+
+void SaverEngine::lockProcessFullyActivated()
 {
 	mState = Saving;
 
@@ -795,7 +821,8 @@
 
 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 @@
 	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);
+	}
 }
\ No newline at end of file
diff --git a/kdesktop/lockeng.h b/kdesktop/lockeng.h
index 782f779..9827090 100644
--- a/kdesktop/lockeng.h
+++ b/kdesktop/lockeng.h
@@ -9,6 +9,7 @@
 #define __LOCKENG_H__
 
 #include <tqwidget.h>
+#include <tqthread.h>
 #include <kprocess.h>
 #include <tqvaluevector.h>
 #include "KScreensaverIface.h"
@@ -20,6 +21,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();
+};
 
 //===========================================================================
 /**
@@ -91,16 +106,19 @@
 	 */
 	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 @@
 	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;