LCOV - code coverage report
Current view: top level - include/vcl - lazydelete.hxx (source / functions) Hit Total Coverage
Test: commit 0e63ca4fde4e446f346e35849c756a30ca294aab Lines: 47 56 83.9 %
Date: 2014-04-11 Functions: 33 41 80.5 %
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             : 
      20             : #ifndef INCLUDED_VCL_LAZYDELETE_HXX
      21             : #define INCLUDED_VCL_LAZYDELETE_HXX
      22             : 
      23             : #include <vcl/dllapi.h>
      24             : 
      25             : #include <vector>
      26             : #include <boost/unordered_map.hpp>
      27             : #include <algorithm>
      28             : 
      29             : #if OSL_DEBUG_LEVEL > 2
      30             : #include <typeinfo>
      31             : #include <stdio.h>
      32             : #endif
      33             : 
      34             : #include <com/sun/star/lang/XComponent.hpp>
      35             : 
      36             : namespace vcl
      37             : {
      38             :     /* Helpers for lazy object deletion
      39             : 
      40             :     With vcl it is often necessary to delete objects (especially Windows)
      41             :     in the right order as well as in a way ensuring that the deleted objects
      42             :     are not still on the stack (e.g. deleting a Window in its key handler). To
      43             :     make this easier a helper class is given here which takes care of both
      44             :     sorting as well as lazy deletion.
      45             : 
      46             :     The grisly details:
      47             :     LazyDelete is a class that LazyDeletor register to. When vcl's event
      48             :     loop (that is Application::Yield or Application::Reschedule) comes out
      49             :     of the last level, the LazyDelete::flush is called. This will cause
      50             :     LazyDelete to delete all registered LazyDeletor objects.
      51             : 
      52             :     LazyDeletor<T> is a one instance object that contains a list of
      53             :     <T> objects to be deleted in sorted order. It is derived from
      54             :     LazyDeletorBase as to be able to register itself in LazyDelete.
      55             : 
      56             :     The user calls the static method LazyDeletor<T>::Delete( T* ) with the
      57             :     object to be destroyed lazy. The static method creates the LazyDeletor<T>
      58             :     (which in turn registers itself in LazyDelete) if this is the first time
      59             :     a T* is to be destroyed lazy. It then inserts the object. When the LazyDeletor<T>
      60             :     gets delte it will delete the stored objects in a fashion
      61             :     that will ensure the correct order of deletion via the specialized is_less method
      62             :     (e.g. if a Window is a child of another Window and therefore should be destroyed
      63             :     first it is "less" in this sense)
      64             : 
      65             :     LazyDelete::flush will be called when the top of the nested event loop is
      66             :     reached again and will then destroy each registered LazyDeletor<T> which
      67             :     in turn destroys the objects needed to be destroyed lazily. After this
      68             :     the state is as before entering the event loop.
      69             : 
      70             :     Preconditions:
      71             :     - The class <T> of which objects are to be destroyed needs a virtual
      72             :     destructor or must be final, else the wrong type will be destroyed.
      73             :     - The destructor of <T> should call LazyDeletor<T>::Undelete( this ). This
      74             :     prevents duplicate deletionin case someone destroys the object prematurely.
      75             :     */
      76             : 
      77             :     class LazyDeletorBase;
      78             :     class VCL_DLLPUBLIC LazyDelete
      79             :     {
      80             :         public:
      81             :         /** flush all registered object lists
      82             :         */
      83             :         static void flush();
      84             :         /** register an object list to be destroyed
      85             :         */
      86             :         static void addDeletor( LazyDeletorBase* pDeletor );
      87             :     };
      88             : 
      89             :     class VCL_DLLPUBLIC LazyDeletorBase
      90             :     {
      91             :         friend void LazyDelete::flush();
      92             :         protected:
      93             :         LazyDeletorBase();
      94             :         virtual ~LazyDeletorBase();
      95             :     };
      96             : 
      97             :     template < typename T >
      98             :     class VCL_DLLPUBLIC LazyDeletor : public LazyDeletorBase
      99             :     {
     100             :         static LazyDeletor< T >*     s_pOneInstance;
     101             : 
     102             :         struct DeleteObjectEntry
     103             :         {
     104             :             T*      m_pObject;
     105             :             bool    m_bDeleted;
     106             : 
     107             :             DeleteObjectEntry() :
     108             :                 m_pObject( NULL ),
     109             :                 m_bDeleted( false )
     110             :             {}
     111             : 
     112        6001 :             DeleteObjectEntry( T* i_pObject ) :
     113             :                 m_pObject( i_pObject ),
     114        6001 :                 m_bDeleted( false )
     115        6001 :             {}
     116             :         };
     117             : 
     118             :         std::vector< DeleteObjectEntry >    m_aObjects;
     119             :         typedef boost::unordered_map< sal_IntPtr, unsigned int > PtrToIndexMap;
     120             :         PtrToIndexMap                       m_aPtrToIndex;
     121             : 
     122             :         /** strict weak ordering funtion to bring objects to be destroyed lazily
     123             :         in correct order, e.g. for Window objects children before parents
     124             :         */
     125             :         static bool is_less( T* left, T* right );
     126             : 
     127        1923 :         LazyDeletor()  { LazyDelete::addDeletor( this ); }
     128        3774 :         virtual ~LazyDeletor()
     129             :         {
     130             :             #if OSL_DEBUG_LEVEL > 2
     131             :             fprintf( stderr, "%s %p deleted\n",
     132             :                      typeid(*this).name(), this );
     133             :             #endif
     134        1887 :             if( s_pOneInstance == this ) // sanity check
     135        1887 :                 s_pOneInstance = NULL;
     136             : 
     137             :             // do the actual work
     138        1887 :             unsigned int nCount = m_aObjects.size();
     139        1887 :             std::vector<T*> aRealDelete;
     140        1887 :             aRealDelete.reserve( nCount );
     141        7755 :             for( unsigned int i = 0; i < nCount; i++ )
     142             :             {
     143        5868 :                 if( ! m_aObjects[i].m_bDeleted )
     144             :                 {
     145          88 :                     aRealDelete.push_back( m_aObjects[i].m_pObject );
     146             :                 }
     147             :             }
     148             :             // sort the vector of objects to be destroyed
     149        1887 :             std::sort( aRealDelete.begin(), aRealDelete.end(), is_less );
     150        1887 :             nCount = aRealDelete.size();
     151        1975 :             for( unsigned int n = 0; n < nCount; n++ )
     152             :             {
     153             :                 #if OSL_DEBUG_LEVEL > 2
     154             :                 fprintf( stderr, "%s deletes object %p of type %s\n",
     155             :                          typeid(*this).name(),
     156             :                          aRealDelete[n],
     157             :                          typeid(*aRealDelete[n]).name() );
     158             :                 #endif
     159             :                 // check if the object to be deleted is not already destroyed
     160             :                 // as a side effect of a previous lazily destroyed object
     161          88 :                 if( ! m_aObjects[ m_aPtrToIndex[ reinterpret_cast<sal_IntPtr>(aRealDelete[n]) ] ].m_bDeleted )
     162          88 :                     delete aRealDelete[n];
     163        1887 :             }
     164        5661 :         }
     165             : 
     166             :         public:
     167             :         /** mark an object for lazy deletion
     168             :         */
     169        6099 :         static void Delete( T* i_pObject )
     170             :         {
     171        6099 :             if( s_pOneInstance == NULL )
     172        1923 :                 s_pOneInstance = new LazyDeletor<T>();
     173             : 
     174             :             // is this object already in the list ?
     175             :             // if so mark it as not to be deleted; else insert it
     176        6099 :             PtrToIndexMap::const_iterator dup = s_pOneInstance->m_aPtrToIndex.find( reinterpret_cast<sal_IntPtr>(i_pObject) );
     177        6099 :             if( dup != s_pOneInstance->m_aPtrToIndex.end() )
     178             :             {
     179          98 :                 s_pOneInstance->m_aObjects[ dup->second ].m_bDeleted = false;
     180             :             }
     181             :             else
     182             :             {
     183        6001 :                 s_pOneInstance->m_aPtrToIndex[ reinterpret_cast<sal_IntPtr>(i_pObject) ] = s_pOneInstance->m_aObjects.size();
     184        6001 :                 s_pOneInstance->m_aObjects.push_back( DeleteObjectEntry( i_pObject ) );
     185             :             }
     186        6099 :         }
     187             :         /** unmark an object already marked for lazy deletion
     188             :         */
     189      163263 :         static void Undelete( T* i_pObject )
     190             :         {
     191      163263 :             if( s_pOneInstance )
     192             :             {
     193      109805 :                 PtrToIndexMap::const_iterator it = s_pOneInstance->m_aPtrToIndex.find( reinterpret_cast<sal_IntPtr>(i_pObject) );
     194      109805 :                 if( it != s_pOneInstance->m_aPtrToIndex.end() )
     195        6048 :                     s_pOneInstance->m_aObjects[ it->second ].m_bDeleted = true;
     196             :             }
     197      163263 :         }
     198             :     };
     199             : 
     200             :     /*
     201             :     class DeleteOnDeinit matches a similar need as LazyDelete for static objects:
     202             :     you may not access vcl objects after DeInitVCL has been called this includes their destruction
     203             :     therefore disallowing the existance of static vcl object like e.g. a static BitmapEx
     204             :     To work around this use DeleteOnDeinit<BitmapEx> which will allow you to have a static object container,
     205             :     that will have its contents destroyed on DeinitVCL. The single drawback is that you need to check on the
     206             :     container object whether it still contains content before actually accessing it.
     207             : 
     208             :     caveat: when constructing a vcl object, you certainly want to ensure that InitVCL has run already.
     209             :     However this is not necessarily the case when using a class static member or a file level static variable.
     210             :     In these cases make judicious use of the set() method of DeleteOnDeinit, but beware of the changing
     211             :     ownership.
     212             : 
     213             :     example use case: use a lazy initialized on call BitmapEx in a paint method. Of course a paint method
     214             :     would not normally be called after DeInitVCL anyway, so the check might not be necessary in a
     215             :     Window::Paint implementation, but always checking is a good idea.
     216             : 
     217             :     SomeWindow::Paint()
     218             :     {
     219             :         static vcl::DeleteOnDeinit< BitmapEx > aBmp( new BitmapEx( ResId( 1000, myResMgr ) ) );
     220             : 
     221             :         if( aBmp.get() ) // check whether DeInitVCL has been called already
     222             :             DrawBitmapEx( Point( 10, 10 ), *aBmp.get() );
     223             :     }
     224             :     */
     225             : 
     226         366 :     class VCL_DLLPUBLIC DeleteOnDeinitBase
     227             :     {
     228             :     public:
     229             :         static void SAL_DLLPRIVATE ImplDeleteOnDeInit();
     230             :         virtual ~DeleteOnDeinitBase();
     231             :     protected:
     232             :         static void addDeinitContainer( DeleteOnDeinitBase* i_pContainer );
     233             : 
     234             :         virtual void doCleanup() = 0;
     235             :     };
     236             : 
     237             :     template < typename T >
     238             :     class DeleteOnDeinit : public DeleteOnDeinitBase
     239             :     {
     240             :         T* m_pT;
     241         218 :         virtual void doCleanup() SAL_OVERRIDE { delete m_pT; m_pT = NULL; }
     242             :     public:
     243         363 :         DeleteOnDeinit( T* i_pT ) : m_pT( i_pT ) { addDeinitContainer( this ); }
     244         363 :         virtual ~DeleteOnDeinit() {}
     245             : 
     246             :         // get contents
     247       58175 :         T* get() { return m_pT; }
     248             : 
     249             :         // set contents, returning old contents
     250             :         // ownership is transferred !
     251           9 :         T* set( T* i_pNew ) { T* pOld = m_pT; m_pT = i_pNew; return pOld; }
     252             : 
     253             :         // set contents, deleting old contents
     254             :         // ownership is transferred !
     255          31 :         void reset( T* i_pNew = NULL )
     256          31 :             { OSL_ASSERT( i_pNew != m_pT || i_pNew == NULL ); T* pOld = m_pT; m_pT = i_pNew; delete pOld; }
     257             :     };
     258             : 
     259             :     /** Similar to DeleteOnDeinit, the DeleteUnoReferenceOnDeinit
     260             :         template class makes sure that a static UNO object is disposed
     261             :         and released at the right time.
     262             : 
     263             :         Use like
     264             :             static DeleteUnoReferenceOnDeinit<lang::XMultiServiceFactory>
     265             :                 xStaticFactory (\<create factory object>);
     266             :             Reference<lang::XMultiServiceFactory> xFactory (xStaticFactory.get());
     267             :             if (xFactory.is())
     268             :                 \<do something with xFactory>
     269             :     */
     270             :     template <typename I>
     271             :     class DeleteUnoReferenceOnDeinit : public ::vcl::DeleteOnDeinitBase
     272             :     {
     273             :         ::com::sun::star::uno::Reference<I> m_xI;
     274           0 :         virtual void doCleanup() SAL_OVERRIDE { set(NULL); }
     275             :     public:
     276           3 :         DeleteUnoReferenceOnDeinit(const ::com::sun::star::uno::Reference<I>& r_xI ) : m_xI( r_xI ) {
     277           3 :             addDeinitContainer( this ); }
     278           3 :         virtual ~DeleteUnoReferenceOnDeinit() {}
     279             : 
     280          21 :         ::com::sun::star::uno::Reference<I> get (void) { return m_xI; }
     281             : 
     282           0 :         void set (const ::com::sun::star::uno::Reference<I>& r_xNew )
     283             :         {
     284           0 :             ::com::sun::star::uno::Reference< ::com::sun::star::lang::XComponent> xComponent (m_xI, ::com::sun::star::uno::UNO_QUERY);
     285           0 :             m_xI = r_xNew;
     286           0 :             if (xComponent.is()) try
     287             :             {
     288           0 :                 xComponent->dispose();
     289             :             }
     290           0 :             catch( ::com::sun::star::uno::Exception& )
     291             :             {
     292           0 :             }
     293           0 :         }
     294             :     };
     295             : }
     296             : 
     297             : #endif
     298             : 
     299             : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.10