summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJan-Marek Glogowski <glogow@fbihome.de>2017-12-15 13:39:32 +0100
committerpranavk <pranavk@collabora.co.uk>2017-12-28 14:01:38 +0100
commit794b6bf95f0dc2cff2b9d04651e8d14529f2f518 (patch)
tree9db9445fdc99bfcac9b6bd1da9ef44696ddc3ed0
parentfeb476f67a906d01697d4e5b9ffca5b8da2fb987 (diff)
Prevent out-of-order LO event processing
There's a callback processing loop, introduced by a7c84375db517769035080c8fed33b2f303fc42f, which releases the SolarMutex and is triggered by a queued user event. Such a scenario can easily be reproduced by any LOK client resulting in hitting the empty user event processing list assertion. I'm not sure this should be handled via LO events at all. So this - again - gets rid of the the assertion and tries to prevent processing the user events out-of-order. In the case of giving up the SolarMutex while processing a user event an other thread or even nested loop can "steal" the user event list and continue processing. Most VCL backends run the event loop just in the main process, so for them this scenario is guaranteed. But the headless backend - running without UI or from LOK - is still allowed to process events in any thread. This is harder to fix and probably should use the same solution the gtk* backends use. This also changes the dequeues into lists to use splice for appending them. Change-Id: Id4a93a01dea415271ad96098830f18f06d4a75b9 Reviewed-on: https://gerrit.libreoffice.org/46550 Tested-by: Jenkins <ci@libreoffice.org> Reviewed-by: Henry Castro <hcastro@collabora.com> Reviewed-by: pranavk <pranavk@collabora.co.uk> Tested-by: pranavk <pranavk@collabora.co.uk> (cherry picked from commit 5469ac13b13e458904900539e6542d4a83d44c4e) Reviewed-on: https://gerrit.libreoffice.org/46890
-rw-r--r--vcl/inc/salusereventlist.hxx8
-rw-r--r--vcl/source/app/salusereventlist.cxx41
2 files changed, 32 insertions, 17 deletions
diff --git a/vcl/inc/salusereventlist.hxx b/vcl/inc/salusereventlist.hxx
index 123b9dadd1c7..52a195a68f82 100644
--- a/vcl/inc/salusereventlist.hxx
+++ b/vcl/inc/salusereventlist.hxx
@@ -23,10 +23,11 @@
#include <sal/config.h>
#include <vcl/dllapi.h>
#include <osl/mutex.hxx>
+#include <osl/thread.hxx>
#include <assert.h>
-#include <deque>
+#include <list>
#include <unordered_set>
class SalFrame;
@@ -65,10 +66,11 @@ public:
protected:
mutable osl::Mutex m_aUserEventsMutex;
- std::deque< SalUserEvent > m_aUserEvents;
- std::deque< SalUserEvent > m_aProcessingUserEvents;
+ std::list< SalUserEvent > m_aUserEvents;
+ std::list< SalUserEvent > m_aProcessingUserEvents;
bool m_bAllUserEventProcessedSignaled;
SalFrameSet m_aFrames;
+ oslThreadIdentifier m_aProcessingThread;
virtual void ProcessEvent( SalUserEvent aEvent ) = 0;
virtual void TriggerUserEventProcessing() = 0;
diff --git a/vcl/source/app/salusereventlist.cxx b/vcl/source/app/salusereventlist.cxx
index 50ef1f892002..bd58a508464f 100644
--- a/vcl/source/app/salusereventlist.cxx
+++ b/vcl/source/app/salusereventlist.cxx
@@ -33,6 +33,7 @@
SalUserEventList::SalUserEventList()
: m_bAllUserEventProcessedSignaled( true )
+ , m_aProcessingThread(0)
{
}
@@ -57,30 +58,40 @@ void SalUserEventList::eraseFrame( SalFrame* pFrame )
bool SalUserEventList::DispatchUserEvents( bool bHandleAllCurrentEvents )
{
bool bWasEvent = false;
+ oslThreadIdentifier aCurId = osl::Thread::getCurrentIdentifier();
+ DBG_TESTSOLARMUTEX();
+ // cleared after we pop a single event and are save in the 2nd guard.
+ // this way we guarantee to process at least one event, if available.
+ osl::ResettableMutexGuard aResettableGuard(m_aUserEventsMutex);
+
+ if (!m_aUserEvents.empty())
{
- osl::MutexGuard aGuard( m_aUserEventsMutex );
- assert( m_aProcessingUserEvents.empty() );
- if( ! m_aUserEvents.empty() )
+ if (bHandleAllCurrentEvents)
{
- if( bHandleAllCurrentEvents )
- m_aProcessingUserEvents.swap( m_aUserEvents );
+ if (m_aProcessingUserEvents.empty())
+ m_aProcessingUserEvents.swap(m_aUserEvents);
else
- {
- m_aProcessingUserEvents.push_back( m_aUserEvents.front() );
- m_aUserEvents.pop_front();
- }
- bWasEvent = true;
+ m_aProcessingUserEvents.splice(m_aProcessingUserEvents.end(), m_aUserEvents);
+ }
+ else if (m_aProcessingUserEvents.empty())
+ {
+ m_aProcessingUserEvents.push_back( m_aUserEvents.front() );
+ m_aUserEvents.pop_front();
}
}
- if( bWasEvent )
+ if (HasUserEvents())
{
+ bWasEvent = true;
+ m_aProcessingThread = aCurId;
+
SalUserEvent aEvent( nullptr, nullptr, SalEvent::NONE );
do {
{
- osl::MutexGuard aGuard( m_aUserEventsMutex );
- if ( m_aProcessingUserEvents.empty() )
+ osl::MutexGuard aGuard(m_aUserEventsMutex);
+ aResettableGuard.clear();
+ if (m_aProcessingUserEvents.empty() || aCurId != m_aProcessingThread)
break;
aEvent = m_aProcessingUserEvents.front();
m_aProcessingUserEvents.pop_front();
@@ -113,11 +124,13 @@ bool SalUserEventList::DispatchUserEvents( bool bHandleAllCurrentEvents )
SAL_WARN("vcl", "Uncaught exception during DispatchUserEvents!");
std::abort();
}
+ if (!bHandleAllCurrentEvents)
+ break;
}
while( true );
+ aResettableGuard.reset();
}
- osl::MutexGuard aGuard( m_aUserEventsMutex );
if ( !m_bAllUserEventProcessedSignaled && !HasUserEvents() )
{
m_bAllUserEventProcessedSignaled = true;