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