xref: /trunk/main/chart2/source/tools/LifeTime.cxx (revision 74cbd1f1)
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_chart2.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() throw(uno::RuntimeException)
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 	throw ( uno::Exception )
207 {
208 	//no mutex is allowed to be acquired
209 	{
210 		osl::ResettableGuard< osl::Mutex > aGuard( m_aAccessMutex );
211         if( impl_isDisposedOrClosed(false) )
212             return sal_False;
213 
214 		//Mutex needs to be acquired exactly ones; will be released inbetween
215 		if( !impl_canStartApiCall() )
216 			return sal_False;
217 		//mutex is acquired
218 
219 		//not closed already -> we try to close again
220 		m_bInTryClose = sal_True;
221 		m_aEndTryClosingCondition.reset();
222 
223 		impl_registerApiCall(sal_False);
224 	}
225 
226 	//------------------------------------------------
227 	//no mutex is acquired
228 
229 	//only remove listener calls will be worked on until end of tryclose
230 	//all other new calls will wait till end of try close // @todo? is that really ok
231 
232 	//?? still running calls have the freedom to finish their work without crash
233 
234 	try
235 	{
236 		uno::Reference< util::XCloseable > xCloseable =
237 			uno::Reference< util::XCloseable >(m_pCloseable);;
238 		if(xCloseable.is())
239 		{
240 			//--call queryClosing on all registered close listeners
241 			::cppu::OInterfaceContainerHelper* pIC = m_aListenerContainer.getContainer(
242 						::getCppuType((const uno::Reference< util::XCloseListener >*)0) );;
243 			if( pIC )
244 			{
245 				//lang::EventObject aEvent( static_cast< util::XCloseable*>(xCloseable) );
246 				lang::EventObject aEvent( xCloseable );
247 				::cppu::OInterfaceIteratorHelper aIt( *pIC );
248 				while( aIt.hasMoreElements() )
249                 {
250                     uno::Reference< util::XCloseListener > xCloseListener( aIt.next(), uno::UNO_QUERY );
251                     if(xCloseListener.is())
252 					    xCloseListener->queryClosing( aEvent, bDeliverOwnership );
253                 }
254 			}
255 		}
256 	}
257 	catch( uno::Exception& ex )
258 	{
259 		//no mutex is acquired
260 		g_close_endTryClose(bDeliverOwnership, sal_False);
261         (void)(ex);
262         throw;
263 	}
264 	return sal_True;
265 }
266 
267 	void CloseableLifeTimeManager
g_close_endTryClose(sal_Bool bDeliverOwnership,sal_Bool)268 ::g_close_endTryClose(sal_Bool bDeliverOwnership, sal_Bool /* bMyVeto */ )
269 {
270 	//this method is called, if the try to close was not successful
271 	osl::Guard< osl::Mutex > aGuard( m_aAccessMutex );
272 	impl_setOwnership( bDeliverOwnership, sal_False );
273 
274 	m_bInTryClose = sal_False;
275 	m_aEndTryClosingCondition.set();
276 
277 	//Mutex needs to be acquired exactly ones
278 	//mutex may be released inbetween in special case of impl_apiCallCountReachedNull()
279 	impl_unregisterApiCall(sal_False);
280 }
281 
282 	sal_Bool CloseableLifeTimeManager
g_close_isNeedToCancelLongLastingCalls(sal_Bool bDeliverOwnership,util::CloseVetoException & ex)283 ::g_close_isNeedToCancelLongLastingCalls( sal_Bool bDeliverOwnership, util::CloseVetoException& ex )
284 	throw ( util::CloseVetoException )
285 {
286 	//this method is called when no closelistener has had a veto during queryclosing
287 	//the method returns false, if nothing stands against closing anymore
288 	//it returns true, if some longlasting calls are running, which might be cancelled
289 	//it throws the given exception, if long calls are running but not cancelable
290 
291 	osl::Guard< osl::Mutex > aGuard( m_aAccessMutex );
292 	//this count cannot grow after try of close has started, because we wait in all those methods for end of try closing
293 	if( !m_nLongLastingCallCount )
294 		return sal_False;
295 
296   	if(m_bLongLastingCallsCancelable)
297 		return sal_True;
298 
299 	impl_setOwnership( bDeliverOwnership, sal_True );
300 
301 	m_bInTryClose = sal_False;
302 	m_aEndTryClosingCondition.set();
303 
304 	//Mutex needs to be acquired exactly ones
305 	//mutex may be released inbetween in special case of impl_apiCallCountReachedNull()
306 	impl_unregisterApiCall(sal_False);
307 
308 	throw ex;
309 }
310 
311 	void CloseableLifeTimeManager
g_close_endTryClose_doClose()312 ::g_close_endTryClose_doClose()
313 {
314 	//this method is called, if the try to close was successful
315 	osl::ResettableGuard< osl::Mutex > aGuard( m_aAccessMutex );
316 
317 	m_bInTryClose		= sal_False;
318 	m_aEndTryClosingCondition.set();
319 
320 	//Mutex needs to be acquired exactly ones
321 	//mutex may be released inbetween in special case of impl_apiCallCountReachedNull()
322 	impl_unregisterApiCall(sal_False);
323 	impl_doClose();
324 }
325 
326 	void CloseableLifeTimeManager
impl_setOwnership(sal_Bool bDeliverOwnership,sal_Bool bMyVeto)327 ::impl_setOwnership( sal_Bool bDeliverOwnership, sal_Bool bMyVeto )
328 {
329 	m_bOwnership			= bDeliverOwnership && bMyVeto;
330 	m_bOwnershipIsWellKnown	= sal_True;
331 }
332 	sal_Bool CloseableLifeTimeManager
impl_shouldCloseAtNextChance()333 ::impl_shouldCloseAtNextChance()
334 {
335 	return m_bOwnership;
336 }
337 
338 	void CloseableLifeTimeManager
impl_apiCallCountReachedNull()339 ::impl_apiCallCountReachedNull()
340 {
341 	//Mutex needs to be acquired exactly ones
342 	//mutex will be released inbetween in impl_doClose()
343 	if( m_pCloseable && impl_shouldCloseAtNextChance() )
344 		impl_doClose();
345 }
346 
347 	void CloseableLifeTimeManager
impl_doClose()348 ::impl_doClose()
349 {
350 	//Mutex needs to be acquired exactly ones before calling impl_doClose()
351 
352 	if(m_bClosed)
353 		return; //behave as passive as possible, if disposed or closed already
354 	if( m_bDisposed || m_bInDispose )
355 		return; //behave as passive as possible, if disposed or closed already
356 
357 	//--------
358 	m_bClosed = sal_True;
359 
360 	NegativeGuard< osl::Mutex > aNegativeGuard( m_aAccessMutex );
361 	//mutex is not acquired, mutex will be reacquired at the end of this method automatically
362 
363 	uno::Reference< util::XCloseable > xCloseable=NULL;
364 	try
365 	{
366 		xCloseable = uno::Reference< util::XCloseable >(m_pCloseable);;
367 		if(xCloseable.is())
368 		{
369 			//--call notifyClosing on all registered close listeners
370 			::cppu::OInterfaceContainerHelper* pIC = m_aListenerContainer.getContainer(
371 						::getCppuType((const uno::Reference< util::XCloseListener >*)0) );;
372 			if( pIC )
373 			{
374 				//lang::EventObject aEvent( static_cast< util::XCloseable*>(xCloseable) );
375 				lang::EventObject aEvent( xCloseable );
376 				::cppu::OInterfaceIteratorHelper aIt( *pIC );
377 				while( aIt.hasMoreElements() )
378                 {
379                     uno::Reference< util::XCloseListener > xListener( aIt.next(), uno::UNO_QUERY );
380                     if( xListener.is() )
381                         xListener->notifyClosing( aEvent );
382                 }
383 			}
384 		}
385 	}
386 	catch( uno::Exception& ex )
387 	{
388 		ASSERT_EXCEPTION( ex );
389 	}
390 
391 	if(xCloseable.is())
392 	{
393 		uno::Reference< lang::XComponent > xComponent =
394 			uno::Reference< lang::XComponent >( xCloseable, uno::UNO_QUERY );
395 		if(xComponent.is())
396 		{
397 			OSL_ENSURE( m_bClosed, "a not closed component will be disposed " );
398 			xComponent->dispose();
399 		}
400 	}
401 	//mutex will be reacquired in destructor of aNegativeGuard
402 }
403 
404 	sal_Bool CloseableLifeTimeManager
g_addCloseListener(const uno::Reference<util::XCloseListener> & xListener)405 ::g_addCloseListener( const uno::Reference<	util::XCloseListener > & xListener )
406 	throw(uno::RuntimeException)
407 {
408 	osl::Guard< osl::Mutex > aGuard( m_aAccessMutex );
409 	//Mutex needs to be acquired exactly ones; will be released inbetween
410 	if( !impl_canStartApiCall() )
411 		return sal_False;
412 	//mutex is acquired
413 
414 	m_aListenerContainer.addInterface( ::getCppuType((const uno::Reference< util::XCloseListener >*)0),xListener );
415 	m_bOwnership = sal_False;
416 	return sal_True;
417 }
418 
419 	sal_Bool CloseableLifeTimeManager
impl_canStartApiCall()420 ::impl_canStartApiCall()
421 {
422 	//Mutex needs to be acquired exactly ones before calling this method
423 	//the mutex will be released inbetween and reacquired
424 
425 	if( impl_isDisposed() )
426 		return sal_False; //behave passive if already disposed
427 	if( m_bClosed )
428 		return sal_False; //behave passive if closing is already done
429 
430 	//during try-close most calls need to wait for the decision
431 	while( m_bInTryClose )
432 	{
433 		//if someone tries to close this object at the moment
434 		//we need to wait for his end because the result of the preceding call
435 		//is relevant for our behaviour here
436 
437 		m_aAccessMutex.release();
438 		m_aEndTryClosingCondition.wait(); //@todo??? this may block??? try closing
439 		m_aAccessMutex.acquire();
440 		if( m_bDisposed || m_bInDispose || m_bClosed )
441 			return sal_False; //return if closed already
442 	}
443 	//mutex is acquired
444 	return sal_True;
445 }
446 
447 //--------------------------
448 
449 	sal_Bool LifeTimeGuard
startApiCall(sal_Bool bLongLastingCall)450 ::startApiCall(sal_Bool bLongLastingCall)
451 {
452 	//Mutex needs to be acquired exactly ones; will be released inbetween
453 	//mutex is requiered due to constructor of LifeTimeGuard
454 
455 	OSL_ENSURE( !m_bCallRegistered, "this method is only allowed ones" );
456 	if(m_bCallRegistered)
457 		return sal_False;
458 
459 	//Mutex needs to be acquired exactly ones; will be released inbetween
460 	if( !m_rManager.impl_canStartApiCall() )
461 		return sal_False;
462 	//mutex is acquired
463 
464 	m_bCallRegistered = sal_True;
465 	m_bLongLastingCallRegistered = bLongLastingCall;
466 	m_rManager.impl_registerApiCall(bLongLastingCall);
467 	return sal_True;
468 }
469 
~LifeTimeGuard()470 LifeTimeGuard::~LifeTimeGuard()
471 {
472 	try
473 	{
474 		//do acquire the mutex if it was cleared before
475         osl::MutexGuard g(m_rManager.m_aAccessMutex);
476 		if(m_bCallRegistered)
477 		{
478 			//Mutex needs to be acquired exactly ones
479 			//mutex may be released inbetween in special case of impl_apiCallCountReachedNull()
480 			m_rManager.impl_unregisterApiCall(m_bLongLastingCallRegistered);
481 		}
482 	}
483 	catch( uno::Exception& ex )
484 	{
485 		//@todo ? allow a uno::RuntimeException from dispose to travel through??
486         ex.Context.is(); //to avoid compilation warnings
487 	}
488 }
489 
490 /*
491 the XCloseable::close method has to be implemented in the following way:
492 ::close
493 {
494 	//hold no mutex
495 
496 	if( !m_aLifeTimeManager.g_close_startTryClose( bDeliverOwnership ) )
497 		return;
498 	//no mutex is acquired
499 
500 	// At the end of this method may we must dispose ourself ...
501     // and may nobody from outside hold a reference to us ...
502     // then it's a good idea to do that by ourself.
503     uno::Reference< uno::XInterface > xSelfHold( static_cast< ::cppu::OWeakObject* >(this) );
504 
505 	//the listeners have had no veto
506 	//check whether we self can close
507 	{
508 		util::CloseVetoException aVetoException = util::CloseVetoException(
509 						::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
510 						"the model itself could not be closed" ) )
511 						, static_cast< ::cppu::OWeakObject* >(this));
512 
513 		if( m_aLifeTimeManager.g_close_isNeedToCancelLongLastingCalls( bDeliverOwnership, aVetoException ) )
514 		{
515 			////you can empty this block, if you never start longlasting calls or
516 			////if your longlasting calls are per default not cancelable (check how you have constructed your LifeTimeManager)
517 
518 			sal_Bool bLongLastingCallsAreCanceled = sal_False;
519 			try
520 			{
521 				//try to cancel running longlasting calls
522 				//// @todo
523 			}
524 			catch( uno::Exception& ex )
525 			{
526 				//// @todo
527 				//do not throw anything here!! (without endTryClose)
528 			}
529 			//if not successful canceled
530 			if(!bLongLastingCallsAreCanceled)
531 			{
532 				m_aLifeTimeManager.g_close_endTryClose( bDeliverOwnership, sal_True );
533                 throw aVetoException;
534 			}
535 		}
536 
537 	}
538 	m_aLifeTimeManager.g_close_endTryClose_doClose();
539 }
540 */
541 
542 }//end namespace apphelper
543