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