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"); }); }
