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

Generated by: LCOV version 1.11