include/comphelper/interfacecontainer4.hxx |   95 +++++++++++++++++++++++++++++
 sfx2/source/notify/globalevents.cxx        |    6 +
 2 files changed, 99 insertions(+), 2 deletions(-)

New commits:
commit 755b28d019db62d29ef36535a501809912182960
Author:     Stephan Bergmann <[email protected]>
AuthorDate: Wed Nov 20 15:04:06 2024 +0100
Commit:     Stephan Bergmann <[email protected]>
CommitDate: Wed Nov 20 17:14:14 2024 +0100

    Catch exceptions thrown when notifying individual listeners
    
    Any such exceptions were already caught further up the stack (by
    SfxBaseModel::postEvent_Impl), but prevented later listeners from being 
notified
    as soon as one listener threw an exception (which I saw happen with some
    3rd-party extension).
    
    Change-Id: Ia6bd1c73d29ab6d6e131652df51939ba0c0e988e
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/176854
    Tested-by: Jenkins
    Reviewed-by: Stephan Bergmann <[email protected]>
    Reviewed-by: Noel Grandin <[email protected]>

diff --git a/include/comphelper/interfacecontainer4.hxx 
b/include/comphelper/interfacecontainer4.hxx
index e4a41e30675d..c23c04c76018 100644
--- a/include/comphelper/interfacecontainer4.hxx
+++ b/include/comphelper/interfacecontainer4.hxx
@@ -204,6 +204,26 @@ public:
     template <typename FuncT>
     inline void forEach(std::unique_lock<std::mutex>& rGuard, FuncT const& 
func) const;
 
+    /** Executes a functor for each contained listener of specified type, e.g.
+        <code>forEach<awt::XPaintListener>(...</code>.
+
+        If a css::lang::DisposedException occurs which relates to
+        the called listener, then that listener is removed from the container.
+
+        If any other UNO exception occurs, the exceptionFunc is called.
+
+        @tparam FuncT unary functor type, let your compiler deduce this for you
+        @tparam ExceptionFuncT nullary functor type, let your compiler deduce 
this for you
+        @param func unary functor object expecting an argument of type
+                    css::uno::Reference<ListenerT>
+        @param exceptionFunc nullary functor object
+        @param rGuard
+            this parameter only here to make that this container is accessed 
while locked
+    */
+    template <typename FuncT, typename ExceptionFuncT>
+    inline void forEach(std::unique_lock<std::mutex>& rGuard, FuncT const& 
func,
+                        ExceptionFuncT const& exceptionFunc) const;
+
     /** Calls a UNO listener method for each contained listener.
 
         The listener method must take a single argument of type EventT,
@@ -231,6 +251,31 @@ public:
                            void (SAL_CALL 
ListenerT::*NotificationMethod)(const EventT&),
                            const EventT& Event) const;
 
+    /** Calls a UNO listener method for each contained listener.
+
+        The listener method must take a single argument of type EventT,
+        and return <code>void</code>.
+
+        If a css::lang::DisposedException occurs which relates to
+        the called listener, then that listener is removed from the container.
+
+        If any other UNO exception occurs, the exceptionFunc is called.
+
+        @tparam EventT event type, let your compiler deduce this for you
+        @tparam ExceptionFuncT nullary functor type, let your compiler deduce 
this for you
+        @param NotificationMethod
+            Pointer to a method of a ListenerT interface.
+        @param Event
+            Event to notify to all contained listeners
+        @param exceptionFunc nullary functor object
+        @param rGuard
+            this parameter only here to make that this container is accessed 
while locked
+    */
+    template <typename EventT, typename ExceptionFuncT>
+    inline void notifyEach(std::unique_lock<std::mutex>& rGuard,
+                           void (SAL_CALL 
ListenerT::*NotificationMethod)(const EventT&),
+                           const EventT& Event, const ExceptionFuncT& 
exceptionFunc) const;
+
     // this is moveable, but not copyable
     OInterfaceContainerHelper4(OInterfaceContainerHelper4&&) = default;
     OInterfaceContainerHelper4& operator=(OInterfaceContainerHelper4&&) = 
