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