Kaydet (Commit) 60e67b38 authored tarafından Noel Grandin's avatar Noel Grandin

fix O(n^2) in vcl event broadcasting tdf#90199

Change-Id: If3d7514364589058334369432cdcf4f7586c239d
üst 68f411fb
......@@ -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;
......
......@@ -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);
}
}
......
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