default;
@@ -316,6 +361,45 @@ inline void 
OInterfaceContainerHelper4<T>::forEach(std::unique_lock<std::mutex>&
     rGuard.lock();
 }
 
+template <class T>
+template <typename FuncT, typename ExceptionFuncT>
+inline void 
OInterfaceContainerHelper4<T>::forEach(std::unique_lock<std::mutex>& rGuard,
+                                                   FuncT const& func,
+                                                   ExceptionFuncT const& 
exceptionFunc) const
+{
+    assert(rGuard.owns_lock());
+    if (std::as_const(maData)->empty())
+    {
+        return;
+    }
+    const_cast<OInterfaceContainerHelper4&>(*this)
+        .maData.make_unique(); // so we can iterate over the data without 
holding the lock
+    OInterfaceIteratorHelper4<T> iter(rGuard, 
const_cast<OInterfaceContainerHelper4&>(*this));
+    rGuard.unlock();
+    while (iter.hasMoreElements())
+    {
+        auto xListener = iter.next();
+        try
+        {
+            func(xListener);
+        }
+        catch (css::lang::DisposedException const& exc)
+        {
+            if (exc.Context == xListener)
+            {
+                rGuard.lock();
+                iter.remove(rGuard);
+                rGuard.unlock();
+            }
+        }
+        catch (css::uno::Exception)
+        {
+            exceptionFunc();
+        }
+    }
+    rGuard.lock();
+}
+
 template <class ListenerT>
 template <typename EventT>
 inline void OInterfaceContainerHelper4<ListenerT>::notifyEach(
@@ -326,6 +410,17 @@ inline void 
OInterfaceContainerHelper4<ListenerT>::notifyEach(
                                           
NotifySingleListener<EventT>(NotificationMethod, Event));
 }
 
+template <class ListenerT>
+template <typename EventT, typename ExceptionFuncT>
+inline void OInterfaceContainerHelper4<ListenerT>::notifyEach(
+    std::unique_lock<std::mutex>& rGuard,
+    void (SAL_CALL ListenerT::*NotificationMethod)(const EventT&), const 
EventT& Event,
+    const ExceptionFuncT& exceptionFunc) const
+{
+    forEach<NotifySingleListener<EventT>>(
+        rGuard, NotifySingleListener<EventT>(NotificationMethod, Event), 
exceptionFunc);
+}
+
 template <class ListenerT>
 sal_Int32
 OInterfaceContainerHelper4<ListenerT>::getLength(std::unique_lock<std::mutex>& 
rGuard) const
diff --git a/sfx2/source/notify/globalevents.cxx 
b/sfx2/source/notify/globalevents.cxx
index bf440b5ce215..ae05b7c93911 100644
--- a/sfx2/source/notify/globalevents.cxx
+++ b/sfx2/source/notify/globalevents.cxx
@@ -482,9 +482,11 @@ void SfxGlobalEvents_Impl::implts_notifyListener(const 
document::DocumentEvent&
 
     document::EventObject aLegacyEvent(aEvent.Source, aEvent.EventName);
     m_aLegacyListeners.notifyEach(g,
-        &document::XEventListener::notifyEvent, aLegacyEvent);
+        &document::XEventListener::notifyEvent, aLegacyEvent,
+        [] { TOOLS_WARN_EXCEPTION("sfx.notify", "ignoring"); });
     m_aDocumentListeners.notifyEach(g,
-        &document::XDocumentEventListener::documentEventOccured, aEvent);
+        &document::XDocumentEventListener::documentEventOccured, aEvent,
+        [] { TOOLS_WARN_EXCEPTION("sfx.notify", "ignoring"); });
 }
 
 

Reply via email to