Coverage Report

Created: 2026-06-30 11:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/cppuhelper/inc/interfacecontainer4.hxx
Line
Count
Source
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/*
3
 * This file is part of the LibreOffice project.
4
 *
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/.
8
 *
9
 * This file incorporates work covered by the following license notice:
10
 *
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 .
18
 */
19
#pragma once
20
21
#include <sal/config.h>
22
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>
29
30
namespace com::sun::star::uno
31
{
32
class XInterface;
33
}
34
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.
38
*/
39
40
namespace cppuhelper
41
{
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.
47
48
  @tparam ListenerT UNO event listener type
49
  @see OInterfaceContainerHelper4
50
 */
51
template <class ListenerT> class OInterfaceIteratorHelper4
52
{
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.
60
61
       Remark: The copy is on demand. The iterator copy the elements only if the container
62
       change the contents...
63
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
67
     */
68
    OInterfaceIteratorHelper4(std::unique_lock<std::mutex>& rGuard,
69
                              OInterfaceContainerHelper4<ListenerT>& rCont_)
70
0
        : m_rCont(rCont_)
71
0
        , m_aData(m_rCont.maData)
72
        // const_cast so we don't trigger make_unique via o3tl::cow_wrapper::operator->
73
0
        , m_nRemain(std::as_const(m_aData)->size())
74
0
    {
75
0
        assert(rGuard.owns_lock());
76
0
        (void)rGuard;
77
0
    }
78
79
    /** Return true, if there are more elements in the iterator. */
80
0
    bool hasMoreElements() const { return m_nRemain != 0; }
81
    /** Return the next element of the iterator. Calling this method if
82
        hasMoreElements() has returned false, is an error.
83
     */
84
    css::uno::Reference<ListenerT> const& next();
85
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
92
    */
93
    void remove(::std::unique_lock<::std::mutex>& rGuard);
94
95
private:
96
    OInterfaceContainerHelper4<ListenerT>& m_rCont;
97
    o3tl::cow_wrapper<std::vector<css::uno::Reference<ListenerT>>,
98
                      o3tl::ThreadSafeRefCountingPolicy>
99
        m_aData;
100
    sal_Int32 m_nRemain;
101
102
    OInterfaceIteratorHelper4(const OInterfaceIteratorHelper4&) = delete;
103
    OInterfaceIteratorHelper4& operator=(const OInterfaceIteratorHelper4&) = delete;
104
};
105
106
template <class ListenerT>
107
const css::uno::Reference<ListenerT>& OInterfaceIteratorHelper4<ListenerT>::next()
108
0
{
109
0
    m_nRemain--;
110
0
    return (*std::as_const(m_aData))[m_nRemain];
111
0
}
112
113
template <class ListenerT>
114
void OInterfaceIteratorHelper4<ListenerT>::remove(::std::unique_lock<::std::mutex>& rGuard)
115
{
116
    m_rCont.removeInterface(rGuard, (*std::as_const(m_aData))[m_nRemain]);
117
}
118
119
/**
120
  A container of interfaces. To access the elements use an iterator.
121
  This implementation is thread-safe.
122
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.
127
128
  @tparam ListenerT UNO event listener type
129
  @see OInterfaceIteratorHelper
130
 */
131
template <class ListenerT> class OInterfaceContainerHelper4
132
{
133
public:
134
    OInterfaceContainerHelper4();
135
136
    /**
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
141
     */
142
    sal_Int32 getLength(std::unique_lock<std::mutex>& rGuard) const;
143
144
    /**
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
148
     **/
149
    std::vector<css::uno::Reference<ListenerT>>
150
    getElements(std::unique_lock<std::mutex>& rGuard) const;
151
152
    /** Inserts an element into the container.  The position is not specified, thus it is not
153
        specified in which order events are fired.
154
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).
161
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
169
    */
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.
173
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
180
    */
181
    sal_Int32 removeInterface(std::unique_lock<std::mutex>& rGuard,
182
                              const css::uno::Reference<ListenerT>& rxIFace);
183
    /**
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.
187
     */
188
    void disposeAndClear(::std::unique_lock<::std::mutex>& rGuard,
189
                         const css::lang::EventObject& rEvt);
190
    /**
191
      Clears the container without calling disposing().
192
        @param rGuard
193
            this parameter only here to make that this container is accessed while locked
194
     */
195
    void clear(::std::unique_lock<::std::mutex>& rGuard);
196
197
    /** Executes a functor for each contained listener of specified type, e.g.
198
        <code>forEach<awt::XPaintListener>(...</code>.
199
200
        If a css::lang::DisposedException occurs which relates to
201
        the called listener, then that listener is removed from the container.
202
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
208
    */
209
    template <typename FuncT>
210
    inline void forEach(std::unique_lock<std::mutex>& rGuard, FuncT const& func) const;
211
212
    /** Calls a UNO listener method for each contained listener.
213
214
        The listener method must take a single argument of type EventT,
215
        and return <code>void</code>.
216
217
        If a css::lang::DisposedException occurs which relates to
218
        the called listener, then that listener is removed from the container.
219
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
227
228
        Example:
229
@code
230
    awt::PaintEvent aEvent( static_cast< cppu::OWeakObject* >( this ), ... );
231
    listeners.notifyEach( &XPaintListener::windowPaint, aEvent );
232
@endcode
233
    */
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;
238
239
    // this is moveable, but not copyable
240
    OInterfaceContainerHelper4(OInterfaceContainerHelper4&&) = default;
241
    OInterfaceContainerHelper4& operator=(OInterfaceContainerHelper4&&) = default;
242
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;
250
251
    static o3tl::cow_wrapper<std::vector<css::uno::Reference<ListenerT>>,
252
                             o3tl::ThreadSafeRefCountingPolicy>&
253
    DEFAULT()
254
318
    {
255
318
        static o3tl::cow_wrapper<std::vector<css::uno::Reference<ListenerT>>,
256
318
                                 o3tl::ThreadSafeRefCountingPolicy>
257
318
            SINGLETON;
258
318
        return SINGLETON;
259
318
    }
260
261
private:
262
    template <typename EventT> class NotifySingleListener
263
    {
264
    private:
265
        typedef void (SAL_CALL ListenerT::*NotificationMethod)(const EventT&);
266
        NotificationMethod const m_pMethod;
267
        const EventT& m_rEvent;
268
269
    public:
270
        NotifySingleListener(NotificationMethod method, const EventT& event)
271
            : m_pMethod(method)
272
            , m_rEvent(event)
273
        {
274
            assert(m_pMethod);
275
        }
276
277
        void operator()(const css::uno::Reference<ListenerT>& listener) const
278
        {
279
            (listener.get()->*m_pMethod)(m_rEvent);
280
        }
281
    };
282
};
283
284
template <class T>
285
inline OInterfaceContainerHelper4<T>::OInterfaceContainerHelper4()
286
318
    : maData(OInterfaceContainerHelper4<T>::DEFAULT())
287
318
{
288
318
}
289
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
294
{
295
    assert(rGuard.owns_lock());
296
    if (std::as_const(maData)->size() == 0)
297
    {
298
        return;
299
    }
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())
305
    {
306
        auto xListener = iter.next();
307
        try
308
        {
309
            func(xListener);
310
        }
311
        catch (css::lang::DisposedException const& exc)
312
        {
313
            if (exc.Context == xListener)
314
            {
315
                rGuard.lock();
316
                iter.remove(rGuard);
317
                rGuard.unlock();
318
            }
319
        }
320
    }
321
    rGuard.lock();
322
}
323
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
329
{
330
    forEach<NotifySingleListener<EventT>>(rGuard,
331
                                          NotifySingleListener<EventT>(NotificationMethod, Event));
332
}
333
334
template <class ListenerT>
335
sal_Int32
336
OInterfaceContainerHelper4<ListenerT>::getLength(std::unique_lock<std::mutex>& rGuard) const
337
{
338
    assert(rGuard.owns_lock());
339
    (void)rGuard;
340
    return maData->size();
341
}
342
343
template <class ListenerT>
344
std::vector<css::uno::Reference<ListenerT>>
345
OInterfaceContainerHelper4<ListenerT>::getElements(std::unique_lock<std::mutex>& rGuard) const
346
{
347
    assert(rGuard.owns_lock());
348
    (void)rGuard;
349
    return *maData;
350
}
351
352
template <class ListenerT>
353
sal_Int32
354
OInterfaceContainerHelper4<ListenerT>::addInterface(std::unique_lock<std::mutex>& rGuard,
355
                                                    const css::uno::Reference<ListenerT>& rListener)
356
111
{
357
111
    assert(rGuard.owns_lock());
358
111
    (void)rGuard;
359
111
    assert(rListener.is());
360
111
    maData->push_back(rListener);
361
111
    return std::as_const(maData)->size();
362
111
}
363
364
template <class ListenerT>
365
sal_Int32 OInterfaceContainerHelper4<ListenerT>::removeInterface(
366
    std::unique_lock<std::mutex>& rGuard, const css::uno::Reference<ListenerT>& rListener)
367
0
{
368
0
    assert(rGuard.owns_lock());
369
0
    (void)rGuard;
370
0
    assert(rListener.is());
371
372
    // It is not valid to compare the pointer directly, but it's faster.
373
0
    auto it = std::find_if(maData->begin(), maData->end(),
374
0
                           [&rListener](const css::uno::Reference<css::uno::XInterface>& rItem) {
375
0
                               return rItem.get() == rListener.get();
376
0
                           });
377
378
    // interface not found, use the correct compare method
379
0
    if (it == maData->end())
380
0
        it = std::find(maData->begin(), maData->end(), rListener);
381
382
0
    if (it != maData->end())
383
0
        maData->erase(it);
384
385
0
    return std::as_const(maData)->size();
386
0
}
387
388
template <class ListenerT>
389
void OInterfaceContainerHelper4<ListenerT>::disposeAndClear(std::unique_lock<std::mutex>& rGuard,
390
                                                            const css::lang::EventObject& rEvt)
391
0
{
392
0
    {
393
0
        OInterfaceIteratorHelper4<ListenerT> aIt(rGuard, *this);
394
0
        maData
395
0
            = DEFAULT(); // cheaper than calling maData->clear() because it doesn't allocate a new vector
396
0
        rGuard.unlock();
397
        // unlock followed by iterating is only safe because we are not going to call remove() on the iterator
398
0
        while (aIt.hasMoreElements())
399
0
        {
400
0
            try
401
0
            {
402
0
                aIt.next()->disposing(rEvt);
403
0
            }
404
0
            catch (css::uno::RuntimeException&)
405
0
            {
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(.
408
0
            }
409
0
        }
410
0
    }
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
0
    rGuard.lock();
415
0
}
416
417
template <class ListenerT>
418
void OInterfaceContainerHelper4<ListenerT>::clear(::std::unique_lock<::std::mutex>& rGuard)
419
{
420
    assert(rGuard.owns_lock());
421
    (void)rGuard;
422
    maData->clear();
423
}
424
}
425
426
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */