1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 #ifndef INCLUDED_SLIDESHOW_SOURCE_INC_LISTENERCONTAINER_HXX
20 #define INCLUDED_SLIDESHOW_SOURCE_INC_LISTENERCONTAINER_HXX
22 #include <osl/mutex.hxx>
23 #include <boost/utility.hpp>
32 struct EmptyGuard
{ explicit EmptyGuard(EmptyBase
) {} };
33 struct EmptyClearableGuard
35 explicit EmptyClearableGuard(EmptyBase
) {}
36 static void clear() {}
37 static void reset() {}
40 typedef EmptyGuard Guard
;
41 typedef EmptyClearableGuard ClearableGuard
;
47 struct Guard
: public osl::MutexGuard
49 explicit Guard(MutexBase
const& rBase
) :
50 osl::MutexGuard(rBase
.maMutex
)
53 struct ClearableGuard
: public osl::ClearableMutexGuard
55 explicit ClearableGuard(MutexBase
const& rBase
) :
56 osl::ClearableMutexGuard(rBase
.maMutex
)
60 mutable osl::Mutex maMutex
;
63 template< typename result_type
, typename ListenerTargetT
> struct FunctionApply
65 template<typename FuncT
> static bool apply(
67 ListenerTargetT
const& rArg
)
73 template<typename ListenerTargetT
> struct FunctionApply
<void,ListenerTargetT
>
75 template<typename FuncT
> static bool apply(
77 ListenerTargetT
const& rArg
)
84 template< typename ListenerT
> struct ListenerOperations
86 /// Notify a single one of the listeners
87 template< typename ContainerT
,
89 static bool notifySingleListener( ContainerT
& rContainer
,
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(),
99 /// Notify all listeners
100 template< typename ContainerT
,
102 static bool notifyAllListeners( ContainerT
& rContainer
,
106 typename
ContainerT::const_iterator
aCurr( rContainer
.begin() );
107 typename
ContainerT::const_iterator
const aEnd ( rContainer
.end() );
108 while( aCurr
!= aEnd
)
110 if( FunctionApply
< typename
FuncT::result_type
,
111 typename
ContainerT::value_type
>::apply(
121 // true: at least one handler returned true
122 // false: not a single handler returned true
126 /// Prune container from deceased listeners
127 template< typename ContainerT
>
128 static void pruneListeners( ContainerT
&, size_t )
133 template< typename ListenerTargetT
>
134 struct ListenerOperations
< boost::weak_ptr
<ListenerTargetT
> >
136 template< typename ContainerT
,
138 static bool notifySingleListener( ContainerT
& rContainer
,
141 typename
ContainerT::const_iterator
aCurr( rContainer
.begin() );
142 typename
ContainerT::const_iterator
const aEnd ( rContainer
.end() );
143 while( aCurr
!= aEnd
)
145 boost::shared_ptr
<ListenerTargetT
> pListener( aCurr
->lock() );
147 if( pListener
&& func(pListener
) )
156 template< typename ContainerT
,
158 static bool notifyAllListeners( ContainerT
& rContainer
,
162 typename
ContainerT::const_iterator
aCurr( rContainer
.begin() );
163 typename
ContainerT::const_iterator
const aEnd ( rContainer
.end() );
164 while( aCurr
!= aEnd
)
166 boost::shared_ptr
<ListenerTargetT
> pListener( aCurr
->lock() );
168 if( pListener
.get() &&
169 FunctionApply
< typename
FuncT::result_type
,
170 boost::shared_ptr
<ListenerTargetT
> >::apply(func
,pListener
) )
180 template< typename ContainerT
>
181 static void pruneListeners( ContainerT
& rContainer
,
182 size_t nSizeThreshold
)
184 if( rContainer
.size() <= nSizeThreshold
)
187 ContainerT aAliveListeners
;
188 aAliveListeners
.reserve(rContainer
.size());
190 typename
ContainerT::const_iterator
aCurr( rContainer
.begin() );
191 typename
ContainerT::const_iterator
const aEnd ( rContainer
.end() );
192 while( aCurr
!= aEnd
)
194 if( !aCurr
->expired() )
195 aAliveListeners
.push_back( *aCurr
);
200 std::swap( rContainer
, aAliveListeners
);
204 /** Container for objects that can be notified.
206 This templatized container holds listener objects, than can get
207 notified (by calling certain methods on them).
210 Type for the listener objects to be held
213 Full type of the container to store the listener objects. Defaults
214 to std::vector<ListenerT>
216 @tpl MaxDeceasedListenerUllage
217 Threshold, from which upwards the listener container gets
218 pruned. Avoids frequent copying of nearly empty containers.
220 @attention internal class, not to be used. functionality is
221 incomplete, please use the Thread(Un)safeListenerContainer types
224 template< typename ListenerT
,
225 typename MutexHolderBaseT
,
226 typename ContainerT
=std::vector
<ListenerT
>,
227 size_t MaxDeceasedListenerUllage
=16 > class ListenerContainerBase
: public MutexHolderBaseT
229 typedef typename
MutexHolderBaseT::Guard Guard
;
230 typedef typename
MutexHolderBaseT::ClearableGuard ClearableGuard
;
233 typedef ListenerT listener_type
;
234 typedef ContainerT container_type
;
235 typedef MutexHolderBaseT mutex_type
;
237 /** Check whether listener container is empty
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.
246 return maListeners
.empty();
249 /** Check whether given listener is already added
251 @return true, if given listener is already added.
253 bool isAdded( listener_type
const& rListener
) const
257 const typename
container_type::const_iterator
aEnd( maListeners
.end() );
258 if( std::find( maListeners
.begin(),
260 rListener
) != aEnd
)
262 return true; // already added
273 @return false, if the listener is already added, true
276 bool add( listener_type
const& rListener
)
281 if( isAdded(rListener
) )
282 return false; // already added
284 maListeners
.push_back( rListener
);
286 ListenerOperations
<ListenerT
>::pruneListeners(
288 MaxDeceasedListenerUllage
);
293 /** Add new listener into sorted container
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.
304 @return false, if the listener is already added, true
307 bool addSorted( listener_type
const& rListener
)
312 if( isAdded(rListener
) )
313 return false; // already added
315 maListeners
.push_back( rListener
);
317 // a single entry does not need to be sorted
318 if( maListeners
.size() > 1 )
322 boost::prior(maListeners
.end()),
326 ListenerOperations
<ListenerT
>::pruneListeners(
328 MaxDeceasedListenerUllage
);
333 /** Remove listener from container
336 The listener to remove
338 @return false, if listener not found in container, true
341 bool remove( listener_type
const& rListener
)
345 const typename
container_type::iterator
aEnd( maListeners
.end() );
346 typename
container_type::iterator aIter
;
347 if( (aIter
=std::remove(maListeners
.begin(),
349 rListener
)) == aEnd
)
351 return false; // listener not found
354 maListeners
.erase( aIter
, aEnd
);
359 /// Removes all listeners in one go
367 /** Apply functor to one listener
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.
374 Given functor is called with listeners, until it returns true
376 @return true, if functor was successfully applied to a
379 template< typename FuncT
> bool apply( FuncT func
) const
381 ClearableGuard
aGuard(*this);
383 // generate a local copy of all handlers, to make method
384 // reentrant and thread-safe.
385 container_type
const local( maListeners
);
389 ListenerOperations
<ListenerT
>::notifySingleListener(
394 Guard
aGuard2(*this);
395 ListenerOperations
<ListenerT
>::pruneListeners(
396 const_cast<container_type
&>(maListeners
),
397 MaxDeceasedListenerUllage
);
403 /** Apply functor to all listeners
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.
410 Given functor is called with listeners.
412 @return true, if functor was successfully applied to at least
415 template< typename FuncT
> bool applyAll( FuncT func
) const
417 ClearableGuard
aGuard(*this);
419 // generate a local copy of all handlers, to make method
420 // reentrant and thread-safe.
421 container_type
const local( maListeners
);
425 ListenerOperations
<ListenerT
>::notifyAllListeners(
430 Guard
aGuard2(*this);
431 ListenerOperations
<ListenerT
>::pruneListeners(
432 const_cast<container_type
&>(maListeners
),
433 MaxDeceasedListenerUllage
);
440 ContainerT maListeners
;
445 /** ListenerContainer variant that serialized access
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
452 template< typename ListenerT
,
453 typename ContainerT
=std::vector
<ListenerT
> >
454 class ThreadSafeListenerContainer
: public ListenerContainerBase
<ListenerT
,
462 /** ListenerContainer variant that does not serialize access
464 This ListenerContainer version is not safe to use in a
465 multi-threaded scenario, but has less overhead.
467 template< typename ListenerT
,
468 typename ContainerT
=std::vector
<ListenerT
> >
469 class ThreadUnsafeListenerContainer
: public ListenerContainerBase
<ListenerT
,
475 } // namespace internal
476 } // namespace slideshow
478 #endif // INCLUDED_SLIDESHOW_SOURCE_INC_LISTENERCONTAINER_HXX
480 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */