| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| |
| #ifndef _VCL_LAZYDELETE_HXX |
| #define _VCL_LAZYDELETE_HXX |
| |
| #include "dllapi.h" |
| |
| #include <vector> |
| #include <hash_map> |
| #include <algorithm> |
| |
| #if OSL_DEBUG_LEVEL > 2 |
| #include <typeinfo> |
| #include <stdio.h> |
| #endif |
| |
| #include <com/sun/star/lang/XComponent.hpp> |
| |
| namespace vcl |
| { |
| /* Helpers for lazy object deletion |
| |
| With vcl it is often necessary to delete objects (especially Windows) |
| in the right order as well as in a way ensuring that the deleted objects |
| are not still on the stack (e.g. deleting a Window in its key handler). To |
| make this easier a helper class is given here which takes care of both |
| sorting as well as lazy deletion. |
| |
| The grisly details: |
| LazyDelete is a class that LazyDeletor register to. When vcl's event |
| loop (that is Application::Yield or Application::Reschedule) comes out |
| of the last level, the LazyDelete::flush is called. This will cause |
| LazyDelete to delete all registered LazyDeletor objects. |
| |
| LazyDeletor<T> is a one instance object that contains a list of |
| <T> objects to be deleted in sorted order. It is derived from |
| LazyDeletorBase as to be able to register itself in LazyDelete. |
| |
| The user calls the static method LazyDeletor<T>::Delete( T* ) with the |
| object to be destroyed lazy. The static method creates the LazyDeletor<T> |
| (which in turn registers itself in LazyDelete) if this is the first time |
| a T* is to be destroyed lazy. It then inserts the object. When the LazyDeletor<T> |
| gets delte it will delete the stored objects in a fashion |
| that will ensure the correct order of deletion via the specialized is_less method |
| (e.g. if a Window is a child of another Window and therefore should be destroyed |
| first it is "less" in this sense) |
| |
| LazyDelete::flush will be called when the top of the nested event loop is |
| reached again and will then destroy each registered LazyDeletor<T> which |
| in turn destroys the objects needed to be destroyed lazily. After this |
| the state is as before entering the event loop. |
| |
| Preconditions: |
| - The class <T> of which objects are to be destroyed needs a virtual |
| destructor or must be final, else the wrong type will be destroyed. |
| - The destructor of <T> should call LazyDeletor<T>::Undelete( this ). This |
| prevents duplicate deletionin case someone destroys the object prematurely. |
| */ |
| |
| class LazyDeletorBase; |
| class VCL_DLLPUBLIC LazyDelete |
| { |
| public: |
| /** flush all registered object lists |
| */ |
| static void flush(); |
| /** register an object list to be destroyed |
| */ |
| static void addDeletor( LazyDeletorBase* pDeletor ); |
| }; |
| |
| class VCL_DLLPUBLIC LazyDeletorBase |
| { |
| friend void LazyDelete::flush(); |
| protected: |
| LazyDeletorBase(); |
| virtual ~LazyDeletorBase(); |
| }; |
| |
| template < typename T > |
| class VCL_DLLPUBLIC LazyDeletor : public LazyDeletorBase |
| { |
| static LazyDeletor< T >* s_pOneInstance; |
| |
| struct DeleteObjectEntry |
| { |
| T* m_pObject; |
| bool m_bDeleted; |
| |
| DeleteObjectEntry() : |
| m_pObject( NULL ), |
| m_bDeleted( false ) |
| {} |
| |
| DeleteObjectEntry( T* i_pObject ) : |
| m_pObject( i_pObject ), |
| m_bDeleted( false ) |
| {} |
| }; |
| |
| std::vector< DeleteObjectEntry > m_aObjects; |
| typedef std::hash_map< sal_IntPtr, unsigned int > PtrToIndexMap; |
| PtrToIndexMap m_aPtrToIndex; |
| |
| /** strict weak ordering funtion to bring objects to be destroyed lazily |
| in correct order, e.g. for Window objects children before parents |
| */ |
| static bool is_less( T* left, T* right ); |
| |
| LazyDeletor() { LazyDelete::addDeletor( this ); } |
| virtual ~LazyDeletor() |
| { |
| #if OSL_DEBUG_LEVEL > 2 |
| fprintf( stderr, "%s %p deleted\n", |
| typeid(*this).name(), this ); |
| #endif |
| if( s_pOneInstance == this ) // sanity check |
| s_pOneInstance = NULL; |
| |
| // do the actual work |
| unsigned int nCount = m_aObjects.size(); |
| std::vector<T*> aRealDelete; |
| aRealDelete.reserve( nCount ); |
| for( unsigned int i = 0; i < nCount; i++ ) |
| { |
| if( ! m_aObjects[i].m_bDeleted ) |
| { |
| aRealDelete.push_back( m_aObjects[i].m_pObject ); |
| } |
| } |
| // sort the vector of objects to be destroyed |
| std::sort( aRealDelete.begin(), aRealDelete.end(), is_less ); |
| nCount = aRealDelete.size(); |
| for( unsigned int n = 0; n < nCount; n++ ) |
| { |
| #if OSL_DEBUG_LEVEL > 2 |
| fprintf( stderr, "%s deletes object %p of type %s\n", |
| typeid(*this).name(), |
| aRealDelete[n], |
| typeid(*aRealDelete[n]).name() ); |
| #endif |
| // check if the object to be deleted is not already destroyed |
| // as a side effect of a previous lazily destroyed object |
| if( ! m_aObjects[ m_aPtrToIndex[ reinterpret_cast<sal_IntPtr>(aRealDelete[n]) ] ].m_bDeleted ) |
| delete aRealDelete[n]; |
| } |
| } |
| |
| public: |
| /** mark an object for lazy deletion |
| */ |
| static void Delete( T* i_pObject ) |
| { |
| if( s_pOneInstance == NULL ) |
| s_pOneInstance = new LazyDeletor<T>(); |
| |
| // is this object already in the list ? |
| // if so mark it as not to be deleted; else insert it |
| PtrToIndexMap::const_iterator dup = s_pOneInstance->m_aPtrToIndex.find( reinterpret_cast<sal_IntPtr>(i_pObject) ); |
| if( dup != s_pOneInstance->m_aPtrToIndex.end() ) |
| { |
| s_pOneInstance->m_aObjects[ dup->second ].m_bDeleted = false; |
| } |
| else |
| { |
| s_pOneInstance->m_aPtrToIndex[ reinterpret_cast<sal_IntPtr>(i_pObject) ] = s_pOneInstance->m_aObjects.size(); |
| s_pOneInstance->m_aObjects.push_back( DeleteObjectEntry( i_pObject ) ); |
| } |
| } |
| /** unmark an object already marked for lazy deletion |
| */ |
| static void Undelete( T* i_pObject ) |
| { |
| if( s_pOneInstance ) |
| { |
| PtrToIndexMap::const_iterator it = s_pOneInstance->m_aPtrToIndex.find( reinterpret_cast<sal_IntPtr>(i_pObject) ); |
| if( it != s_pOneInstance->m_aPtrToIndex.end() ) |
| s_pOneInstance->m_aObjects[ it->second ].m_bDeleted = true; |
| } |
| } |
| }; |
| |
| /* |
| class DeleteOnDeinit matches a similar need as LazyDelete for static objects: |
| you may not access vcl objects after DeInitVCL has been called this includes their destruction |
| therefore disallowing the existance of static vcl object like e.g. a static BitmapEx |
| To work around this use DeleteOnDeinit<BitmapEx> which will allow you to have a static object container, |
| that will have its contents destroyed on DeinitVCL. The single drawback is that you need to check on the |
| container object whether it still contains content before actually accessing it. |
| |
| caveat: when constructing a vcl object, you certainly want to ensure that InitVCL has run already. |
| However this is not necessarily the case when using a class static member or a file level static variable. |
| In these cases make judicious use of the set() method of DeleteOnDeinit, but beware of the changing |
| ownership. |
| |
| example use case: use a lazy initialized on call BitmapEx in a paint method. Of course a paint method |
| would not normally be called after DeInitVCL anyway, so the check might not be necessary in a |
| Window::Paint implementation, but always checking is a good idea. |
| |
| SomeWindow::Paint() |
| { |
| static vcl::DeleteOnDeinit< BitmapEx > aBmp( new BitmapEx( ResId( 1000, myResMgr ) ) ); |
| |
| if( aBmp.get() ) // check whether DeInitVCL has been called already |
| DrawBitmapEx( Point( 10, 10 ), *aBmp.get() ); |
| } |
| */ |
| |
| class VCL_DLLPUBLIC DeleteOnDeinitBase |
| { |
| public: |
| static void SAL_DLLPRIVATE ImplDeleteOnDeInit(); |
| virtual ~DeleteOnDeinitBase(); |
| protected: |
| static void addDeinitContainer( DeleteOnDeinitBase* i_pContainer ); |
| |
| virtual void doCleanup() = 0; |
| }; |
| |
| template < typename T > |
| class DeleteOnDeinit : public DeleteOnDeinitBase |
| { |
| T* m_pT; |
| virtual void doCleanup() { delete m_pT; m_pT = NULL; } |
| public: |
| DeleteOnDeinit( T* i_pT ) : m_pT( i_pT ) { addDeinitContainer( this ); } |
| virtual ~DeleteOnDeinit() {} |
| |
| // get contents |
| T* get() { return m_pT; } |
| |
| // set contents, returning old contents |
| // ownership is transfered ! |
| T* set( T* i_pNew ) { T* pOld = m_pT; m_pT = i_pNew; return pOld; } |
| }; |
| |
| /** Similar to DeleteOnDeinit, the DeleteUnoReferenceOnDeinit |
| template class makes sure that a static UNO object is disposed |
| and released at the right time. |
| |
| Use like |
| static DeleteUnoReferenceOnDeinit<lang::XMultiServiceFactory> |
| xStaticFactory (<create factory object>); |
| Reference<lang::XMultiServiceFactory> xFactory (xStaticFactory.get()); |
| if (xFactory.is()) |
| <do something with xFactory> |
| */ |
| template <typename I> |
| class DeleteUnoReferenceOnDeinit : public ::vcl::DeleteOnDeinitBase |
| { |
| ::com::sun::star::uno::Reference<I> m_xI; |
| virtual void doCleanup() { set(NULL); } |
| public: |
| DeleteUnoReferenceOnDeinit(const ::com::sun::star::uno::Reference<I>& r_xI ) : m_xI( r_xI ) { |
| addDeinitContainer( this ); } |
| virtual ~DeleteUnoReferenceOnDeinit() {} |
| |
| ::com::sun::star::uno::Reference<I> get (void) { return m_xI; } |
| |
| void set (const ::com::sun::star::uno::Reference<I>& r_xNew ) |
| { |
| ::com::sun::star::uno::Reference< ::com::sun::star::lang::XComponent> xComponent (m_xI, ::com::sun::star::uno::UNO_QUERY); |
| m_xI = r_xNew; |
| if (xComponent.is()) try |
| { |
| xComponent->dispose(); |
| } |
| catch( ::com::sun::star::uno::Exception& ) |
| { |
| } |
| } |
| }; |
| } |
| |
| #endif |
| |