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 <stephan.bergmann@allotropia.de>
Reviewed-by: Noel Grandin <noel.grandin@collabora.co.uk>
This commit is contained in:
Stephan Bergmann 2024-11-20 15:04:06 +01:00
parent e8a7423baa
commit 755b28d019
2 changed files with 99 additions and 2 deletions

View file

@ -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

View file

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