Version 5.4.3.2, tag libreoffice-5.4.3.2
[LibreOffice.git] / include / vcl / lazydelete.hxx
blob63196185701a4ccb9bd987505b9466361c24ccfb
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #ifndef INCLUDED_VCL_LAZYDELETE_HXX
21 #define INCLUDED_VCL_LAZYDELETE_HXX
23 #include <vcl/dllapi.h>
24 #include <vcl/vclptr.hxx>
25 #include <vcl/window.hxx>
27 #include <unordered_map>
28 #include <vector>
29 #include <algorithm>
31 #include <typeinfo>
33 #include <com/sun/star/lang/XComponent.hpp>
35 namespace vcl
37 /* Helpers for lazy object deletion
39 With vcl it is often necessary to delete objects (especially Windows)
40 in the right order as well as in a way ensuring that the deleted objects
41 are not still on the stack (e.g. deleting a Window in its key handler). To
42 make this easier a helper class is given here which takes care of both
43 sorting as well as lazy deletion.
45 The grisly details:
46 LazyDelete is a class that LazyDeletor register to. When vcl's event
47 loop (that is Application::Yield or Application::Reschedule) comes out
48 of the last level, the LazyDelete::flush is called. This will cause
49 LazyDelete to delete all registered LazyDeletor objects.
51 LazyDeletor<T> is a one instance object that contains a list of
52 <T> objects to be deleted in sorted order. It is derived from
53 LazyDeletorBase as to be able to register itself in LazyDelete.
55 The user calls the static method LazyDeletor<T>::Delete( T* ) with the
56 object to be destroyed lazy. The static method creates the LazyDeletor<T>
57 (which in turn registers itself in LazyDelete) if this is the first time
58 a T* is to be destroyed lazy. It then inserts the object. When the LazyDeletor<T>
59 gets delte it will delete the stored objects in a fashion
60 that will ensure the correct order of deletion via the specialized is_less method
61 (e.g. if a Window is a child of another Window and therefore should be destroyed
62 first it is "less" in this sense)
64 LazyDelete::flush will be called when the top of the nested event loop is
65 reached again and will then destroy each registered LazyDeletor<T> which
66 in turn destroys the objects needed to be destroyed lazily. After this
67 the state is as before entering the event loop.
69 Preconditions:
70 - The class <T> of which objects are to be destroyed needs a virtual
71 destructor or must be final, else the wrong type will be destroyed.
72 - The destructor of <T> should call LazyDeletor<T>::Undelete( this ). This
73 prevents duplicate deletion in case someone destroys the object prematurely.
76 class LazyDeletorBase;
77 class VCL_DLLPUBLIC LazyDelete
79 public:
80 /** flush all registered object lists
82 static void flush();
83 /** register an object list to be destroyed
85 static void addDeletor( LazyDeletorBase* pDeletor );
88 class VCL_DLLPUBLIC LazyDeletorBase
90 friend void LazyDelete::flush();
91 protected:
92 LazyDeletorBase();
93 virtual ~LazyDeletorBase();
96 class VCL_DLLPUBLIC LazyDeletor : public LazyDeletorBase
98 static LazyDeletor* s_pOneInstance;
100 struct DeleteObjectEntry
102 VclPtr<vcl::Window> m_pObject;
103 bool m_bDeleted;
105 DeleteObjectEntry( vcl::Window* i_pObject ) :
106 m_pObject( i_pObject ),
107 m_bDeleted( false )
111 std::vector< DeleteObjectEntry > m_aObjects;
112 typedef std::unordered_map< sal_IntPtr, unsigned int > PtrToIndexMap;
113 PtrToIndexMap m_aPtrToIndex;
115 /** strict weak ordering function to bring objects to be destroyed lazily
116 in correct order, e.g. for Window objects children before parents
118 static bool is_less( vcl::Window* left, vcl::Window* right );
120 LazyDeletor() { LazyDelete::addDeletor( this ); }
121 virtual ~LazyDeletor() override
123 SAL_INFO("vcl.lazydelete", typeid(*this).name() << std::hex << this << " deleted");
124 if( s_pOneInstance == this ) // sanity check
125 s_pOneInstance = nullptr;
127 // do the actual work
128 unsigned int nCount = m_aObjects.size();
129 std::vector< VclPtr < vcl::Window > > aRealDelete;
130 aRealDelete.reserve( nCount );
131 for( unsigned int i = 0; i < nCount; i++ )
133 if( ! m_aObjects[i].m_bDeleted )
135 aRealDelete.push_back( m_aObjects[i].m_pObject );
138 // sort the vector of objects to be destroyed
139 std::sort( aRealDelete.begin(), aRealDelete.end(), is_less );
140 nCount = aRealDelete.size();
141 for( unsigned int n = 0; n < nCount; n++ )
143 SAL_INFO("vcl.lazydelete", typeid(*this).name() << " deletes object " << aRealDelete[n] << " of type "
144 << typeid(*aRealDelete[n]).name());
145 // check if the object to be deleted is not already destroyed
146 // as a side effect of a previous lazily destroyed object
147 if( ! m_aObjects[ m_aPtrToIndex[ reinterpret_cast<sal_IntPtr>(aRealDelete[n].get()) ] ].m_bDeleted )
148 aRealDelete[n].disposeAndClear();
152 public:
153 /** mark an object for lazy deletion
155 static void Delete( vcl::Window* i_pObject )
157 if( s_pOneInstance == nullptr )
158 s_pOneInstance = new LazyDeletor();
160 // is this object already in the list ?
161 // if so mark it as not to be deleted; else insert it
162 PtrToIndexMap::const_iterator dup = s_pOneInstance->m_aPtrToIndex.find( reinterpret_cast<sal_IntPtr>(i_pObject) );
163 if( dup != s_pOneInstance->m_aPtrToIndex.end() )
165 s_pOneInstance->m_aObjects[ dup->second ].m_bDeleted = false;
167 else
169 s_pOneInstance->m_aPtrToIndex[ reinterpret_cast<sal_IntPtr>(i_pObject) ] = s_pOneInstance->m_aObjects.size();
170 s_pOneInstance->m_aObjects.push_back( DeleteObjectEntry( i_pObject ) );
173 /** unmark an object already marked for lazy deletion
175 static void Undelete( vcl::Window* i_pObject )
177 if( s_pOneInstance )
179 PtrToIndexMap::const_iterator it = s_pOneInstance->m_aPtrToIndex.find( reinterpret_cast<sal_IntPtr>(i_pObject) );
180 if( it != s_pOneInstance->m_aPtrToIndex.end() )
181 s_pOneInstance->m_aObjects[ it->second ].m_bDeleted = true;
187 class DeleteOnDeinit matches a similar need as LazyDelete for static objects:
188 you may not access vcl objects after DeInitVCL has been called this includes their destruction
189 therefore disallowing the existence of static vcl object like e.g. a static BitmapEx
190 To work around this use DeleteOnDeinit<BitmapEx> which will allow you to have a static object container,
191 that will have its contents destroyed on DeinitVCL. The single drawback is that you need to check on the
192 container object whether it still contains content before actually accessing it.
194 caveat: when constructing a vcl object, you certainly want to ensure that InitVCL has run already.
195 However this is not necessarily the case when using a class static member or a file level static variable.
196 In these cases make judicious use of the set() method of DeleteOnDeinit, but beware of the changing
197 ownership.
199 example use case: use a lazy initialized on call BitmapEx in a paint method. Of course a paint method
200 would not normally be called after DeInitVCL anyway, so the check might not be necessary in a
201 Window::Paint implementation, but always checking is a good idea.
203 SomeWindow::Paint()
205 static vcl::DeleteOnDeinit< BitmapEx > aBmp( new BitmapEx( ... ) );
207 if( aBmp.get() ) // check whether DeInitVCL has been called already
208 DrawBitmapEx( Point( 10, 10 ), *aBmp.get() );
212 class VCL_DLLPUBLIC DeleteOnDeinitBase
214 public:
215 static void SAL_DLLPRIVATE ImplDeleteOnDeInit();
216 virtual ~DeleteOnDeinitBase();
217 protected:
218 static void addDeinitContainer( DeleteOnDeinitBase* i_pContainer );
220 virtual void doCleanup() = 0;
223 template < typename T >
224 class DeleteOnDeinit : public DeleteOnDeinitBase
226 T* m_pT;
227 virtual void doCleanup() override { delete m_pT; m_pT = nullptr; }
228 public:
229 DeleteOnDeinit( T* i_pT ) : m_pT( i_pT ) { addDeinitContainer( this ); }
231 // get contents
232 T* get() { return m_pT; }
234 // set contents, returning old contents
235 // ownership is transferred !
236 T* set( T* i_pNew ) { T* pOld = m_pT; m_pT = i_pNew; return pOld; }
239 /** Similar to DeleteOnDeinit, the DeleteUnoReferenceOnDeinit
240 template class makes sure that a static UNO object is disposed
241 and released at the right time.
243 Use like
244 static DeleteUnoReferenceOnDeinit<lang::XMultiServiceFactory>
245 xStaticFactory (\<create factory object>);
246 Reference<lang::XMultiServiceFactory> xFactory (xStaticFactory.get());
247 if (xFactory.is())
248 \<do something with xFactory>
250 template <typename I>
251 class DeleteUnoReferenceOnDeinit : public vcl::DeleteOnDeinitBase
253 css::uno::Reference<I> m_xI;
254 virtual void doCleanup() override { set(nullptr); }
255 public:
256 DeleteUnoReferenceOnDeinit(const css::uno::Reference<I>& r_xI ) : m_xI( r_xI ) {
257 addDeinitContainer( this ); }
259 css::uno::Reference<I> get() { return m_xI; }
261 void set (const css::uno::Reference<I>& r_xNew )
263 css::uno::Reference< css::lang::XComponent> xComponent (m_xI, css::uno::UNO_QUERY);
264 m_xI = r_xNew;
265 if (xComponent.is()) try
267 xComponent->dispose();
269 catch( css::uno::Exception& )
276 #endif
277 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */