LCOV - code coverage report
Current view: top level - slideshow/source/inc - listenercontainer.hxx (source / functions) Hit Total Coverage
Test: commit 0e63ca4fde4e446f346e35849c756a30ca294aab Lines: 42 109 38.5 %
Date: 2014-04-11 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_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: */

Generated by: LCOV version 1.10