Branch data 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 : : #include "LifeTime.hxx"
21 : : #include "macros.hxx"
22 : : #include <osl/diagnose.h>
23 : :
24 : : #include <com/sun/star/util/XModifyListener.hpp>
25 : : #include <com/sun/star/util/XCloseListener.hpp>
26 : :
27 : : using namespace ::com::sun::star;
28 : :
29 : : namespace apphelper
30 : : {
31 : : //--------------------------
32 : :
33 : 90 : LifeTimeManager::LifeTimeManager( lang::XComponent* pComponent, sal_Bool bLongLastingCallsCancelable )
34 : : : m_aListenerContainer( m_aAccessMutex )
35 : : , m_pComponent(pComponent)
36 [ + - ][ + - ]: 90 : , m_bLongLastingCallsCancelable(bLongLastingCallsCancelable)
[ + - ]
37 : : {
38 [ + - ]: 90 : impl_init();
39 : 90 : }
40 : :
41 : 90 : void LifeTimeManager::impl_init()
42 : : {
43 : 90 : m_bDisposed = sal_False;
44 : 90 : m_bInDispose = sal_False;
45 : 90 : m_nAccessCount = 0;
46 : 90 : m_nLongLastingCallCount = 0;
47 : 90 : m_aNoAccessCountCondition.set();
48 : 90 : m_aNoLongLastingCallCountCondition.set();
49 : 90 : }
50 : :
51 [ + - ][ + - ]: 90 : LifeTimeManager::~LifeTimeManager()
[ + - ]
52 : : {
53 [ - + ]: 90 : }
54 : :
55 : 8285 : bool LifeTimeManager::impl_isDisposed( bool bAssert )
56 : : {
57 [ + - ][ - + ]: 8285 : if( m_bDisposed || m_bInDispose )
[ - + ]
58 : : {
59 : : if( bAssert )
60 : : {
61 : : OSL_FAIL( "This component is already disposed " );
62 : : (void)(bAssert);
63 : : }
64 : 0 : return sal_True;
65 : : }
66 : 8285 : return sal_False;
67 : : }
68 : 0 : sal_Bool LifeTimeManager
69 : : ::impl_canStartApiCall()
70 : : {
71 [ # # ]: 0 : if( impl_isDisposed() )
72 : 0 : return sal_False; //behave passive if already disposed
73 : :
74 : : //mutex is acquired
75 : 0 : return sal_True;
76 : : }
77 : :
78 : 6111 : void LifeTimeManager
79 : : ::impl_registerApiCall(sal_Bool bLongLastingCall)
80 : : {
81 : : //only allowed if not disposed
82 : : //do not acquire the mutex here because it will be acquired already
83 : 6111 : m_nAccessCount++;
84 [ + + ]: 6111 : if(m_nAccessCount==1)
85 : : //@todo? is it ok to wake some threads here while we have acquired the mutex?
86 : 6048 : m_aNoAccessCountCondition.reset();
87 : :
88 [ - + ]: 6111 : if(bLongLastingCall)
89 : 0 : m_nLongLastingCallCount++;
90 [ - + ]: 6111 : if(m_nLongLastingCallCount==1)
91 : 0 : m_aNoLongLastingCallCountCondition.reset();
92 : 6111 : }
93 : 6111 : void LifeTimeManager
94 : : ::impl_unregisterApiCall(sal_Bool bLongLastingCall)
95 : : {
96 : : //Mutex needs to be acquired exactly ones
97 : : //mutex may be released inbetween in special case of impl_apiCallCountReachedNull()
98 : :
99 : : OSL_ENSURE( m_nAccessCount>0, "access count mismatch" );
100 : 6111 : m_nAccessCount--;
101 [ - + ]: 6111 : if(bLongLastingCall)
102 : 0 : m_nLongLastingCallCount--;
103 [ + - ]: 6111 : if( m_nLongLastingCallCount==0 )
104 : : {
105 : 6111 : m_aNoLongLastingCallCountCondition.set();
106 : : }
107 [ + + ]: 6111 : if( m_nAccessCount== 0)
108 : : {
109 : 6048 : m_aNoAccessCountCondition.set();
110 : 6048 : impl_apiCallCountReachedNull();
111 : :
112 : : }
113 : 6111 : }
114 : :
115 : 180 : sal_Bool LifeTimeManager
116 : : ::dispose() throw(uno::RuntimeException)
117 : : {
118 : : //hold no mutex
119 : : {
120 [ + - ]: 180 : osl::Guard< osl::Mutex > aGuard( m_aAccessMutex );
121 : :
122 [ + + ][ - + ]: 180 : if( m_bDisposed || m_bInDispose )
[ + + ]
123 : : {
124 : : OSL_TRACE( "This component is already disposed " );
125 : 90 : return sal_False; //behave passive if already disposed
126 : : }
127 : :
128 [ + - ][ + + ]: 180 : m_bInDispose = true;
129 : : //adding any listener is not allowed anymore
130 : : //new calls will not be accepted
131 : : //still running calls have the freedom to finish their work without crash
132 : : }
133 : : //no mutex is acquired
134 : :
135 : : //--do the disposing of listeners after calling this method
136 : : {
137 : : uno::Reference< lang::XComponent > xComponent =
138 [ + - ]: 180 : uno::Reference< lang::XComponent >(m_pComponent);;
139 [ + + ]: 90 : if(xComponent.is())
140 : : {
141 : : // notify XCLoseListeners
142 [ + - ]: 56 : lang::EventObject aEvent( xComponent );
143 [ + - ][ + - ]: 56 : m_aListenerContainer.disposeAndClear( aEvent );
144 : 90 : }
145 : : }
146 : :
147 : : //no mutex is acquired
148 : : {
149 [ + - ]: 90 : osl::ClearableGuard< osl::Mutex > aGuard( m_aAccessMutex );
150 : : OSL_ENSURE( !m_bDisposed, "dispose was called already" );
151 : 90 : m_bDisposed = sal_True;
152 [ + - ][ + - ]: 90 : aGuard.clear();
153 : : }
154 : : //no mutex is acquired
155 : :
156 : : //wait until all still running calls have finished
157 : : //the accessCount cannot grow anymore, because all calls will return after checking m_bDisposed
158 : 90 : m_aNoAccessCountCondition.wait();
159 : :
160 : : //we are the only ones working on our data now
161 : :
162 : 180 : return sal_True;
163 : : //--release all resources and references after calling this method successful
164 : : }
165 : :
166 : : //-----------------------------------------------------------------
167 : :
168 : 56 : CloseableLifeTimeManager::CloseableLifeTimeManager( ::com::sun::star::util::XCloseable* pCloseable
169 : : , ::com::sun::star::lang::XComponent* pComponent
170 : : , sal_Bool bLongLastingCallsCancelable )
171 : : : LifeTimeManager( pComponent, bLongLastingCallsCancelable )
172 [ + - ]: 56 : , m_pCloseable(pCloseable)
173 : : {
174 [ + - ]: 56 : impl_init();
175 : 56 : }
176 : :
177 [ + - ]: 56 : CloseableLifeTimeManager::~CloseableLifeTimeManager()
178 : : {
179 [ - + ]: 56 : }
180 : :
181 : 440 : bool CloseableLifeTimeManager::impl_isDisposedOrClosed( bool bAssert )
182 : : {
183 [ - + ]: 440 : if( impl_isDisposed( bAssert ) )
184 : 0 : return sal_True;
185 : :
186 [ + + ]: 440 : if( m_bClosed )
187 : : {
188 : : if( bAssert )
189 : : {
190 : : OSL_FAIL( "This object is already closed" );
191 : : (void)(bAssert);//avoid warnings
192 : : }
193 : 30 : return sal_True;
194 : : }
195 : 440 : return sal_False;
196 : : }
197 : :
198 : 56 : sal_Bool CloseableLifeTimeManager
199 : : ::g_close_startTryClose(sal_Bool bDeliverOwnership)
200 : : throw ( uno::Exception )
201 : : {
202 : : //no mutex is allowed to be acquired
203 : : {
204 [ + - ]: 56 : osl::ResettableGuard< osl::Mutex > aGuard( m_aAccessMutex );
205 [ + - ][ - + ]: 56 : if( impl_isDisposedOrClosed(false) )
206 : 0 : return sal_False;
207 : :
208 : : //Mutex needs to be acquired exactly ones; will be released inbetween
209 [ + - ][ - + ]: 56 : if( !impl_canStartApiCall() )
210 : 0 : return sal_False;
211 : : //mutex is acquired
212 : :
213 : : //not closed already -> we try to close again
214 : 56 : m_bInTryClose = sal_True;
215 [ + - ]: 56 : m_aEndTryClosingCondition.reset();
216 : :
217 [ + - ][ + - ]: 56 : impl_registerApiCall(sal_False);
[ + - ]
218 : : }
219 : :
220 : : //------------------------------------------------
221 : : //no mutex is acquired
222 : :
223 : : //only remove listener calls will be worked on until end of tryclose
224 : : //all other new calls will wait till end of try close // @todo? is that really ok
225 : :
226 : : //?? still running calls have the freedom to finish their work without crash
227 : :
228 : : try
229 : : {
230 : : uno::Reference< util::XCloseable > xCloseable =
231 [ + - ]: 56 : uno::Reference< util::XCloseable >(m_pCloseable);;
232 [ + - ]: 56 : if(xCloseable.is())
233 : : {
234 : : //--call queryClosing on all registered close listeners
235 : : ::cppu::OInterfaceContainerHelper* pIC = m_aListenerContainer.getContainer(
236 [ + - ][ + - ]: 56 : ::getCppuType((const uno::Reference< util::XCloseListener >*)0) );;
237 [ + - ]: 56 : if( pIC )
238 : : {
239 [ + - ]: 56 : lang::EventObject aEvent( xCloseable );
240 [ + - ]: 56 : ::cppu::OInterfaceIteratorHelper aIt( *pIC );
241 [ + + ]: 110 : while( aIt.hasMoreElements() )
242 : : {
243 [ + - ][ + - ]: 56 : uno::Reference< util::XCloseListener > xCloseListener( aIt.next(), uno::UNO_QUERY );
244 [ + - ]: 56 : if(xCloseListener.is())
245 [ + - ][ + + ]: 56 : xCloseListener->queryClosing( aEvent, bDeliverOwnership );
246 [ + - ][ + - ]: 110 : }
247 : : }
248 : 56 : }
249 : : }
250 [ - + ]: 4 : catch( const uno::Exception& )
251 : : {
252 : : //no mutex is acquired
253 [ - + ]: 2 : g_close_endTryClose(bDeliverOwnership, sal_False);
254 : 2 : throw;
255 : : }
256 : 54 : return sal_True;
257 : : }
258 : :
259 : 2 : void CloseableLifeTimeManager
260 : : ::g_close_endTryClose(sal_Bool bDeliverOwnership, sal_Bool /* bMyVeto */ )
261 : : {
262 : : //this method is called, if the try to close was not successfull
263 [ + - ]: 2 : osl::Guard< osl::Mutex > aGuard( m_aAccessMutex );
264 : 2 : impl_setOwnership( bDeliverOwnership, sal_False );
265 : :
266 : 2 : m_bInTryClose = sal_False;
267 [ + - ]: 2 : m_aEndTryClosingCondition.set();
268 : :
269 : : //Mutex needs to be acquired exactly ones
270 : : //mutex may be released inbetween in special case of impl_apiCallCountReachedNull()
271 [ + - ][ + - ]: 2 : impl_unregisterApiCall(sal_False);
272 : 2 : }
273 : :
274 : 54 : sal_Bool CloseableLifeTimeManager
275 : : ::g_close_isNeedToCancelLongLastingCalls( sal_Bool bDeliverOwnership, util::CloseVetoException& ex )
276 : : throw ( util::CloseVetoException )
277 : : {
278 : : //this method is called when no closelistener has had a veto during queryclosing
279 : : //the method returns false, if nothing stands against closing anymore
280 : : //it returns true, if some longlasting calls are running, which might be cancelled
281 : : //it throws the given exception, if long calls are running but not cancelable
282 : :
283 [ + - ]: 54 : osl::Guard< osl::Mutex > aGuard( m_aAccessMutex );
284 : : //this count cannot grow after try of close has started, because we wait in all those methods for end of try closing
285 [ + - ]: 54 : if( !m_nLongLastingCallCount )
286 : 54 : return sal_False;
287 : :
288 [ # # ]: 0 : if(m_bLongLastingCallsCancelable)
289 : 0 : return sal_True;
290 : :
291 : 0 : impl_setOwnership( bDeliverOwnership, sal_True );
292 : :
293 : 0 : m_bInTryClose = sal_False;
294 [ # # ]: 0 : m_aEndTryClosingCondition.set();
295 : :
296 : : //Mutex needs to be acquired exactly ones
297 : : //mutex may be released inbetween in special case of impl_apiCallCountReachedNull()
298 [ # # ]: 0 : impl_unregisterApiCall(sal_False);
299 : :
300 [ # # ][ + - ]: 54 : throw ex;
301 : : }
302 : :
303 : 54 : void CloseableLifeTimeManager
304 : : ::g_close_endTryClose_doClose()
305 : : {
306 : : //this method is called, if the try to close was successfull
307 [ + - ]: 54 : osl::ResettableGuard< osl::Mutex > aGuard( m_aAccessMutex );
308 : :
309 : 54 : m_bInTryClose = sal_False;
310 [ + - ]: 54 : m_aEndTryClosingCondition.set();
311 : :
312 : : //Mutex needs to be acquired exactly ones
313 : : //mutex may be released inbetween in special case of impl_apiCallCountReachedNull()
314 [ + - ]: 54 : impl_unregisterApiCall(sal_False);
315 [ + - ][ + - ]: 54 : impl_doClose();
316 : 54 : }
317 : :
318 : 2 : void CloseableLifeTimeManager
319 : : ::impl_setOwnership( sal_Bool bDeliverOwnership, sal_Bool bMyVeto )
320 : : {
321 [ + - ][ - + ]: 2 : m_bOwnership = bDeliverOwnership && bMyVeto;
322 : 2 : m_bOwnershipIsWellKnown = sal_True;
323 : 2 : }
324 : 6048 : sal_Bool CloseableLifeTimeManager
325 : : ::impl_shouldCloseAtNextChance()
326 : : {
327 : 6048 : return m_bOwnership;
328 : : }
329 : :
330 : 6048 : void CloseableLifeTimeManager
331 : : ::impl_apiCallCountReachedNull()
332 : : {
333 : : //Mutex needs to be acquired exactly ones
334 : : //mutex will be released inbetween in impl_doClose()
335 [ + - ][ - + ]: 6048 : if( m_pCloseable && impl_shouldCloseAtNextChance() )
[ - + ]
336 : 0 : impl_doClose();
337 : 6048 : }
338 : :
339 : 54 : void CloseableLifeTimeManager
340 : : ::impl_doClose()
341 : : {
342 : : //Mutex needs to be acquired exactly ones before calling impl_doClose()
343 : :
344 [ + - ]: 54 : if(m_bClosed)
345 : : return; //behave as passive as possible, if disposed or closed already
346 [ + - ][ - + ]: 54 : if( m_bDisposed || m_bInDispose )
[ + - ]
347 : : return; //behave as passive as possible, if disposed or closed already
348 : :
349 : : //--------
350 : 54 : m_bClosed = sal_True;
351 : :
352 [ + - ]: 54 : NegativeGuard< osl::Mutex > aNegativeGuard( m_aAccessMutex );
353 : : //mutex is not acquired, mutex will be reacquired at the end of this method automatically
354 : :
355 [ + - ]: 54 : uno::Reference< util::XCloseable > xCloseable=NULL;
356 : : try
357 : : {
358 [ + - ][ + - ]: 54 : xCloseable = uno::Reference< util::XCloseable >(m_pCloseable);;
359 [ + - ]: 54 : if(xCloseable.is())
360 : : {
361 : : //--call notifyClosing on all registered close listeners
362 : : ::cppu::OInterfaceContainerHelper* pIC = m_aListenerContainer.getContainer(
363 [ + - ][ + - ]: 54 : ::getCppuType((const uno::Reference< util::XCloseListener >*)0) );;
364 [ + - ]: 54 : if( pIC )
365 : : {
366 [ + - ]: 54 : lang::EventObject aEvent( xCloseable );
367 [ + - ]: 54 : ::cppu::OInterfaceIteratorHelper aIt( *pIC );
368 [ + + ]: 108 : while( aIt.hasMoreElements() )
369 : : {
370 [ + - ][ + - ]: 54 : uno::Reference< util::XCloseListener > xListener( aIt.next(), uno::UNO_QUERY );
371 [ + - ]: 54 : if( xListener.is() )
372 [ + - ][ + - ]: 54 : xListener->notifyClosing( aEvent );
373 [ + - ][ + - ]: 108 : }
[ # # ]
374 : : }
375 : : }
376 : : }
377 [ # # ]: 0 : catch( const uno::Exception& ex )
378 : : {
379 : : ASSERT_EXCEPTION( ex );
380 : : }
381 : :
382 [ + - ]: 54 : if(xCloseable.is())
383 : : {
384 : : uno::Reference< lang::XComponent > xComponent =
385 [ + - ]: 54 : uno::Reference< lang::XComponent >( xCloseable, uno::UNO_QUERY );
386 [ + - ]: 54 : if(xComponent.is())
387 : : {
388 : : OSL_ENSURE( m_bClosed, "a not closed component will be disposed " );
389 [ + - ][ + - ]: 54 : xComponent->dispose();
390 : 54 : }
391 [ + - ]: 54 : }
392 : : //mutex will be reacquired in destructor of aNegativeGuard
393 : : }
394 : :
395 : 58 : sal_Bool CloseableLifeTimeManager
396 : : ::g_addCloseListener( const uno::Reference< util::XCloseListener > & xListener )
397 : : throw(uno::RuntimeException)
398 : : {
399 [ + - ]: 58 : osl::Guard< osl::Mutex > aGuard( m_aAccessMutex );
400 : : //Mutex needs to be acquired exactly ones; will be released inbetween
401 [ + - ][ - + ]: 58 : if( !impl_canStartApiCall() )
402 : 0 : return sal_False;
403 : : //mutex is acquired
404 : :
405 [ + - ][ + - ]: 58 : m_aListenerContainer.addInterface( ::getCppuType((const uno::Reference< util::XCloseListener >*)0),xListener );
406 : 58 : m_bOwnership = sal_False;
407 [ + - ]: 58 : return sal_True;
408 : : }
409 : :
410 : 6169 : sal_Bool CloseableLifeTimeManager
411 : : ::impl_canStartApiCall()
412 : : {
413 : : //Mutex needs to be acquired exactly ones before calling this method
414 : : //the mutex will be released inbetween and reacquired
415 : :
416 [ - + ]: 6169 : if( impl_isDisposed() )
417 : 0 : return sal_False; //behave passive if already disposed
418 [ - + ]: 6169 : if( m_bClosed )
419 : 0 : return sal_False; //behave passive if closing is already done
420 : :
421 : : //during try-close most calls need to wait for the decision
422 [ - + ]: 6169 : while( m_bInTryClose )
423 : : {
424 : : //if someone tries to close this object at the moment
425 : : //we need to wait for his end because the result of the preceding call
426 : : //is relevant for our behaviour here
427 : :
428 : 0 : m_aAccessMutex.release();
429 : 0 : m_aEndTryClosingCondition.wait(); //@todo??? this may block??? try closing
430 : 0 : m_aAccessMutex.acquire();
431 [ # # ][ # # ]: 0 : if( m_bDisposed || m_bInDispose || m_bClosed )
[ # # ][ # # ]
432 : 0 : return sal_False; //return if closed already
433 : : }
434 : : //mutex is acquired
435 : 6169 : return sal_True;
436 : : }
437 : :
438 : : //--------------------------
439 : :
440 : 6055 : sal_Bool LifeTimeGuard
441 : : ::startApiCall(sal_Bool bLongLastingCall)
442 : : {
443 : : //Mutex needs to be acquired exactly ones; will be released inbetween
444 : : //mutex is requiered due to constructor of LifeTimeGuard
445 : :
446 : : OSL_ENSURE( !m_bCallRegistered, "this method is only allowed ones" );
447 [ - + ]: 6055 : if(m_bCallRegistered)
448 : 0 : return sal_False;
449 : :
450 : : //Mutex needs to be acquired exactly ones; will be released inbetween
451 [ - + ]: 6055 : if( !m_rManager.impl_canStartApiCall() )
452 : 0 : return sal_False;
453 : : //mutex is acquired
454 : :
455 : 6055 : m_bCallRegistered = sal_True;
456 : 6055 : m_bLongLastingCallRegistered = bLongLastingCall;
457 : 6055 : m_rManager.impl_registerApiCall(bLongLastingCall);
458 : 6055 : return sal_True;
459 : : }
460 : :
461 : 6055 : LifeTimeGuard::~LifeTimeGuard()
462 : : {
463 : : try
464 : : {
465 : : //do acquire the mutex if it was cleared before
466 [ + - ]: 6055 : osl::MutexGuard g(m_rManager.m_aAccessMutex);
467 [ + - ]: 6055 : if(m_bCallRegistered)
468 : : {
469 : : //Mutex needs to be acquired exactly ones
470 : : //mutex may be released inbetween in special case of impl_apiCallCountReachedNull()
471 [ + - ]: 6055 : m_rManager.impl_unregisterApiCall(m_bLongLastingCallRegistered);
472 [ + - ][ # # ]: 6055 : }
473 : : }
474 [ # # ]: 0 : catch( uno::Exception& ex )
475 : : {
476 : : //@todo ? allow a uno::RuntimeException from dispose to travel through??
477 : 0 : ex.Context.is(); //to avoid compilation warnings
478 : : }
479 : 6055 : }
480 : :
481 : : }//end namespace apphelper
482 : :
483 : : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|