5f9da6e0ee
some stuff was hiding inside templates Change-Id: I89fd1a926dd6bf96e35a74e5028165acae51c60c Reviewed-on: https://gerrit.libreoffice.org/c/core/+/162159 Tested-by: Jenkins Reviewed-by: Noel Grandin <noel.grandin@collabora.co.uk>
421 lines
15 KiB
C++
421 lines
15 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*
|
|
* This file is part of the LibreOffice project.
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*
|
|
* This file incorporates work covered by the following license notice:
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed
|
|
* with this work for additional information regarding copyright
|
|
* ownership. The ASF licenses this file to you under the Apache
|
|
* License, Version 2.0 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy of
|
|
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
|
|
*/
|
|
#pragma once
|
|
|
|
#include <sal/config.h>
|
|
|
|
#include <com/sun/star/lang/EventObject.hpp>
|
|
#include <com/sun/star/lang/DisposedException.hpp>
|
|
#include <o3tl/cow_wrapper.hxx>
|
|
#include <cassert>
|
|
#include <mutex>
|
|
#include <vector>
|
|
|
|
namespace com::sun::star::uno
|
|
{
|
|
class XInterface;
|
|
}
|
|
|
|
namespace comphelper
|
|
{
|
|
template <class ListenerT> class OInterfaceContainerHelper4;
|
|
/**
|
|
This is the iterator of an OInterfaceContainerHelper4. Typically
|
|
one constructs an instance on the stack for one firing session.
|
|
It is not allowed to assign or copy an instance of this class.
|
|
|
|
@tparam ListenerT UNO event listener type
|
|
@see OInterfaceContainerHelper4
|
|
*/
|
|
template <class ListenerT> class OInterfaceIteratorHelper4
|
|
{
|
|
public:
|
|
/**
|
|
Create an iterator over the elements of the container. The iterator
|
|
copies the elements of the container. A change to the container
|
|
during the lifetime of an iterator is allowed and does not
|
|
affect the iterator-instance. The iterator and the container take cares
|
|
themself for concurrent access, no additional guarding is necessary.
|
|
|
|
Remark: The copy is on demand. The iterator copy the elements only if the container
|
|
change the contents...
|
|
|
|
@param rCont the container of the elements.
|
|
@param rGuard
|
|
this parameter only here to make that this container is accessed while locked
|
|
*/
|
|
OInterfaceIteratorHelper4(std::unique_lock<std::mutex>& rGuard,
|
|
OInterfaceContainerHelper4<ListenerT>& rCont_)
|
|
: rCont(rCont_)
|
|
, maData(rCont.maData)
|
|
// const_cast so we don't trigger make_unique via o3tl::cow_wrapper::operator->
|
|
, nRemain(std::as_const(maData)->size())
|
|
{
|
|
assert(rGuard.owns_lock());
|
|
(void)rGuard;
|
|
}
|
|
|
|
/** Return true, if there are more elements in the iterator. */
|
|
bool hasMoreElements() const { return nRemain != 0; }
|
|
/** Return the next element of the iterator. Calling this method if
|
|
hasMoreElements() has returned false, is an error.
|
|
*/
|
|
css::uno::Reference<ListenerT> const& next();
|
|
|
|
/** Removes the current element (the last one returned by next())
|
|
from the underlying container. Calling this method before
|
|
next() has been called or calling it twice with no next()
|
|
in between is an error.
|
|
@param rGuard
|
|
this parameter only here to make that this container is accessed while locked
|
|
*/
|
|
void remove(::std::unique_lock<::std::mutex>& rGuard);
|
|
|
|
private:
|
|
OInterfaceContainerHelper4<ListenerT>& rCont;
|
|
o3tl::cow_wrapper<std::vector<css::uno::Reference<ListenerT>>,
|
|
o3tl::ThreadSafeRefCountingPolicy>
|
|
maData;
|
|
sal_Int32 nRemain;
|
|
|
|
OInterfaceIteratorHelper4(const OInterfaceIteratorHelper4&) = delete;
|
|
OInterfaceIteratorHelper4& operator=(const OInterfaceIteratorHelper4&) = delete;
|
|
};
|
|
|
|
template <class ListenerT>
|
|
const css::uno::Reference<ListenerT>& OInterfaceIteratorHelper4<ListenerT>::next()
|
|
{
|
|
nRemain--;
|
|
return (*std::as_const(maData))[nRemain];
|
|
}
|
|
|
|
template <class ListenerT>
|
|
void OInterfaceIteratorHelper4<ListenerT>::remove(::std::unique_lock<::std::mutex>& rGuard)
|
|
{
|
|
rCont.removeInterface(rGuard, (*std::as_const(maData))[nRemain]);
|
|
}
|
|
|
|
/**
|
|
A container of interfaces. To access the elements use an iterator.
|
|
This implementation is thread-safe.
|
|
|
|
This is a copy of the code at include/comphelper/interfacecontainer3.hxx,
|
|
except that it (a) uses std::mutex instead of osl::Mutex and (b) does not
|
|
store a reference to the mutex, but relies on the calling class to take
|
|
a lock around using it.
|
|
|
|
@tparam ListenerT UNO event listener type
|
|
@see OInterfaceIteratorHelper
|
|
*/
|
|
template <class ListenerT> class OInterfaceContainerHelper4
|
|
{
|
|
public:
|
|
OInterfaceContainerHelper4();
|
|
|
|
/**
|
|
Return the number of Elements in the container. Only useful if you have acquired
|
|
the mutex.
|
|
@param rGuard
|
|
this parameter only here to make that this container is accessed while locked
|
|
*/
|
|
sal_Int32 getLength(std::unique_lock<std::mutex>& rGuard) const;
|
|
|
|
/**
|
|
Return all interfaces added to this container.
|
|
@param rGuard
|
|
this parameter only here to make that this container is accessed while locked
|
|
**/
|
|
std::vector<css::uno::Reference<ListenerT>>
|
|
getElements(std::unique_lock<std::mutex>& rGuard) const;
|
|
|
|
/** Inserts an element into the container. The position is not specified, thus it is not
|
|
specified in which order events are fired.
|
|
|
|
@attention
|
|
If you add the same interface more than once, then it will be added to the elements list
|
|
more than once and thus if you want to remove that interface from the list, you have to call
|
|
removeInterface() the same number of times.
|
|
In the latter case, you will also get events fired more than once (if the interface is a
|
|
listener interface).
|
|
|
|
@param rxIFace
|
|
interface to be added; it is allowed to insert
|
|
the same interface more than once
|
|
@param rGuard
|
|
this parameter only here to make that this container is accessed while locked
|
|
@return
|
|
the new count of elements in the container
|
|
*/
|
|
sal_Int32 addInterface(std::unique_lock<std::mutex>& rGuard,
|
|
const css::uno::Reference<ListenerT>& rxIFace);
|
|
/** Removes an element from the container. It uses interface equality to remove the interface.
|
|
|
|
@param rxIFace
|
|
interface to be removed
|
|
@param rGuard
|
|
this parameter only here to make that this container is accessed while locked
|
|
@return
|
|
the new count of elements in the container
|
|
*/
|
|
sal_Int32 removeInterface(std::unique_lock<std::mutex>& rGuard,
|
|
const css::uno::Reference<ListenerT>& rxIFace);
|
|
/**
|
|
Call disposing on all object in the container that
|
|
support XEventListener. Then clear the container.
|
|
The guard is unlock()'ed before calling the listeners.
|
|
*/
|
|
void disposeAndClear(::std::unique_lock<::std::mutex>& rGuard,
|
|
const css::lang::EventObject& rEvt);
|
|
/**
|
|
Clears the container without calling disposing().
|
|
@param rGuard
|
|
this parameter only here to make that this container is accessed while locked
|
|
*/
|
|
void clear(::std::unique_lock<::std::mutex>& rGuard);
|
|
|
|
/** 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.
|
|
|
|
@tparam FuncT unary functor type, let your compiler deduce this for you
|
|
@param func unary functor object expecting an argument of type
|
|
css::uno::Reference<ListenerT>
|
|
@param rGuard
|
|
this parameter only here to make that this container is accessed while locked
|
|
*/
|
|
template <typename FuncT>
|
|
inline void forEach(std::unique_lock<std::mutex>& rGuard, FuncT const& func) 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.
|
|
|
|
@tparam EventT event 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 rGuard
|
|
this parameter only here to make that this container is accessed while locked
|
|
|
|
Example:
|
|
@code
|
|
awt::PaintEvent aEvent( static_cast< cppu::OWeakObject* >( this ), ... );
|
|
listeners.notifyEach( &XPaintListener::windowPaint, aEvent );
|
|
@endcode
|
|
*/
|
|
template <typename EventT>
|
|
inline void notifyEach(std::unique_lock<std::mutex>& rGuard,
|
|
void (SAL_CALL ListenerT::*NotificationMethod)(const EventT&),
|
|
const EventT& Event) const;
|
|
|
|
// this is moveable, but not copyable
|
|
OInterfaceContainerHelper4(OInterfaceContainerHelper4&&) = default;
|
|
OInterfaceContainerHelper4& operator=(OInterfaceContainerHelper4&&) = default;
|
|
|
|
private:
|
|
friend class OInterfaceIteratorHelper4<ListenerT>;
|
|
o3tl::cow_wrapper<std::vector<css::uno::Reference<ListenerT>>,
|
|
o3tl::ThreadSafeRefCountingPolicy>
|
|
maData;
|
|
OInterfaceContainerHelper4(const OInterfaceContainerHelper4&) = delete;
|
|
OInterfaceContainerHelper4& operator=(const OInterfaceContainerHelper4&) = delete;
|
|
|
|
static o3tl::cow_wrapper<std::vector<css::uno::Reference<ListenerT>>,
|
|
o3tl::ThreadSafeRefCountingPolicy>&
|
|
DEFAULT()
|
|
{
|
|
static o3tl::cow_wrapper<std::vector<css::uno::Reference<ListenerT>>,
|
|
o3tl::ThreadSafeRefCountingPolicy>
|
|
SINGLETON;
|
|
return SINGLETON;
|
|
}
|
|
|
|
private:
|
|
template <typename EventT> class NotifySingleListener
|
|
{
|
|
private:
|
|
typedef void (SAL_CALL ListenerT::*NotificationMethod)(const EventT&);
|
|
NotificationMethod const m_pMethod;
|
|
const EventT& m_rEvent;
|
|
|
|
public:
|
|
NotifySingleListener(NotificationMethod method, const EventT& event)
|
|
: m_pMethod(method)
|
|
, m_rEvent(event)
|
|
{
|
|
assert(m_pMethod);
|
|
}
|
|
|
|
void operator()(const css::uno::Reference<ListenerT>& listener) const
|
|
{
|
|
(listener.get()->*m_pMethod)(m_rEvent);
|
|
}
|
|
};
|
|
};
|
|
|
|
template <class T>
|
|
inline OInterfaceContainerHelper4<T>::OInterfaceContainerHelper4()
|
|
: maData(OInterfaceContainerHelper4<T>::DEFAULT())
|
|
{
|
|
}
|
|
|
|
template <class T>
|
|
template <typename FuncT>
|
|
inline void OInterfaceContainerHelper4<T>::forEach(std::unique_lock<std::mutex>& rGuard,
|
|
FuncT const& func) 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();
|
|
}
|
|
}
|
|
}
|
|
rGuard.lock();
|
|
}
|
|
|
|
template <class ListenerT>
|
|
template <typename EventT>
|
|
inline void OInterfaceContainerHelper4<ListenerT>::notifyEach(
|
|
std::unique_lock<std::mutex>& rGuard,
|
|
void (SAL_CALL ListenerT::*NotificationMethod)(const EventT&), const EventT& Event) const
|
|
{
|
|
forEach<NotifySingleListener<EventT>>(rGuard,
|
|
NotifySingleListener<EventT>(NotificationMethod, Event));
|
|
}
|
|
|
|
template <class ListenerT>
|
|
sal_Int32
|
|
OInterfaceContainerHelper4<ListenerT>::getLength(std::unique_lock<std::mutex>& rGuard) const
|
|
{
|
|
assert(rGuard.owns_lock());
|
|
(void)rGuard;
|
|
return maData->size();
|
|
}
|
|
|
|
template <class ListenerT>
|
|
std::vector<css::uno::Reference<ListenerT>>
|
|
OInterfaceContainerHelper4<ListenerT>::getElements(std::unique_lock<std::mutex>& rGuard) const
|
|
{
|
|
assert(rGuard.owns_lock());
|
|
(void)rGuard;
|
|
return *maData;
|
|
}
|
|
|
|
template <class ListenerT>
|
|
sal_Int32
|
|
OInterfaceContainerHelper4<ListenerT>::addInterface(std::unique_lock<std::mutex>& rGuard,
|
|
const css::uno::Reference<ListenerT>& rListener)
|
|
{
|
|
assert(rGuard.owns_lock());
|
|
(void)rGuard;
|
|
assert(rListener.is());
|
|
maData->push_back(rListener);
|
|
return std::as_const(maData)->size();
|
|
}
|
|
|
|
template <class ListenerT>
|
|
sal_Int32 OInterfaceContainerHelper4<ListenerT>::removeInterface(
|
|
std::unique_lock<std::mutex>& rGuard, const css::uno::Reference<ListenerT>& rListener)
|
|
{
|
|
assert(rGuard.owns_lock());
|
|
(void)rGuard;
|
|
assert(rListener.is());
|
|
|
|
// It is not valid to compare the pointer directly, but it's faster.
|
|
auto it = std::find_if(maData->begin(), maData->end(),
|
|
[&rListener](const css::uno::Reference<css::uno::XInterface>& rItem) {
|
|
return rItem.get() == rListener.get();
|
|
});
|
|
|
|
// interface not found, use the correct compare method
|
|
if (it == maData->end())
|
|
it = std::find(maData->begin(), maData->end(), rListener);
|
|
|
|
if (it != maData->end())
|
|
maData->erase(it);
|
|
|
|
return std::as_const(maData)->size();
|
|
}
|
|
|
|
template <class ListenerT>
|
|
void OInterfaceContainerHelper4<ListenerT>::disposeAndClear(std::unique_lock<std::mutex>& rGuard,
|
|
const css::lang::EventObject& rEvt)
|
|
{
|
|
{
|
|
OInterfaceIteratorHelper4<ListenerT> aIt(rGuard, *this);
|
|
maData
|
|
= DEFAULT(); // cheaper than calling maData->clear() because it doesn't allocate a new vector
|
|
rGuard.unlock();
|
|
// unlock followed by iterating is only safe because we are not going to call remove() on the iterator
|
|
while (aIt.hasMoreElements())
|
|
{
|
|
try
|
|
{
|
|
aIt.next()->disposing(rEvt);
|
|
}
|
|
catch (css::uno::RuntimeException&)
|
|
{
|
|
// be robust, if e.g. a remote bridge has disposed already.
|
|
// there is no way to delegate the error to the caller :o(.
|
|
}
|
|
}
|
|
}
|
|
// tdf#152077 need to destruct the OInterfaceIteratorHelper4 before we take the lock again
|
|
// because there is a vague chance that destructing it will trigger a call back into something
|
|
// that wants to take the lock.
|
|
rGuard.lock();
|
|
}
|
|
|
|
template <class ListenerT>
|
|
void OInterfaceContainerHelper4<ListenerT>::clear(::std::unique_lock<::std::mutex>& rGuard)
|
|
{
|
|
assert(rGuard.owns_lock());
|
|
(void)rGuard;
|
|
maData->clear();
|
|
}
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|