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