1 /**************************************************************
2 *
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 *
20 *************************************************************/
21
22
23
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_charttools.hxx"
26 #include "LifeTime.hxx"
27 #include "macros.hxx"
28 #include <osl/diagnose.h>
29
30 #include <com/sun/star/util/XModifyListener.hpp>
31 #include <com/sun/star/util/XCloseListener.hpp>
32
33 using namespace ::com::sun::star;
34
35 namespace apphelper
36 {
37 //--------------------------
38
LifeTimeManager(lang::XComponent * pComponent,sal_Bool bLongLastingCallsCancelable)39 LifeTimeManager::LifeTimeManager( lang::XComponent* pComponent, sal_Bool bLongLastingCallsCancelable )
40 : m_aListenerContainer( m_aAccessMutex )
41 , m_pComponent(pComponent)
42 , m_bLongLastingCallsCancelable(bLongLastingCallsCancelable)
43 {
44 impl_init();
45 }
46
impl_init()47 void LifeTimeManager::impl_init()
48 {
49 m_bDisposed = sal_False;
50 m_bInDispose = sal_False;
51 m_nAccessCount = 0;
52 m_nLongLastingCallCount = 0;
53 m_aNoAccessCountCondition.set();
54 m_aNoLongLastingCallCountCondition.set();
55 }
56
~LifeTimeManager()57 LifeTimeManager::~LifeTimeManager()
58 {
59 }
60
impl_isDisposed(bool bAssert)61 bool LifeTimeManager::impl_isDisposed( bool bAssert )
62 {
63 if( m_bDisposed || m_bInDispose )
64 {
65 if( bAssert )
66 {
67 OSL_ENSURE( sal_False, "This component is already disposed " );
68 (void)(bAssert);
69 }
70 return sal_True;
71 }
72 return sal_False;
73 }
74 sal_Bool LifeTimeManager
impl_canStartApiCall()75 ::impl_canStartApiCall()
76 {
77 if( impl_isDisposed() )
78 return sal_False; //behave passive if already disposed
79
80 //mutex is acquired
81 return sal_True;
82 }
83
84 void LifeTimeManager
impl_registerApiCall(sal_Bool bLongLastingCall)85 ::impl_registerApiCall(sal_Bool bLongLastingCall)
86 {
87 //only allowed if not disposed
88 //do not acquire the mutex here because it will be acquired already
89 m_nAccessCount++;
90 if(m_nAccessCount==1)
91 //@todo? is it ok to wake some threads here while we have acquired the mutex?
92 m_aNoAccessCountCondition.reset();
93
94 if(bLongLastingCall)
95 m_nLongLastingCallCount++;
96 if(m_nLongLastingCallCount==1)
97 m_aNoLongLastingCallCountCondition.reset();
98 }
99 void LifeTimeManager
impl_unregisterApiCall(sal_Bool bLongLastingCall)100 ::impl_unregisterApiCall(sal_Bool bLongLastingCall)
101 {
102 //Mutex needs to be acquired exactly ones
103 //mutex may be released inbetween in special case of impl_apiCallCountReachedNull()
104
105 OSL_ENSURE( m_nAccessCount>0, "access count mismatch" );
106 m_nAccessCount--;
107 if(bLongLastingCall)
108 m_nLongLastingCallCount--;
109 if( m_nLongLastingCallCount==0 )
110 {
111 m_aNoLongLastingCallCountCondition.set();
112 }
113 if( m_nAccessCount== 0)
114 {
115 m_aNoAccessCountCondition.set();
116 impl_apiCallCountReachedNull();
117
118 }
119 }
120
121 sal_Bool LifeTimeManager
dispose()122 ::dispose()
123 {
124 //hold no mutex
125 {
126 osl::Guard< osl::Mutex > aGuard( m_aAccessMutex );
127
128 if( m_bDisposed || m_bInDispose )
129 {
130 OSL_TRACE( "This component is already disposed " );
131 return sal_False; //behave passive if already disposed
132 }
133
134 m_bInDispose = true;
135 //adding any listener is not allowed anymore
136 //new calls will not be accepted
137 //still running calls have the freedom to finish their work without crash
138 }
139 //no mutex is acquired
140
141 //--do the disposing of listeners after calling this method
142 {
143 uno::Reference< lang::XComponent > xComponent =
144 uno::Reference< lang::XComponent >(m_pComponent);
145 if(xComponent.is())
146 {
147 // notify XCLoseListeners
148 lang::EventObject aEvent( xComponent );
149 m_aListenerContainer.disposeAndClear( aEvent );
150 }
151 }
152
153 //no mutex is acquired
154 {
155 osl::ClearableGuard< osl::Mutex > aGuard( m_aAccessMutex );
156 OSL_ENSURE( !m_bDisposed, "dispose was called already" );
157 m_bDisposed = sal_True;
158 aGuard.clear();
159 }
160 //no mutex is acquired
161
162 //wait until all still running calls have finished
163 //the accessCount cannot grow anymore, because all calls will return after checking m_bDisposed
164 m_aNoAccessCountCondition.wait();
165
166 //we are the only ones working on our data now
167
168 return sal_True;
169 //--release all resources and references after calling this method successful
170 }
171
172 //-----------------------------------------------------------------
173
CloseableLifeTimeManager(::com::sun::star::util::XCloseable * pCloseable,::com::sun::star::lang::XComponent * pComponent,sal_Bool bLongLastingCallsCancelable)174 CloseableLifeTimeManager::CloseableLifeTimeManager( ::com::sun::star::util::XCloseable* pCloseable
175 , ::com::sun::star::lang::XComponent* pComponent
176 , sal_Bool bLongLastingCallsCancelable )
177 : LifeTimeManager( pComponent, bLongLastingCallsCancelable )
178 , m_pCloseable(pCloseable)
179 {
180 impl_init();
181 }
182
~CloseableLifeTimeManager()183 CloseableLifeTimeManager::~CloseableLifeTimeManager()
184 {
185 }
186
impl_isDisposedOrClosed(bool bAssert)187 bool CloseableLifeTimeManager::impl_isDisposedOrClosed( bool bAssert )
188 {
189 if( impl_isDisposed( bAssert ) )
190 return sal_True;
191
192 if( m_bClosed )
193 {
194 if( bAssert )
195 {
196 OSL_ENSURE( sal_False, "This object is already closed" );
197 (void)(bAssert);//avoid warnings
198 }
199 return sal_True;
200 }
201 return sal_False;
202 }
203
204 sal_Bool CloseableLifeTimeManager
g_close_startTryClose(sal_Bool bDeliverOwnership)205 ::g_close_startTryClose(sal_Bool bDeliverOwnership)
206 {
207 //no mutex is allowed to be acquired
208 {
209 osl::ResettableGuard< osl::Mutex > aGuard( m_aAccessMutex );
210 if( impl_isDisposedOrClosed(false) )
211 return sal_False;
212
213 //Mutex needs to be acquired exactly ones; will be released inbetween
214 if( !impl_canStartApiCall() )
215 return sal_False;
216 //mutex is acquired
217
218 //not closed already -> we try to close again
219 m_bInTryClose = sal_True;
220 m_aEndTryClosingCondition.reset();
221
222 impl_registerApiCall(sal_False);
223 }
224
225 //------------------------------------------------
226 //no mutex is acquired
227
228 //only remove listener calls will be worked on until end of tryclose
229 //all other new calls will wait till end of try close // @todo? is that really ok
230
231 //?? still running calls have the freedom to finish their work without crash
232
233 try
234 {
235 uno::Reference< util::XCloseable > xCloseable =
236 uno::Reference< util::XCloseable >(m_pCloseable);
237 if(xCloseable.is())
238 {
239 //--call queryClosing on all registered close listeners
240 ::cppu::OInterfaceContainerHelper* pIC = m_aListenerContainer.getContainer(
241 ::getCppuType((const uno::Reference< util::XCloseListener >*)0) );
242 if( pIC )
243 {
244 //lang::EventObject aEvent( static_cast< util::XCloseable*>(xCloseable) );
245 lang::EventObject aEvent( xCloseable );
246 ::cppu::OInterfaceIteratorHelper aIt( *pIC );
247 while( aIt.hasMoreElements() )
248 {
249 uno::Reference< util::XCloseListener > xCloseListener( aIt.next(), uno::UNO_QUERY );
250 if(xCloseListener.is())
251 xCloseListener->queryClosing( aEvent, bDeliverOwnership );
252 }
253 }
254 }
255 }
256 catch( uno::Exception& ex )
257 {
258 //no mutex is acquired
259 g_close_endTryClose(bDeliverOwnership, sal_False);
260 (void)(ex);
261 throw;
262 }
263 return sal_True;
264 }
265
266 void CloseableLifeTimeManager
g_close_endTryClose(sal_Bool bDeliverOwnership,sal_Bool)267 ::g_close_endTryClose(sal_Bool bDeliverOwnership, sal_Bool /* bMyVeto */ )
268 {
269 //this method is called, if the try to close was not successful
270 osl::Guard< osl::Mutex > aGuard( m_aAccessMutex );
271 impl_setOwnership( bDeliverOwnership, sal_False );
272
273 m_bInTryClose = sal_False;
274 m_aEndTryClosingCondition.set();
275
276 //Mutex needs to be acquired exactly ones
277 //mutex may be released inbetween in special case of impl_apiCallCountReachedNull()
278 impl_unregisterApiCall(sal_False);
279 }
280
281 sal_Bool CloseableLifeTimeManager
g_close_isNeedToCancelLongLastingCalls(sal_Bool bDeliverOwnership,util::CloseVetoException & ex)282 ::g_close_isNeedToCancelLongLastingCalls( sal_Bool bDeliverOwnership, util::CloseVetoException& ex )
283 {
284 //this method is called when no closelistener has had a veto during queryclosing
285 //the method returns false, if nothing stands against closing anymore
286 //it returns true, if some longlasting calls are running, which might be cancelled
287 //it throws the given exception, if long calls are running but not cancelable
288
289 osl::Guard< osl::Mutex > aGuard( m_aAccessMutex );
290 //this count cannot grow after try of close has started, because we wait in all those methods for end of try closing
291 if( !m_nLongLastingCallCount )
292 return sal_False;
293
294 if(m_bLongLastingCallsCancelable)
295 return sal_True;
296
297 impl_setOwnership( bDeliverOwnership, sal_True );
298
299 m_bInTryClose = sal_False;
300 m_aEndTryClosingCondition.set();
301
302 //Mutex needs to be acquired exactly ones
303 //mutex may be released inbetween in special case of impl_apiCallCountReachedNull()
304 impl_unregisterApiCall(sal_False);
305
306 throw ex;
307 }
308
309 void CloseableLifeTimeManager
g_close_endTryClose_doClose()310 ::g_close_endTryClose_doClose()
311 {
312 //this method is called, if the try to close was successful
313 osl::ResettableGuard< osl::Mutex > aGuard( m_aAccessMutex );
314
315 m_bInTryClose = sal_False;
316 m_aEndTryClosingCondition.set();
317
318 //Mutex needs to be acquired exactly ones
319 //mutex may be released inbetween in special case of impl_apiCallCountReachedNull()
320 impl_unregisterApiCall(sal_False);
321 impl_doClose();
322 }
323
324 void CloseableLifeTimeManager
impl_setOwnership(sal_Bool bDeliverOwnership,sal_Bool bMyVeto)325 ::impl_setOwnership( sal_Bool bDeliverOwnership, sal_Bool bMyVeto )
326 {
327 m_bOwnership = bDeliverOwnership && bMyVeto;
328 m_bOwnershipIsWellKnown = sal_True;
329 }
330 sal_Bool CloseableLifeTimeManager
impl_shouldCloseAtNextChance()331 ::impl_shouldCloseAtNextChance()
332 {
333 return m_bOwnership;
334 }
335
336 void CloseableLifeTimeManager
impl_apiCallCountReachedNull()337 ::impl_apiCallCountReachedNull()
338 {
339 //Mutex needs to be acquired exactly ones
340 //mutex will be released inbetween in impl_doClose()
341 if( m_pCloseable && impl_shouldCloseAtNextChance() )
342 impl_doClose();
343 }
344
345 void CloseableLifeTimeManager
impl_doClose()346 ::impl_doClose()
347 {
348 //Mutex needs to be acquired exactly ones before calling impl_doClose()
349
350 if(m_bClosed)
351 return; //behave as passive as possible, if disposed or closed already
352 if( m_bDisposed || m_bInDispose )
353 return; //behave as passive as possible, if disposed or closed already
354
355 //--------
356 m_bClosed = sal_True;
357
358 NegativeGuard< osl::Mutex > aNegativeGuard( m_aAccessMutex );
359 //mutex is not acquired, mutex will be reacquired at the end of this method automatically
360
361 uno::Reference< util::XCloseable > xCloseable=NULL;
362 try
363 {
364 xCloseable = uno::Reference< util::XCloseable >(m_pCloseable);
365 if(xCloseable.is())
366 {
367 //--call notifyClosing on all registered close listeners
368 ::cppu::OInterfaceContainerHelper* pIC = m_aListenerContainer.getContainer(
369 ::getCppuType((const uno::Reference< util::XCloseListener >*)0) );
370 if( pIC )
371 {
372 //lang::EventObject aEvent( static_cast< util::XCloseable*>(xCloseable) );
373 lang::EventObject aEvent( xCloseable );
374 ::cppu::OInterfaceIteratorHelper aIt( *pIC );
375 while( aIt.hasMoreElements() )
376 {
377 uno::Reference< util::XCloseListener > xListener( aIt.next(), uno::UNO_QUERY );
378 if( xListener.is() )
379 xListener->notifyClosing( aEvent );
380 }
381 }
382 }
383 }
384 catch( uno::Exception& ex )
385 {
386 ASSERT_EXCEPTION( ex );
387 }
388
389 if(xCloseable.is())
390 {
391 uno::Reference< lang::XComponent > xComponent =
392 uno::Reference< lang::XComponent >( xCloseable, uno::UNO_QUERY );
393 if(xComponent.is())
394 {
395 OSL_ENSURE( m_bClosed, "a not closed component will be disposed " );
396 xComponent->dispose();
397 }
398 }
399 //mutex will be reacquired in destructor of aNegativeGuard
400 }
401
402 sal_Bool CloseableLifeTimeManager
g_addCloseListener(const uno::Reference<util::XCloseListener> & xListener)403 ::g_addCloseListener( const uno::Reference< util::XCloseListener > & xListener )
404 {
405 osl::Guard< osl::Mutex > aGuard( m_aAccessMutex );
406 //Mutex needs to be acquired exactly ones; will be released inbetween
407 if( !impl_canStartApiCall() )
408 return sal_False;
409 //mutex is acquired
410
411 m_aListenerContainer.addInterface( ::getCppuType((const uno::Reference< util::XCloseListener >*)0),xListener );
412 m_bOwnership = sal_False;
413 return sal_True;
414 }
415
416 sal_Bool CloseableLifeTimeManager
impl_canStartApiCall()417 ::impl_canStartApiCall()
418 {
419 //Mutex needs to be acquired exactly ones before calling this method
420 //the mutex will be released inbetween and reacquired
421
422 if( impl_isDisposed() )
423 return sal_False; //behave passive if already disposed
424 if( m_bClosed )
425 return sal_False; //behave passive if closing is already done
426
427 //during try-close most calls need to wait for the decision
428 while( m_bInTryClose )
429 {
430 //if someone tries to close this object at the moment
431 //we need to wait for his end because the result of the preceding call
432 //is relevant for our behaviour here
433
434 m_aAccessMutex.release();
435 m_aEndTryClosingCondition.wait(); //@todo??? this may block??? try closing
436 m_aAccessMutex.acquire();
437 if( m_bDisposed || m_bInDispose || m_bClosed )
438 return sal_False; //return if closed already
439 }
440 //mutex is acquired
441 return sal_True;
442 }
443
444 //--------------------------
445
446 sal_Bool LifeTimeGuard
startApiCall(sal_Bool bLongLastingCall)447 ::startApiCall(sal_Bool bLongLastingCall)
448 {
449 //Mutex needs to be acquired exactly ones; will be released inbetween
450 //mutex is required due to constructor of LifeTimeGuard
451
452 OSL_ENSURE( !m_bCallRegistered, "this method is only allowed ones" );
453 if(m_bCallRegistered)
454 return sal_False;
455
456 //Mutex needs to be acquired exactly ones; will be released inbetween
457 if( !m_rManager.impl_canStartApiCall() )
458 return sal_False;
459 //mutex is acquired
460
461 m_bCallRegistered = sal_True;
462 m_bLongLastingCallRegistered = bLongLastingCall;
463 m_rManager.impl_registerApiCall(bLongLastingCall);
464 return sal_True;
465 }
466
~LifeTimeGuard()467 LifeTimeGuard::~LifeTimeGuard()
468 {
469 try
470 {
471 //do acquire the mutex if it was cleared before
472 osl::MutexGuard g(m_rManager.m_aAccessMutex);
473 if(m_bCallRegistered)
474 {
475 //Mutex needs to be acquired exactly ones
476 //mutex may be released inbetween in special case of impl_apiCallCountReachedNull()
477 m_rManager.impl_unregisterApiCall(m_bLongLastingCallRegistered);
478 }
479 }
480 catch( uno::Exception& ex )
481 {
482 //@todo ? allow a uno::RuntimeException from dispose to travel through??
483 ex.Context.is(); //to avoid compilation warnings
484 }
485 }
486
487 /*
488 the XCloseable::close method has to be implemented in the following way:
489 ::close
490 {
491 //hold no mutex
492
493 if( !m_aLifeTimeManager.g_close_startTryClose( bDeliverOwnership ) )
494 return;
495 //no mutex is acquired
496
497 // At the end of this method may we must dispose ourself ...
498 // and may nobody from outside hold a reference to us ...
499 // then it's a good idea to do that by ourself.
500 uno::Reference< uno::XInterface > xSelfHold( static_cast< ::cppu::OWeakObject* >(this) );
501
502 //the listeners have had no veto
503 //check whether we self can close
504 {
505 util::CloseVetoException aVetoException = util::CloseVetoException(
506 ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
507 "the model itself could not be closed" ) )
508 , static_cast< ::cppu::OWeakObject* >(this));
509
510 if( m_aLifeTimeManager.g_close_isNeedToCancelLongLastingCalls( bDeliverOwnership, aVetoException ) )
511 {
512 ////you can empty this block, if you never start longlasting calls or
513 ////if your longlasting calls are per default not cancelable (check how you have constructed your LifeTimeManager)
514
515 sal_Bool bLongLastingCallsAreCanceled = sal_False;
516 try
517 {
518 //try to cancel running longlasting calls
519 //// @todo
520 }
521 catch( uno::Exception& ex )
522 {
523 //// @todo
524 //do not throw anything here!! (without endTryClose)
525 }
526 //if not successful canceled
527 if(!bLongLastingCallsAreCanceled)
528 {
529 m_aLifeTimeManager.g_close_endTryClose( bDeliverOwnership, sal_True );
530 throw aVetoException;
531 }
532 }
533
534 }
535 m_aLifeTimeManager.g_close_endTryClose_doClose();
536 }
537 */
538
539 }//end namespace apphelper
540