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