summaryrefslogtreecommitdiff
path: root/vcl
diff options
context:
space:
mode:
authorNoel Grandin <noel@peralex.com>2015-12-18 10:08:35 +0200
committerNoel Grandin <noel@peralex.com>2015-12-18 10:53:13 +0200
commit60e67b387e79185a33eb07bc03b01cd6d0d0a56b (patch)
treeea2865821e4af156fdbe8619754c5444d379e392 /vcl
parent68f411fb19d265ba391e730d16051b5887a30aa3 (diff)
fix O(n^2) in vcl event broadcasting tdf#90199
Change-Id: If3d7514364589058334369432cdcf4f7586c239d
Diffstat (limited to 'vcl')
-rw-r--r--vcl/inc/window.h3
-rw-r--r--vcl/source/window/event.cxx22
2 files changed, 20 insertions, 5 deletions
diff --git a/vcl/inc/window.h b/vcl/inc/window.h
index 5d792dea7890..e95036910657 100644
--- a/vcl/inc/window.h
+++ b/vcl/inc/window.h
@@ -39,6 +39,7 @@
#include <vcl/rendersettings.hxx>
#include "vcleventlisteners.hxx"
#include <vector>
+#include <set>
struct SalPaintEvent;
struct ImplDelData;
@@ -231,6 +232,8 @@ public:
VclPtr<vcl::Window> mpDlgCtrlDownWindow;
std::vector<Link<VclWindowEvent&,void>> maEventListeners;
std::vector<Link<VclWindowEvent&,void>> maChildEventListeners;
+ int maChildEventListenersIteratingCount;
+ std::set<Link<VclWindowEvent&,void>> maChildEventListenersDeleted;
// The canvas interface for this VCL window. Is persistent after the first GetCanvas() call
css::uno::WeakReference< css::rendering::XCanvas > mxCanvas;
diff --git a/vcl/source/window/event.cxx b/vcl/source/window/event.cxx
index f90ac286d3ea..c001c7e9ca43 100644
--- a/vcl/source/window/event.cxx
+++ b/vcl/source/window/event.cxx
@@ -30,6 +30,7 @@
#include <com/sun/star/awt/MouseEvent.hpp>
#include <com/sun/star/awt/KeyModifier.hpp>
#include <com/sun/star/awt/MouseButton.hpp>
+#include <comphelper/scopeguard.hxx>
namespace vcl {
@@ -238,17 +239,26 @@ void Window::CallEventListeners( sal_uLong nEvent, void* pData )
if ( aDelData.IsDead() )
return;
- auto& rChildListeners = pWindow->mpWindowImpl->maChildEventListeners;
- if (!rChildListeners.empty())
+ auto& rWindowImpl = *pWindow->mpWindowImpl;
+ if (!rWindowImpl.maChildEventListeners.empty())
{
// Copy the list, because this can be destroyed when calling a Link...
- std::vector<Link<VclWindowEvent&,void>> aCopy( rChildListeners );
+ std::vector<Link<VclWindowEvent&,void>> aCopy( rWindowImpl.maChildEventListeners );
+ // we use an iterating counter/flag and a set of deleted Link's to avoid O(n^2) behaviour
+ rWindowImpl.maChildEventListenersIteratingCount++;
+ comphelper::ScopeGuard aGuard(
+ [&rWindowImpl]()
+ {
+ if (--rWindowImpl.maChildEventListenersIteratingCount == 0)
+ rWindowImpl.maChildEventListenersDeleted.clear();
+ }
+ );
for ( Link<VclWindowEvent&,void>& rLink : aCopy )
{
if (aDelData.IsDead())
return;
- // check this hasn't been removed in some re-enterancy scenario fdo#47368
- if( std::find(rChildListeners.begin(), rChildListeners.end(), rLink) != rChildListeners.end() )
+ // Check this hasn't been removed in some re-enterancy scenario fdo#47368.
+ if( rWindowImpl.maChildEventListenersDeleted.find(rLink) != rWindowImpl.maChildEventListenersDeleted.end() )
rLink.Call( aEvent );
}
}
@@ -292,6 +302,8 @@ void Window::RemoveChildEventListener( const Link<VclWindowEvent&,void>& rEventL
{
auto& rListeners = mpWindowImpl->maChildEventListeners;
rListeners.erase( std::remove(rListeners.begin(), rListeners.end(), rEventListener ), rListeners.end() );
+ if (mpWindowImpl->maChildEventListenersIteratingCount)
+ mpWindowImpl->maChildEventListenersDeleted.insert(rEventListener);
}
}