xref: /trunk/main/chart2/source/tools/LifeTime.cxx (revision 91144cd0085a7583d2099b982122deb2184ab956)
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