1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 #ifndef INCLUDED_SLIDESHOW_LISTENERCONTAINER_HXX
24 #define INCLUDED_SLIDESHOW_LISTENERCONTAINER_HXX
25 
26 #include <osl/mutex.hxx>
27 #include <boost/version.hpp>
28 #if BOOST_VERSION < 106700
29 # include <boost/utility.hpp>
30 #else
31 # include <boost/next_prior.hpp>
32 #endif
33 #include <algorithm>
34 #include <vector>
35 
36 #include "listenercontainerimpl.hxx"
37 
38 namespace slideshow {
39 namespace internal {
40 
41 /** Container for objects that can be notified.
42 
43     This templatized container holds listener objects, than can get
44     notified (by calling certain methods on them).
45 
46     @tpl Listener
47     Type for the listener objects to be held
48 
49     @tpl ContainerT
50     Full type of the container to store the listener objects. Defaults
51     to std::vector<ListenerT>
52 
53     @tpl MaxDeceasedListenerUllage
54     Threshold, from which upwards the listener container gets
55     pruned. Avoids frequent copying of nearly empty containers.
56 
57     @attention internal class, not to be used. functionality is
58     incomplete, please use the Thread(Un)safeListenerContainer types
59     from below
60 */
61 template< typename ListenerT,
62           typename MutexHolderBaseT,
63           typename ContainerT=std::vector<ListenerT>,
64           size_t MaxDeceasedListenerUllage=16 > class ListenerContainerBase : public MutexHolderBaseT
65 {
66     typedef typename MutexHolderBaseT::Guard           Guard;
67     typedef typename MutexHolderBaseT::ClearableGuard  ClearableGuard;
68 
69 public:
70     typedef ListenerT        listener_type;
71     typedef ContainerT       container_type;
72     typedef MutexHolderBaseT mutex_type;
73 
74     /** Check whether listener container is empty
75 
76         @return true, if currently no listeners registered. Note that
77         in a multi-threaded scenario, without external synchronisation
78         to this object, the return value might become wrong at any time.
79      */
isEmpty() const80     bool isEmpty() const
81     {
82         Guard aGuard(*this);
83         return maListeners.empty();
84     }
85 
86     /** Check whether given listener is already added
87 
88         @return true, if given listener is already added.
89      */
isAdded(listener_type const & rListener) const90     bool isAdded( listener_type const& rListener ) const
91     {
92         Guard aGuard(*this);
93 
94         const typename container_type::const_iterator aEnd( maListeners.end() );
95         if( std::find( maListeners.begin(),
96                        aEnd,
97                        rListener ) != aEnd )
98         {
99             return true; // already added
100         }
101 
102         return false;
103     }
104 
105     /** Add new listener
106 
107         @param rListener
108         Listener to add
109 
110         @return false, if the listener is already added, true
111         otherwise
112      */
add(listener_type const & rListener)113     bool add( listener_type const& rListener )
114     {
115         Guard aGuard(*this);
116 
117         // ensure uniqueness
118         if( isAdded(rListener) )
119             return false; // already added
120 
121         maListeners.push_back( rListener );
122 
123         ListenerOperations<ListenerT>::pruneListeners(
124             maListeners,
125             MaxDeceasedListenerUllage);
126 
127         return true;
128     }
129 
130     /** Add new listener into sorted container
131 
132         The stored listeners are kept sorted (using this method
133         requires listener_type to have operator< defined on it). Make
134         sure to call addSorted() for <em>each</em> listener to add to
135         this container - sorting is performed under the assumption
136         that existing entries are already sorted.
137 
138         @param rListener
139         Listener to add
140 
141         @return false, if the listener is already added, true
142         otherwise
143      */
addSorted(listener_type const & rListener)144     bool addSorted( listener_type const& rListener )
145     {
146         Guard aGuard(*this);
147 
148         // ensure uniqueness
149         if( isAdded(rListener) )
150             return false; // already added
151 
152         maListeners.push_back( rListener );
153 
154         // a single entry does not need to be sorted
155         if( maListeners.size() > 1 )
156         {
157             std::inplace_merge(
158                 maListeners.begin(),
159                 boost::prior(maListeners.end()),
160                 maListeners.end() );
161         }
162 
163         ListenerOperations<ListenerT>::pruneListeners(
164             maListeners,
165             MaxDeceasedListenerUllage);
166 
167         return true;
168     }
169 
170     /** Remove listener from container
171 
172         @param rListener
173         The listener to remove
174 
175         @return false, if listener not found in container, true
176         otherwise
177      */
remove(listener_type const & rListener)178     bool remove( listener_type const& rListener )
179     {
180         Guard aGuard(*this);
181 
182         const typename container_type::iterator aEnd( maListeners.end() );
183         typename container_type::iterator       aIter;
184         if( (aIter=std::remove(maListeners.begin(),
185                                aEnd,
186                                rListener)) == aEnd )
187         {
188             return false; // listener not found
189         }
190 
191         maListeners.erase( aIter, aEnd );
192 
193         return true;
194     }
195 
196     /// Removes all listeners in one go
clear()197     void clear()
198     {
199         Guard aGuard(*this);
200 
201         maListeners.clear();
202     }
203 
204     /** Apply functor to one listener
205 
206         This method applies functor to one of the listeners. Starting
207         with the first entry of the container, the functor is called
208         with the listener entries, until it returns true.
209 
210         @param func
211         Given functor is called with listeners, until it returns true
212 
213         @return true, if functor was successfully applied to a
214         listener
215      */
apply(FuncT func) const216     template< typename FuncT > bool apply( FuncT func ) const
217     {
218         ClearableGuard aGuard(*this);
219 
220         // generate a local copy of all handlers, to make method
221         // reentrant and thread-safe.
222         container_type const local( maListeners );
223         aGuard.clear();
224 
225         const bool bRet(
226             ListenerOperations<ListenerT>::notifySingleListener(
227                 local,
228                 func ));
229 
230         {
231             Guard aGuard2(*this);
232             ListenerOperations<ListenerT>::pruneListeners(
233                 const_cast<container_type&>(maListeners),
234                 MaxDeceasedListenerUllage);
235         }
236 
237         return bRet;
238     }
239 
240     /** Apply functor to all listeners
241 
242         This method applies functor to all of the listeners. Starting
243         with the first entry of the container, the functor is called
244         with the listener entries.
245 
246         @param func
247         Given functor is called with listeners.
248 
249         @return true, if functor was successfully applied to at least
250         one listener
251      */
applyAll(FuncT func) const252     template< typename FuncT > bool applyAll( FuncT func ) const
253     {
254         ClearableGuard aGuard(*this);
255 
256         // generate a local copy of all handlers, to make method
257         // reentrant and thread-safe.
258         container_type const local( maListeners );
259         aGuard.clear();
260 
261         const bool bRet(
262             ListenerOperations<ListenerT>::notifyAllListeners(
263                 local,
264                 func ));
265 
266         {
267             Guard aGuard2(*this);
268             ListenerOperations<ListenerT>::pruneListeners(
269                 const_cast<container_type&>(maListeners),
270                 MaxDeceasedListenerUllage);
271         }
272 
273         return bRet;
274     }
275 
276 private:
277     ContainerT  maListeners;
278 };
279 
280 ////////////////////////////////////////////////////////////////////////////
281 
282 /** ListenerContainer variant that serialized access
283 
284     This ListenerContainer is safe to use in a multi-threaded
285     context. It serializes access to the object, and avoids
286     dead-locking by releasing the object mutex before calling
287     listeners.
288  */
289 template< typename ListenerT,
290           typename ContainerT=std::vector<ListenerT> >
291 class ThreadSafeListenerContainer : public ListenerContainerBase<ListenerT,
292                                                                  MutexBase,
293                                                                  ContainerT>
294 {
295 };
296 
297 ////////////////////////////////////////////////////////////////////////////
298 
299 /** ListenerContainer variant that does not serialize access
300 
301     This ListenerContainer version is not safe to use in a
302     multi-threaded scenario, but has less overhead.
303  */
304 template< typename ListenerT,
305           typename ContainerT=std::vector<ListenerT> >
306 class ThreadUnsafeListenerContainer : public ListenerContainerBase<ListenerT,
307                                                                    EmptyBase,
308                                                                    ContainerT>
309 {
310 };
311 
312 } // namespace internal
313 } // namespace slideshow
314 
315 #endif /* INCLUDED_SLIDESHOW_LISTENERCONTAINER_HXX */
316 
317