Line data Source code
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 : #ifndef INCLUDED_SLIDESHOW_SOURCE_INC_LISTENERCONTAINER_HXX
20 : #define INCLUDED_SLIDESHOW_SOURCE_INC_LISTENERCONTAINER_HXX
21 :
22 : #include <osl/mutex.hxx>
23 : #include <boost/utility.hpp>
24 : #include <algorithm>
25 : #include <vector>
26 :
27 : namespace slideshow {
28 : namespace internal {
29 :
30 19 : struct EmptyBase
31 : {
32 42 : struct EmptyGuard{ explicit EmptyGuard(EmptyBase) {} };
33 : struct EmptyClearableGuard
34 : {
35 0 : explicit EmptyClearableGuard(EmptyBase) {}
36 0 : static void clear() {}
37 : static void reset() {}
38 : };
39 :
40 : typedef EmptyGuard Guard;
41 : typedef EmptyClearableGuard ClearableGuard;
42 : };
43 :
44 : class MutexBase
45 : {
46 : public:
47 : struct Guard : public osl::MutexGuard
48 : {
49 : explicit Guard(MutexBase const& rBase) :
50 : osl::MutexGuard(rBase.maMutex)
51 : {}
52 : };
53 : struct ClearableGuard : public osl::ClearableMutexGuard
54 : {
55 : explicit ClearableGuard(MutexBase const& rBase) :
56 : osl::ClearableMutexGuard(rBase.maMutex)
57 : {}
58 : };
59 :
60 : mutable osl::Mutex maMutex;
61 : };
62 :
63 : template< typename result_type, typename ListenerTargetT > struct FunctionApply
64 : {
65 0 : template<typename FuncT> static bool apply(
66 : FuncT func,
67 : ListenerTargetT const& rArg )
68 : {
69 0 : return func(rArg);
70 : }
71 : };
72 :
73 : template<typename ListenerTargetT> struct FunctionApply<void,ListenerTargetT>
74 : {
75 0 : template<typename FuncT> static bool apply(
76 : FuncT func,
77 : ListenerTargetT const& rArg )
78 : {
79 0 : func(rArg);
80 0 : return true;
81 : }
82 : };
83 :
84 : template< typename ListenerT > struct ListenerOperations
85 : {
86 : /// Notify a single one of the listeners
87 : template< typename ContainerT,
88 : typename FuncT >
89 0 : static bool notifySingleListener( ContainerT& rContainer,
90 : FuncT func )
91 : {
92 : // true: a handler in this queue processed the event
93 : // false: no handler in this queue finally processed the event
94 : return std::any_of( rContainer.begin(),
95 : rContainer.end(),
96 0 : func );
97 : }
98 :
99 : /// Notify all listeners
100 : template< typename ContainerT,
101 : typename FuncT >
102 0 : static bool notifyAllListeners( ContainerT& rContainer,
103 : FuncT func )
104 : {
105 0 : bool bRet(false);
106 0 : typename ContainerT::const_iterator aCurr( rContainer.begin() );
107 0 : typename ContainerT::const_iterator const aEnd ( rContainer.end() );
108 0 : while( aCurr != aEnd )
109 : {
110 0 : if( FunctionApply< typename FuncT::result_type,
111 : typename ContainerT::value_type >::apply(
112 : func,
113 0 : *aCurr) )
114 : {
115 0 : bRet = true;
116 : }
117 :
118 0 : ++aCurr;
119 : }
120 :
121 : // true: at least one handler returned true
122 : // false: not a single handler returned true
123 0 : return bRet;
124 : }
125 :
126 : /// Prune container from deceased listeners
127 : template< typename ContainerT >
128 8 : static void pruneListeners( ContainerT&, size_t )
129 : {
130 8 : }
131 : };
132 :
133 : template< typename ListenerTargetT >
134 : struct ListenerOperations< boost::weak_ptr<ListenerTargetT> >
135 : {
136 : template< typename ContainerT,
137 : typename FuncT >
138 : static bool notifySingleListener( ContainerT& rContainer,
139 : FuncT func )
140 : {
141 : typename ContainerT::const_iterator aCurr( rContainer.begin() );
142 : typename ContainerT::const_iterator const aEnd ( rContainer.end() );
143 : while( aCurr != aEnd )
144 : {
145 : boost::shared_ptr<ListenerTargetT> pListener( aCurr->lock() );
146 :
147 : if( pListener && func(pListener) )
148 : return true;
149 :
150 : ++aCurr;
151 : }
152 :
153 : return false;
154 : }
155 :
156 : template< typename ContainerT,
157 : typename FuncT >
158 0 : static bool notifyAllListeners( ContainerT& rContainer,
159 : FuncT func )
160 : {
161 0 : bool bRet(false);
162 0 : typename ContainerT::const_iterator aCurr( rContainer.begin() );
163 0 : typename ContainerT::const_iterator const aEnd ( rContainer.end() );
164 0 : while( aCurr != aEnd )
165 : {
166 0 : boost::shared_ptr<ListenerTargetT> pListener( aCurr->lock() );
167 :
168 0 : if( pListener.get() &&
169 : FunctionApply< typename FuncT::result_type,
170 0 : boost::shared_ptr<ListenerTargetT> >::apply(func,pListener) )
171 : {
172 0 : bRet = true;
173 : }
174 :
175 0 : ++aCurr;
176 : }
177 :
178 0 : return bRet;
179 : }
180 : template< typename ContainerT >
181 0 : static void pruneListeners( ContainerT& rContainer,
182 : size_t nSizeThreshold )
183 : {
184 0 : if( rContainer.size() <= nSizeThreshold )
185 0 : return;
186 :
187 0 : ContainerT aAliveListeners;
188 0 : aAliveListeners.reserve(rContainer.size());
189 :
190 0 : typename ContainerT::const_iterator aCurr( rContainer.begin() );
191 0 : typename ContainerT::const_iterator const aEnd ( rContainer.end() );
192 0 : while( aCurr != aEnd )
193 : {
194 0 : if( !aCurr->expired() )
195 0 : aAliveListeners.push_back( *aCurr );
196 :
197 0 : ++aCurr;
198 : }
199 :
200 0 : std::swap( rContainer, aAliveListeners );
201 : }
202 : };
203 :
204 : /** Container for objects that can be notified.
205 :
206 : This templatized container holds listener objects, than can get
207 : notified (by calling certain methods on them).
208 :
209 : @tpl Listener
210 : Type for the listener objects to be held
211 :
212 : @tpl ContainerT
213 : Full type of the container to store the listener objects. Defaults
214 : to std::vector<ListenerT>
215 :
216 : @tpl MaxDeceasedListenerUllage
217 : Threshold, from which upwards the listener container gets
218 : pruned. Avoids frequent copying of nearly empty containers.
219 :
220 : @attention internal class, not to be used. functionality is
221 : incomplete, please use the Thread(Un)safeListenerContainer types
222 : from below
223 : */
224 : template< typename ListenerT,
225 : typename MutexHolderBaseT,
226 : typename ContainerT=std::vector<ListenerT>,
227 38 : size_t MaxDeceasedListenerUllage=16 > class ListenerContainerBase : public MutexHolderBaseT
228 : {
229 : typedef typename MutexHolderBaseT::Guard Guard;
230 : typedef typename MutexHolderBaseT::ClearableGuard ClearableGuard;
231 :
232 : public:
233 : typedef ListenerT listener_type;
234 : typedef ContainerT container_type;
235 : typedef MutexHolderBaseT mutex_type;
236 :
237 : /** Check whether listener container is empty
238 :
239 : @return true, if currently no listeners registered. Note that
240 : in a multi-threaded scenario, without external synchronisation
241 : to this object, the return value might become wrong at any time.
242 : */
243 3 : bool isEmpty() const
244 : {
245 3 : Guard aGuard(*this);
246 3 : return maListeners.empty();
247 : }
248 :
249 : /** Check whether given listener is already added
250 :
251 : @return true, if given listener is already added.
252 : */
253 8 : bool isAdded( listener_type const& rListener ) const
254 : {
255 8 : Guard aGuard(*this);
256 :
257 8 : const typename container_type::const_iterator aEnd( maListeners.end() );
258 16 : if( std::find( maListeners.begin(),
259 : aEnd,
260 16 : rListener ) != aEnd )
261 : {
262 0 : return true; // already added
263 : }
264 :
265 8 : return false;
266 : }
267 :
268 : /** Add new listener
269 :
270 : @param rListener
271 : Listener to add
272 :
273 : @return false, if the listener is already added, true
274 : otherwise
275 : */
276 7 : bool add( listener_type const& rListener )
277 : {
278 7 : Guard aGuard(*this);
279 :
280 : // ensure uniqueness
281 7 : if( isAdded(rListener) )
282 0 : return false; // already added
283 :
284 7 : maListeners.push_back( rListener );
285 :
286 7 : ListenerOperations<ListenerT>::pruneListeners(
287 : maListeners,
288 7 : MaxDeceasedListenerUllage);
289 :
290 7 : return true;
291 : }
292 :
293 : /** Add new listener into sorted container
294 :
295 : The stored listeners are kept sorted (using this method
296 : requires listener_type to have operator< defined on it). Make
297 : sure to call addSorted() for <em>each</em> listener to add to
298 : this container - sorting is performed under the assumption
299 : that existing entries are already sorted.
300 :
301 : @param rListener
302 : Listener to add
303 :
304 : @return false, if the listener is already added, true
305 : otherwise
306 : */
307 1 : bool addSorted( listener_type const& rListener )
308 : {
309 1 : Guard aGuard(*this);
310 :
311 : // ensure uniqueness
312 1 : if( isAdded(rListener) )
313 0 : return false; // already added
314 :
315 1 : maListeners.push_back( rListener );
316 :
317 : // a single entry does not need to be sorted
318 1 : if( maListeners.size() > 1 )
319 : {
320 0 : std::inplace_merge(
321 : maListeners.begin(),
322 : boost::prior(maListeners.end()),
323 0 : maListeners.end() );
324 : }
325 :
326 1 : ListenerOperations<ListenerT>::pruneListeners(
327 : maListeners,
328 1 : MaxDeceasedListenerUllage);
329 :
330 1 : return true;
331 : }
332 :
333 : /** Remove listener from container
334 :
335 : @param rListener
336 : The listener to remove
337 :
338 : @return false, if listener not found in container, true
339 : otherwise
340 : */
341 8 : bool remove( listener_type const& rListener )
342 : {
343 8 : Guard aGuard(*this);
344 :
345 8 : const typename container_type::iterator aEnd( maListeners.end() );
346 8 : typename container_type::iterator aIter;
347 8 : if( (aIter=std::remove(maListeners.begin(),
348 : aEnd,
349 8 : rListener)) == aEnd )
350 : {
351 0 : return false; // listener not found
352 : }
353 :
354 8 : maListeners.erase( aIter, aEnd );
355 :
356 8 : return true;
357 : }
358 :
359 : /// Removes all listeners in one go
360 15 : void clear()
361 : {
362 15 : Guard aGuard(*this);
363 :
364 15 : maListeners.clear();
365 15 : }
366 :
367 : /** Apply functor to one listener
368 :
369 : This method applies functor to one of the listeners. Starting
370 : with the first entry of the container, the functor is called
371 : with the listener entries, until it returns true.
372 :
373 : @param func
374 : Given functor is called with listeners, until it returns true
375 :
376 : @return true, if functor was successfully applied to a
377 : listener
378 : */
379 0 : template< typename FuncT > bool apply( FuncT func ) const
380 : {
381 0 : ClearableGuard aGuard(*this);
382 :
383 : // generate a local copy of all handlers, to make method
384 : // reentrant and thread-safe.
385 0 : container_type const local( maListeners );
386 0 : aGuard.clear();
387 :
388 : const bool bRet(
389 : ListenerOperations<ListenerT>::notifySingleListener(
390 : local,
391 0 : func ));
392 :
393 : {
394 0 : Guard aGuard2(*this);
395 0 : ListenerOperations<ListenerT>::pruneListeners(
396 : const_cast<container_type&>(maListeners),
397 0 : MaxDeceasedListenerUllage);
398 : }
399 :
400 0 : return bRet;
401 : }
402 :
403 : /** Apply functor to all listeners
404 :
405 : This method applies functor to all of the listeners. Starting
406 : with the first entry of the container, the functor is called
407 : with the listener entries.
408 :
409 : @param func
410 : Given functor is called with listeners.
411 :
412 : @return true, if functor was successfully applied to at least
413 : one listener
414 : */
415 0 : template< typename FuncT > bool applyAll( FuncT func ) const
416 : {
417 0 : ClearableGuard aGuard(*this);
418 :
419 : // generate a local copy of all handlers, to make method
420 : // reentrant and thread-safe.
421 0 : container_type const local( maListeners );
422 0 : aGuard.clear();
423 :
424 : const bool bRet(
425 : ListenerOperations<ListenerT>::notifyAllListeners(
426 : local,
427 0 : func ));
428 :
429 : {
430 0 : Guard aGuard2(*this);
431 0 : ListenerOperations<ListenerT>::pruneListeners(
432 : const_cast<container_type&>(maListeners),
433 0 : MaxDeceasedListenerUllage);
434 : }
435 :
436 0 : return bRet;
437 : }
438 :
439 : private:
440 : ContainerT maListeners;
441 : };
442 :
443 :
444 :
445 : /** ListenerContainer variant that serialized access
446 :
447 : This ListenerContainer is safe to use in a multi-threaded
448 : context. It serializes access to the object, and avoids
449 : dead-locking by releasing the object mutex before calling
450 : listeners.
451 : */
452 : template< typename ListenerT,
453 : typename ContainerT=std::vector<ListenerT> >
454 : class ThreadSafeListenerContainer : public ListenerContainerBase<ListenerT,
455 : MutexBase,
456 : ContainerT>
457 : {
458 : };
459 :
460 :
461 :
462 : /** ListenerContainer variant that does not serialize access
463 :
464 : This ListenerContainer version is not safe to use in a
465 : multi-threaded scenario, but has less overhead.
466 : */
467 : template< typename ListenerT,
468 : typename ContainerT=std::vector<ListenerT> >
469 38 : class ThreadUnsafeListenerContainer : public ListenerContainerBase<ListenerT,
470 : EmptyBase,
471 : ContainerT>
472 : {
473 : };
474 :
475 : } // namespace internal
476 : } // namespace slideshow
477 :
478 : #endif // INCLUDED_SLIDESHOW_SOURCE_INC_LISTENERCONTAINER_HXX
479 :
480 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|