Avoid potential negative array index access to cached text.
[LibreOffice.git] / cppuhelper / inc / interfacecontainer4.hxx
blobbbc082cfd76e9360c1c54f14fc1a5a1708f99c4b
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 /**
36 This is a straight copy of the include/comphelper/interfacecontainer4.hxx file, copied here
37 because it is nigh impossible to move shared code down into the URE layer.
40 namespace cppuhelper
42 template <class ListenerT> class OInterfaceContainerHelper4;
43 /**
44 This is the iterator of an OInterfaceContainerHelper4. Typically
45 one constructs an instance on the stack for one firing session.
46 It is not allowed to assign or copy an instance of this class.
48 @tparam ListenerT UNO event listener type
49 @see OInterfaceContainerHelper4
51 template <class ListenerT> class OInterfaceIteratorHelper4
53 public:
54 /**
55 Create an iterator over the elements of the container. The iterator
56 copies the elements of the container. A change to the container
57 during the lifetime of an iterator is allowed and does not
58 affect the iterator-instance. The iterator and the container take cares
59 themself for concurrent access, no additional guarding is necessary.
61 Remark: The copy is on demand. The iterator copy the elements only if the container
62 change the contents...
64 @param rCont the container of the elements.
65 @param rGuard
66 this parameter only here to make that this container is accessed while locked
68 OInterfaceIteratorHelper4(std::unique_lock<std::mutex>& rGuard,
69 OInterfaceContainerHelper4<ListenerT>& rCont_)
70 : rCont(rCont_)
71 , maData(rCont.maData)
72 // const_cast so we don't trigger make_unique via o3tl::cow_wrapper::operator->
73 , nRemain(std::as_const(maData)->size())
75 assert(rGuard.owns_lock());
76 (void)rGuard;
79 /** Return true, if there are more elements in the iterator. */
80 bool hasMoreElements() const { return nRemain != 0; }
81 /** Return the next element of the iterator. Calling this method if
82 hasMoreElements() has returned false, is an error.
84 css::uno::Reference<ListenerT> const& next();
86 /** Removes the current element (the last one returned by next())
87 from the underlying container. Calling this method before
88 next() has been called or calling it twice with no next()
89 in between is an error.
90 @param rGuard
91 this parameter only here to make that this container is accessed while locked
93 void remove(::std::unique_lock<::std::mutex>& rGuard);
95 private:
96 OInterfaceContainerHelper4<ListenerT>& rCont;
97 o3tl::cow_wrapper<std::vector<css::uno::Reference<ListenerT>>,
98 o3tl::ThreadSafeRefCountingPolicy>
99 maData;
100 sal_Int32 nRemain;
102 OInterfaceIteratorHelper4(const OInterfaceIteratorHelper4&) = delete;
103 OInterfaceIteratorHelper4& operator=(const OInterfaceIteratorHelper4&) = delete;
106 template <class ListenerT>
107 const css::uno::Reference<ListenerT>& OInterfaceIteratorHelper4<ListenerT>::next()
109 nRemain--;
110 return (*std::as_const(maData))[nRemain];
113 template <class ListenerT>
114 void OInterfaceIteratorHelper4<ListenerT>::remove(::std::unique_lock<::std::mutex>& rGuard)
116 rCont.removeInterface(rGuard, (*std::as_const(maData))[nRemain]);
120 A container of interfaces. To access the elements use an iterator.
121 This implementation is thread-safe.
123 This is a copy of the code at include/comphelper/interfacecontainer3.hxx,
124 except that it (a) uses std::mutex instead of osl::Mutex and (b) does not
125 store a reference to the mutex, but relies on the calling class to take
126 a lock around using it.
128 @tparam ListenerT UNO event listener type
129 @see OInterfaceIteratorHelper
131 template <class ListenerT> class OInterfaceContainerHelper4
133 public:
134 OInterfaceContainerHelper4();
137 Return the number of Elements in the container. Only useful if you have acquired
138 the mutex.
139 @param rGuard
140 this parameter only here to make that this container is accessed while locked
142 sal_Int32 getLength(std::unique_lock<std::mutex>& rGuard) const;
145 Return all interfaces added to this container.
146 @param rGuard
147 this parameter only here to make that this container is accessed while locked
149 std::vector<css::uno::Reference<ListenerT>>
150 getElements(std::unique_lock<std::mutex>& rGuard) const;
152 /** Inserts an element into the container. The position is not specified, thus it is not
153 specified in which order events are fired.
155 @attention
156 If you add the same interface more than once, then it will be added to the elements list
157 more than once and thus if you want to remove that interface from the list, you have to call
158 removeInterface() the same number of times.
159 In the latter case, you will also get events fired more than once (if the interface is a
160 listener interface).
162 @param rxIFace
163 interface to be added; it is allowed to insert
164 the same interface more than once
165 @param rGuard
166 this parameter only here to make that this container is accessed while locked
167 @return
168 the new count of elements in the container
170 sal_Int32 addInterface(std::unique_lock<std::mutex>& rGuard,
171 const css::uno::Reference<ListenerT>& rxIFace);
172 /** Removes an element from the container. It uses interface equality to remove the interface.
174 @param rxIFace
175 interface to be removed
176 @param rGuard
177 this parameter only here to make that this container is accessed while locked
178 @return
179 the new count of elements in the container
181 sal_Int32 removeInterface(std::unique_lock<std::mutex>& rGuard,
182 const css::uno::Reference<ListenerT>& rxIFace);
184 Call disposing on all object in the container that
185 support XEventListener. Then clear the container.
186 The guard is unlock()'ed before calling the listeners.
188 void disposeAndClear(::std::unique_lock<::std::mutex>& rGuard,
189 const css::lang::EventObject& rEvt);
191 Clears the container without calling disposing().
192 @param rGuard
193 this parameter only here to make that this container is accessed while locked
195 void clear(::std::unique_lock<::std::mutex>& rGuard);
197 /** Executes a functor for each contained listener of specified type, e.g.
198 <code>forEach<awt::XPaintListener>(...</code>.
200 If a css::lang::DisposedException occurs which relates to
201 the called listener, then that listener is removed from the container.
203 @tparam FuncT unary functor type, let your compiler deduce this for you
204 @param func unary functor object expecting an argument of type
205 css::uno::Reference<ListenerT>
206 @param rGuard
207 this parameter only here to make that this container is accessed while locked
209 template <typename FuncT>
210 inline void forEach(std::unique_lock<std::mutex>& rGuard, FuncT const& func) const;
212 /** Calls a UNO listener method for each contained listener.
214 The listener method must take a single argument of type EventT,
215 and return <code>void</code>.
217 If a css::lang::DisposedException occurs which relates to
218 the called listener, then that listener is removed from the container.
220 @tparam EventT event type, let your compiler deduce this for you
221 @param NotificationMethod
222 Pointer to a method of a ListenerT interface.
223 @param Event
224 Event to notify to all contained listeners
225 @param rGuard
226 this parameter only here to make that this container is accessed while locked
228 Example:
229 @code
230 awt::PaintEvent aEvent( static_cast< cppu::OWeakObject* >( this ), ... );
231 listeners.notifyEach( &XPaintListener::windowPaint, aEvent );
232 @endcode
234 template <typename EventT>
235 inline void notifyEach(std::unique_lock<std::mutex>& rGuard,
236 void (SAL_CALL ListenerT::*NotificationMethod)(const EventT&),
237 const EventT& Event) const;
239 // this is moveable, but not copyable
240 OInterfaceContainerHelper4(OInterfaceContainerHelper4&&) = default;
241 OInterfaceContainerHelper4& operator=(OInterfaceContainerHelper4&&) = default;
243 private:
244 friend class OInterfaceIteratorHelper4<ListenerT>;
245 o3tl::cow_wrapper<std::vector<css::uno::Reference<ListenerT>>,
246 o3tl::ThreadSafeRefCountingPolicy>
247 maData;
248 OInterfaceContainerHelper4(const OInterfaceContainerHelper4&) = delete;
249 OInterfaceContainerHelper4& operator=(const OInterfaceContainerHelper4&) = delete;
251 static o3tl::cow_wrapper<std::vector<css::uno::Reference<ListenerT>>,
252 o3tl::ThreadSafeRefCountingPolicy>&
253 DEFAULT()
255 static o3tl::cow_wrapper<std::vector<css::uno::Reference<ListenerT>>,
256 o3tl::ThreadSafeRefCountingPolicy>
257 SINGLETON;
258 return SINGLETON;
261 private:
262 template <typename EventT> class NotifySingleListener
264 private:
265 typedef void (SAL_CALL ListenerT::*NotificationMethod)(const EventT&);
266 NotificationMethod const m_pMethod;
267 const EventT& m_rEvent;
269 public:
270 NotifySingleListener(NotificationMethod method, const EventT& event)
271 : m_pMethod(method)
272 , m_rEvent(event)
274 assert(m_pMethod);
277 void operator()(const css::uno::Reference<ListenerT>& listener) const
279 (listener.get()->*m_pMethod)(m_rEvent);
284 template <class T>
285 inline OInterfaceContainerHelper4<T>::OInterfaceContainerHelper4()
286 : maData(OInterfaceContainerHelper4<T>::DEFAULT())
290 template <class T>
291 template <typename FuncT>
292 inline void OInterfaceContainerHelper4<T>::forEach(std::unique_lock<std::mutex>& rGuard,
293 FuncT const& func) const
295 assert(rGuard.owns_lock());
296 if (std::as_const(maData)->size() == 0)
298 return;
300 const_cast<OInterfaceContainerHelper4&>(*this)
301 .maData.make_unique(); // so we can iterate over the data without holding the lock
302 OInterfaceIteratorHelper4<T> iter(rGuard, const_cast<OInterfaceContainerHelper4&>(*this));
303 rGuard.unlock();
304 while (iter.hasMoreElements())
306 auto xListener = iter.next();
309 func(xListener);
311 catch (css::lang::DisposedException const& exc)
313 if (exc.Context == xListener)
315 rGuard.lock();
316 iter.remove(rGuard);
317 rGuard.unlock();
321 rGuard.lock();
324 template <class ListenerT>
325 template <typename EventT>
326 inline void OInterfaceContainerHelper4<ListenerT>::notifyEach(
327 std::unique_lock<std::mutex>& rGuard,
328 void (SAL_CALL ListenerT::*NotificationMethod)(const EventT&), const EventT& Event) const
330 forEach<NotifySingleListener<EventT>>(rGuard,
331 NotifySingleListener<EventT>(NotificationMethod, Event));
334 template <class ListenerT>
335 sal_Int32
336 OInterfaceContainerHelper4<ListenerT>::getLength(std::unique_lock<std::mutex>& rGuard) const
338 assert(rGuard.owns_lock());
339 (void)rGuard;
340 return maData->size();
343 template <class ListenerT>
344 std::vector<css::uno::Reference<ListenerT>>
345 OInterfaceContainerHelper4<ListenerT>::getElements(std::unique_lock<std::mutex>& rGuard) const
347 assert(rGuard.owns_lock());
348 (void)rGuard;
349 return *maData;
352 template <class ListenerT>
353 sal_Int32
354 OInterfaceContainerHelper4<ListenerT>::addInterface(std::unique_lock<std::mutex>& rGuard,
355 const css::uno::Reference<ListenerT>& rListener)
357 assert(rGuard.owns_lock());
358 (void)rGuard;
359 assert(rListener.is());
360 maData->push_back(rListener);
361 return std::as_const(maData)->size();
364 template <class ListenerT>
365 sal_Int32 OInterfaceContainerHelper4<ListenerT>::removeInterface(
366 std::unique_lock<std::mutex>& rGuard, const css::uno::Reference<ListenerT>& rListener)
368 assert(rGuard.owns_lock());
369 (void)rGuard;
370 assert(rListener.is());
372 // It is not valid to compare the pointer directly, but it's faster.
373 auto it = std::find_if(maData->begin(), maData->end(),
374 [&rListener](const css::uno::Reference<css::uno::XInterface>& rItem) {
375 return rItem.get() == rListener.get();
378 // interface not found, use the correct compare method
379 if (it == maData->end())
380 it = std::find(maData->begin(), maData->end(), rListener);
382 if (it != maData->end())
383 maData->erase(it);
385 return std::as_const(maData)->size();
388 template <class ListenerT>
389 void OInterfaceContainerHelper4<ListenerT>::disposeAndClear(std::unique_lock<std::mutex>& rGuard,
390 const css::lang::EventObject& rEvt)
393 OInterfaceIteratorHelper4<ListenerT> aIt(rGuard, *this);
394 maData
395 = DEFAULT(); // cheaper than calling maData->clear() because it doesn't allocate a new vector
396 rGuard.unlock();
397 // unlock followed by iterating is only safe because we are not going to call remove() on the iterator
398 while (aIt.hasMoreElements())
402 aIt.next()->disposing(rEvt);
404 catch (css::uno::RuntimeException&)
406 // be robust, if e.g. a remote bridge has disposed already.
407 // there is no way to delegate the error to the caller :o(.
411 // tdf#152077 need to destruct the OInterfaceIteratorHelper4 before we take the lock again
412 // because there is a vague chance that destructing it will trigger a call back into something
413 // that wants to take the lock.
414 rGuard.lock();
417 template <class ListenerT>
418 void OInterfaceContainerHelper4<ListenerT>::clear(::std::unique_lock<::std::mutex>& rGuard)
420 assert(rGuard.owns_lock());
421 (void)rGuard;
422 maData->clear();
426 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */