Avoid potential negative array index access to cached text.
[LibreOffice.git] / include / comphelper / interfacecontainer4.hxx
blobe4a41e30675d82428ca670edd42faa6f072d25d6
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
19 #pragma once
21 #include <sal/config.h>
23 #include <com/sun/star/lang/EventObject.hpp>
24 #include <com/sun/star/lang/DisposedException.hpp>
25 #include <o3tl/cow_wrapper.hxx>
26 #include <cassert>
27 #include <mutex>
28 #include <vector>
30 namespace com::sun::star::uno
32 class XInterface;
35 namespace comphelper
37 template <class ListenerT> class OInterfaceContainerHelper4;
38 /**
39 This is the iterator of an OInterfaceContainerHelper4. Typically
40 one constructs an instance on the stack for one firing session.
41 It is not allowed to assign or copy an instance of this class.
43 @tparam ListenerT UNO event listener type
44 @see OInterfaceContainerHelper4
46 template <class ListenerT> class OInterfaceIteratorHelper4
48 public:
49 /**
50 Create an iterator over the elements of the container. The iterator
51 copies the elements of the container. A change to the container
52 during the lifetime of an iterator is allowed and does not
53 affect the iterator-instance. The iterator and the container take cares
54 themself for concurrent access, no additional guarding is necessary.
56 Remark: The copy is on demand. The iterator copy the elements only if the container
57 change the contents...
59 @param rCont the container of the elements.
60 @param rGuard
61 this parameter only here to make that this container is accessed while locked
63 OInterfaceIteratorHelper4(std::unique_lock<std::mutex>& rGuard,
64 OInterfaceContainerHelper4<ListenerT>& rCont_)
65 : rCont(rCont_)
66 , maData(rCont.maData)
67 // const_cast so we don't trigger make_unique via o3tl::cow_wrapper::operator->
68 , nRemain(std::as_const(maData)->size())
70 assert(rGuard.owns_lock());
71 (void)rGuard;
74 /** Return true, if there are more elements in the iterator. */
75 bool hasMoreElements() const { return nRemain != 0; }
76 /** Return the next element of the iterator. Calling this method if
77 hasMoreElements() has returned false, is an error.
79 css::uno::Reference<ListenerT> const& next();
81 /** Removes the current element (the last one returned by next())
82 from the underlying container. Calling this method before
83 next() has been called or calling it twice with no next()
84 in between is an error.
85 @param rGuard
86 this parameter only here to make that this container is accessed while locked
88 void remove(::std::unique_lock<::std::mutex>& rGuard);
90 private:
91 OInterfaceContainerHelper4<ListenerT>& rCont;
92 o3tl::cow_wrapper<std::vector<css::uno::Reference<ListenerT>>,
93 o3tl::ThreadSafeRefCountingPolicy>
94 maData;
95 sal_Int32 nRemain;
97 OInterfaceIteratorHelper4(const OInterfaceIteratorHelper4&) = delete;
98 OInterfaceIteratorHelper4& operator=(const OInterfaceIteratorHelper4&) = delete;
101 template <class ListenerT>
102 const css::uno::Reference<ListenerT>& OInterfaceIteratorHelper4<ListenerT>::next()
104 nRemain--;
105 return (*std::as_const(maData))[nRemain];
108 template <class ListenerT>
109 void OInterfaceIteratorHelper4<ListenerT>::remove(::std::unique_lock<::std::mutex>& rGuard)
111 rCont.removeInterface(rGuard, (*std::as_const(maData))[nRemain]);
115 A container of interfaces. To access the elements use an iterator.
116 This implementation is thread-safe.
118 This is a copy of the code at include/comphelper/interfacecontainer3.hxx,
119 except that it (a) uses std::mutex instead of osl::Mutex and (b) does not
120 store a reference to the mutex, but relies on the calling class to take
121 a lock around using it.
123 @tparam ListenerT UNO event listener type
124 @see OInterfaceIteratorHelper
126 template <class ListenerT> class OInterfaceContainerHelper4
128 public:
129 OInterfaceContainerHelper4();
132 Return the number of Elements in the container. Only useful if you have acquired
133 the mutex.
134 @param rGuard
135 this parameter only here to make that this container is accessed while locked
137 sal_Int32 getLength(std::unique_lock<std::mutex>& rGuard) const;
140 Return all interfaces added to this container.
141 @param rGuard
142 this parameter only here to make that this container is accessed while locked
144 std::vector<css::uno::Reference<ListenerT>>
145 getElements(std::unique_lock<std::mutex>& rGuard) const;
147 /** Inserts an element into the container. The position is not specified, thus it is not
148 specified in which order events are fired.
150 @attention
151 If you add the same interface more than once, then it will be added to the elements list
152 more than once and thus if you want to remove that interface from the list, you have to call
153 removeInterface() the same number of times.
154 In the latter case, you will also get events fired more than once (if the interface is a
155 listener interface).
157 @param rxIFace
158 interface to be added; it is allowed to insert
159 the same interface more than once
160 @param rGuard
161 this parameter only here to make that this container is accessed while locked
162 @return
163 the new count of elements in the container
165 sal_Int32 addInterface(std::unique_lock<std::mutex>& rGuard,
166 const css::uno::Reference<ListenerT>& rxIFace);
167 /** Removes an element from the container. It uses interface equality to remove the interface.
169 @param rxIFace
170 interface to be removed
171 @param rGuard
172 this parameter only here to make that this container is accessed while locked
173 @return
174 the new count of elements in the container
176 sal_Int32 removeInterface(std::unique_lock<std::mutex>& rGuard,
177 const css::uno::Reference<ListenerT>& rxIFace);
179 Call disposing on all object in the container that
180 support XEventListener. Then clear the container.
181 The guard is unlock()'ed before calling the listeners.
183 void disposeAndClear(::std::unique_lock<::std::mutex>& rGuard,
184 const css::lang::EventObject& rEvt);
186 Clears the container without calling disposing().
187 @param rGuard
188 this parameter only here to make that this container is accessed while locked
190 void clear(::std::unique_lock<::std::mutex>& rGuard);
192 /** Executes a functor for each contained listener of specified type, e.g.
193 <code>forEach<awt::XPaintListener>(...</code>.
195 If a css::lang::DisposedException occurs which relates to
196 the called listener, then that listener is removed from the container.
198 @tparam FuncT unary functor type, let your compiler deduce this for you
199 @param func unary functor object expecting an argument of type
200 css::uno::Reference<ListenerT>
201 @param rGuard
202 this parameter only here to make that this container is accessed while locked
204 template <typename FuncT>
205 inline void forEach(std::unique_lock<std::mutex>& rGuard, FuncT const& func) const;
207 /** Calls a UNO listener method for each contained listener.
209 The listener method must take a single argument of type EventT,
210 and return <code>void</code>.
212 If a css::lang::DisposedException occurs which relates to
213 the called listener, then that listener is removed from the container.
215 @tparam EventT event type, let your compiler deduce this for you
216 @param NotificationMethod
217 Pointer to a method of a ListenerT interface.
218 @param Event
219 Event to notify to all contained listeners
220 @param rGuard
221 this parameter only here to make that this container is accessed while locked
223 Example:
224 @code
225 awt::PaintEvent aEvent( static_cast< cppu::OWeakObject* >( this ), ... );
226 listeners.notifyEach( &XPaintListener::windowPaint, aEvent );
227 @endcode
229 template <typename EventT>
230 inline void notifyEach(std::unique_lock<std::mutex>& rGuard,
231 void (SAL_CALL ListenerT::*NotificationMethod)(const EventT&),
232 const EventT& Event) const;
234 // this is moveable, but not copyable
235 OInterfaceContainerHelper4(OInterfaceContainerHelper4&&) = default;
236 OInterfaceContainerHelper4& operator=(OInterfaceContainerHelper4&&) = default;
238 private:
239 friend class OInterfaceIteratorHelper4<ListenerT>;
240 o3tl::cow_wrapper<std::vector<css::uno::Reference<ListenerT>>,
241 o3tl::ThreadSafeRefCountingPolicy>
242 maData;
243 OInterfaceContainerHelper4(const OInterfaceContainerHelper4&) = delete;
244 OInterfaceContainerHelper4& operator=(const OInterfaceContainerHelper4&) = delete;
246 static o3tl::cow_wrapper<std::vector<css::uno::Reference<ListenerT>>,
247 o3tl::ThreadSafeRefCountingPolicy>&
248 DEFAULT()
250 static o3tl::cow_wrapper<std::vector<css::uno::Reference<ListenerT>>,
251 o3tl::ThreadSafeRefCountingPolicy>
252 SINGLETON;
253 return SINGLETON;
256 private:
257 template <typename EventT> class NotifySingleListener
259 private:
260 typedef void (SAL_CALL ListenerT::*NotificationMethod)(const EventT&);
261 NotificationMethod const m_pMethod;
262 const EventT& m_rEvent;
264 public:
265 NotifySingleListener(NotificationMethod method, const EventT& event)
266 : m_pMethod(method)
267 , m_rEvent(event)
269 assert(m_pMethod);
272 void operator()(const css::uno::Reference<ListenerT>& listener) const
274 (listener.get()->*m_pMethod)(m_rEvent);
279 template <class T>
280 inline OInterfaceContainerHelper4<T>::OInterfaceContainerHelper4()
281 : maData(OInterfaceContainerHelper4<T>::DEFAULT())
285 template <class T>
286 template <typename FuncT>
287 inline void OInterfaceContainerHelper4<T>::forEach(std::unique_lock<std::mutex>& rGuard,
288 FuncT const& func) const
290 assert(rGuard.owns_lock());
291 if (std::as_const(maData)->empty())
293 return;
295 const_cast<OInterfaceContainerHelper4&>(*this)
296 .maData.make_unique(); // so we can iterate over the data without holding the lock
297 OInterfaceIteratorHelper4<T> iter(rGuard, const_cast<OInterfaceContainerHelper4&>(*this));
298 rGuard.unlock();
299 while (iter.hasMoreElements())
301 auto xListener = iter.next();
304 func(xListener);
306 catch (css::lang::DisposedException const& exc)
308 if (exc.Context == xListener)
310 rGuard.lock();
311 iter.remove(rGuard);
312 rGuard.unlock();
316 rGuard.lock();
319 template <class ListenerT>
320 template <typename EventT>
321 inline void OInterfaceContainerHelper4<ListenerT>::notifyEach(
322 std::unique_lock<std::mutex>& rGuard,
323 void (SAL_CALL ListenerT::*NotificationMethod)(const EventT&), const EventT& Event) const
325 forEach<NotifySingleListener<EventT>>(rGuard,
326 NotifySingleListener<EventT>(NotificationMethod, Event));
329 template <class ListenerT>
330 sal_Int32
331 OInterfaceContainerHelper4<ListenerT>::getLength(std::unique_lock<std::mutex>& rGuard) const
333 assert(rGuard.owns_lock());
334 (void)rGuard;
335 return maData->size();
338 template <class ListenerT>
339 std::vector<css::uno::Reference<ListenerT>>
340 OInterfaceContainerHelper4<ListenerT>::getElements(std::unique_lock<std::mutex>& rGuard) const
342 assert(rGuard.owns_lock());
343 (void)rGuard;
344 return *maData;
347 template <class ListenerT>
348 sal_Int32
349 OInterfaceContainerHelper4<ListenerT>::addInterface(std::unique_lock<std::mutex>& rGuard,
350 const css::uno::Reference<ListenerT>& rListener)
352 assert(rGuard.owns_lock());
353 (void)rGuard;
354 assert(rListener.is());
355 maData->push_back(rListener);
356 return std::as_const(maData)->size();
359 template <class ListenerT>
360 sal_Int32 OInterfaceContainerHelper4<ListenerT>::removeInterface(
361 std::unique_lock<std::mutex>& rGuard, const css::uno::Reference<ListenerT>& rListener)
363 assert(rGuard.owns_lock());
364 (void)rGuard;
365 assert(rListener.is());
367 // It is not valid to compare the pointer directly, but it's faster.
368 auto it = std::find_if(maData->begin(), maData->end(),
369 [&rListener](const css::uno::Reference<css::uno::XInterface>& rItem) {
370 return rItem.get() == rListener.get();
373 // interface not found, use the correct compare method
374 if (it == maData->end())
375 it = std::find(maData->begin(), maData->end(), rListener);
377 if (it != maData->end())
378 maData->erase(it);
380 return std::as_const(maData)->size();
383 template <class ListenerT>
384 void OInterfaceContainerHelper4<ListenerT>::disposeAndClear(std::unique_lock<std::mutex>& rGuard,
385 const css::lang::EventObject& rEvt)
388 OInterfaceIteratorHelper4<ListenerT> aIt(rGuard, *this);
389 maData
390 = DEFAULT(); // cheaper than calling maData->clear() because it doesn't allocate a new vector
391 rGuard.unlock();
392 // unlock followed by iterating is only safe because we are not going to call remove() on the iterator
393 while (aIt.hasMoreElements())
397 aIt.next()->disposing(rEvt);
399 catch (css::uno::RuntimeException&)
401 // be robust, if e.g. a remote bridge has disposed already.
402 // there is no way to delegate the error to the caller :o(.
406 // tdf#152077 need to destruct the OInterfaceIteratorHelper4 before we take the lock again
407 // because there is a vague chance that destructing it will trigger a call back into something
408 // that wants to take the lock.
409 rGuard.lock();
412 template <class ListenerT>
413 void OInterfaceContainerHelper4<ListenerT>::clear(::std::unique_lock<::std::mutex>& rGuard)
415 assert(rGuard.owns_lock());
416 (void)rGuard;
417 maData->clear();
421 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */