From aae5b1632264a6a97c2bd37da2e5899779e1b108 Mon Sep 17 00:00:00 2001 From: David Tardon Date: Tue, 10 May 2016 09:00:20 +0200 Subject: [PATCH] improve perf. of VCL event dispatch Change-Id: I5052f0c3e2c8739b336da52ef9590e5008255247 --- vcl/inc/window.h | 4 +++- vcl/source/window/event.cxx | 36 +++++++++++++++++++++++++++++++++--- vcl/source/window/window.cxx | 1 + 3 files changed, 37 insertions(+), 4 deletions(-) diff --git a/vcl/inc/window.h b/vcl/inc/window.h index 67d5f94..60f7e82 100644 --- a/vcl/inc/window.h +++ b/vcl/inc/window.h @@ -224,7 +224,9 @@ public: VclPtr mpNextOverlap; VclPtr mpLastFocusWindow; VclPtr mpDlgCtrlDownWindow; - VclEventListeners maEventListeners; + std::vector> maEventListeners; + int mnEventListenersIteratingCount; + std::set> maEventListenersDeleted; VclEventListeners maChildEventListeners; // The canvas interface for this VCL window. Is persistent after the first GetCanvas() call diff --git a/vcl/source/window/event.cxx b/vcl/source/window/event.cxx index 35c3e38..59fcb61 100644 --- a/vcl/source/window/event.cxx +++ b/vcl/source/window/event.cxx @@ -18,6 +18,7 @@ */ #include +#include #include #include #include @@ -27,6 +28,8 @@ #include #include +#include + #include #include #include @@ -212,7 +215,29 @@ void Window::CallEventListeners( sal_uLong nEvent, void* pData ) if ( aDelData.IsDead() ) return; - mpWindowImpl->maEventListeners.Call( &aEvent ); + if (!mpWindowImpl->maEventListeners.empty()) + { + // Copy the list, because this can be destroyed when calling a Link... + std::vector> aCopy( mpWindowImpl->maEventListeners ); + // we use an iterating counter/flag and a set of deleted Link's to avoid O(n^2) behaviour + mpWindowImpl->mnEventListenersIteratingCount++; + auto& rWindowImpl = *mpWindowImpl; + comphelper::ScopeGuard aGuard( + [&rWindowImpl]() + { + rWindowImpl.mnEventListenersIteratingCount--; + if (rWindowImpl.mnEventListenersIteratingCount == 0) + rWindowImpl.maEventListenersDeleted.clear(); + } + ); + for ( Link<>& rLink : aCopy ) + { + if (aDelData.IsDead()) break; + // check this hasn't been removed in some re-enterancy scenario fdo#47368 + if( rWindowImpl.maEventListenersDeleted.find(rLink) == rWindowImpl.maEventListenersDeleted.end() ) + rLink.Call( &aEvent ); + } + } if ( aDelData.IsDead() ) return; @@ -245,13 +270,18 @@ void Window::FireVclEvent( VclSimpleEvent* pEvent ) void Window::AddEventListener( const Link<>& rEventListener ) { - mpWindowImpl->maEventListeners.addListener( rEventListener ); + mpWindowImpl->maEventListeners.push_back( rEventListener ); } void Window::RemoveEventListener( const Link<>& rEventListener ) { if (mpWindowImpl) - mpWindowImpl->maEventListeners.removeListener( rEventListener ); + { + auto& rListeners = mpWindowImpl->maEventListeners; + rListeners.erase( std::remove(rListeners.begin(), rListeners.end(), rEventListener ), rListeners.end() ); + if (mpWindowImpl->mnEventListenersIteratingCount) + mpWindowImpl->maEventListenersDeleted.insert(rEventListener); + } } void Window::AddChildEventListener( const Link<>& rEventListener ) diff --git a/vcl/source/window/window.cxx b/vcl/source/window/window.cxx index e8d2b96..cbfc4cd 100644 --- a/vcl/source/window/window.cxx +++ b/vcl/source/window/window.cxx @@ -630,6 +630,7 @@ WindowImpl::WindowImpl( WindowType nType ) mpLastFocusWindow = NULL; // window for focus restore mpDlgCtrlDownWindow = NULL; // window for dialog control mpFirstDel = NULL; // Dtor notification list + mnEventListenersIteratingCount = 0; mpUserData = NULL; // user data mpCursor = NULL; // cursor mpControlFont = NULL; // font properties -- 2.7.4