LCOV - code coverage report
Current view: top level - slideshow/source/inc - listenercontainer.hxx (source / functions) Hit Total Coverage
Test: commit c8344322a7af75b84dd3ca8f78b05543a976dfd5 Lines: 42 108 38.9 %
Date: 2015-06-13 12:38:46 Functions: 75 167 44.9 %
Legend: Lines: hit not hit

          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: */

Generated by: LCOV version 1.11