xref: /trunk/main/vcl/inc/vcl/lazydelete.hxx (revision cdf0e10c)
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