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