Kaydet (Commit) 5469ac13 authored tarafından Jan-Marek Glogowski's avatar Jan-Marek Glogowski Kaydeden (comit) pranavk

Prevent out-of-order LO event processing

There's a callback processing loop, introduced by
a7c84375, 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/46550Tested-by: 's avatarJenkins <ci@libreoffice.org>
Reviewed-by: 's avatarHenry Castro <hcastro@collabora.com>
Reviewed-by: 's avatarpranavk <pranavk@collabora.co.uk>
Tested-by: 's avatarpranavk <pranavk@collabora.co.uk>
üst 71cd37fa
......@@ -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;
......
......@@ -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;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment