1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 #include <LifeTime.hxx>
21 #include <osl/diagnose.h>
23 #include <com/sun/star/document/XStorageChangeListener.hpp>
24 #include <com/sun/star/lang/XComponent.hpp>
25 #include <com/sun/star/util/CloseVetoException.hpp>
26 #include <com/sun/star/util/XCloseListener.hpp>
27 #include <com/sun/star/util/XCloseable.hpp>
28 #include <com/sun/star/util/XModifyListener.hpp>
29 #include <com/sun/star/view/XSelectionChangeListener.hpp>
30 #include <comphelper/diagnose_ex.hxx>
31 #include <sal/log.hxx>
33 using namespace ::com::sun::star
;
38 LifeTimeManager::LifeTimeManager( lang::XComponent
* pComponent
)
39 : m_pComponent(pComponent
)
44 m_nLongLastingCallCount
= 0;
45 m_aNoAccessCountCondition
.set();
46 m_aNoLongLastingCallCountCondition
.set();
49 LifeTimeManager::~LifeTimeManager()
53 bool LifeTimeManager::impl_isDisposed( bool bAssert
)
55 if( m_bDisposed
|| m_bInDispose
)
59 OSL_FAIL( "This component is already disposed " );
66 bool LifeTimeManager::impl_canStartApiCall()
68 if( impl_isDisposed() )
69 return false; //behave passive if already disposed
75 void LifeTimeManager::impl_registerApiCall(bool bLongLastingCall
)
77 //only allowed if not disposed
78 //do not acquire the mutex here because it will be acquired already
81 //@todo? is it ok to wake some threads here while we have acquired the mutex?
82 m_aNoAccessCountCondition
.reset();
85 m_nLongLastingCallCount
++;
86 if(m_nLongLastingCallCount
==1)
87 m_aNoLongLastingCallCountCondition
.reset();
90 void LifeTimeManager::impl_unregisterApiCall(std::unique_lock
<std::mutex
>& rGuard
, bool bLongLastingCall
)
92 //Mutex needs to be acquired exactly once
93 //mutex may be released inbetween in special case of impl_apiCallCountReachedNull()
95 OSL_ENSURE( m_nAccessCount
>0, "access count mismatch" );
98 m_nLongLastingCallCount
--;
99 if( m_nLongLastingCallCount
==0 )
101 m_aNoLongLastingCallCountCondition
.set();
103 if( m_nAccessCount
== 0)
105 m_aNoAccessCountCondition
.set();
106 impl_apiCallCountReachedNull(rGuard
);
111 bool LifeTimeManager::dispose()
115 std::unique_lock
aGuard( m_aAccessMutex
);
117 if( m_bDisposed
|| m_bInDispose
)
119 SAL_WARN("chart2", "This component is already disposed " );
120 return false; //behave passive if already disposed
124 //adding any listener is not allowed anymore
125 //new calls will not be accepted
126 //still running calls have the freedom to finish their work without crash
128 uno::Reference
< lang::XComponent
> xComponent(m_pComponent
);
131 // notify XCLoseListeners
132 lang::EventObject
aEvent( xComponent
);
133 m_aCloseListeners
.disposeAndClear( aGuard
, aEvent
);
134 m_aModifyListeners
.disposeAndClear( aGuard
, aEvent
);
135 m_aStorageChangeListeners
.disposeAndClear( aGuard
, aEvent
);
136 m_aEventListeners
.disposeAndClear( aGuard
, aEvent
);
137 m_aSelectionChangeListeners
.disposeAndClear( aGuard
, aEvent
);
140 OSL_ENSURE( !m_bDisposed
, "dispose was called already" );
143 //no mutex is acquired
145 //wait until all still running calls have finished
146 //the accessCount cannot grow anymore, because all calls will return after checking m_bDisposed
147 m_aNoAccessCountCondition
.wait();
149 //we are the only ones working on our data now
152 //--release all resources and references after calling this method successful
155 CloseableLifeTimeManager::CloseableLifeTimeManager( css::util::XCloseable
* pCloseable
156 , css::lang::XComponent
* pComponent
)
157 : LifeTimeManager( pComponent
)
158 , m_pCloseable(pCloseable
)
161 m_bInTryClose
= false;
162 m_bOwnership
= false;
163 m_aEndTryClosingCondition
.set();
166 CloseableLifeTimeManager::~CloseableLifeTimeManager()
170 bool CloseableLifeTimeManager::impl_isDisposedOrClosed( bool bAssert
)
172 if( impl_isDisposed( bAssert
) )
179 OSL_FAIL( "This object is already closed" );
186 bool CloseableLifeTimeManager::g_close_startTryClose(bool bDeliverOwnership
)
188 //no mutex is allowed to be acquired
190 std::unique_lock
aGuard( m_aAccessMutex
);
191 if( impl_isDisposedOrClosed(false) )
194 //Mutex needs to be acquired exactly once; will be released inbetween
195 if( !impl_canStartApiCall() )
199 //not closed already -> we try to close again
200 m_bInTryClose
= true;
201 m_aEndTryClosingCondition
.reset();
203 impl_registerApiCall(false);
206 //no mutex is acquired
208 //only remove listener calls will be worked on until end of tryclose
209 //all other new calls will wait till end of try close // @todo? is that really ok
211 //?? still running calls have the freedom to finish their work without crash
215 uno::Reference
< util::XCloseable
> xCloseable(m_pCloseable
);
218 std::unique_lock
aGuard( m_aAccessMutex
);
219 //--call queryClosing on all registered close listeners
220 if( m_aCloseListeners
.getLength(aGuard
) )
222 lang::EventObject
aEvent( xCloseable
);
223 m_aCloseListeners
.forEach(aGuard
,
224 [&aEvent
, bDeliverOwnership
](const uno::Reference
<util::XCloseListener
>& l
)
226 l
->queryClosing(aEvent
, bDeliverOwnership
);
231 catch( const uno::Exception
& )
233 //no mutex is acquired
234 g_close_endTryClose(bDeliverOwnership
);
240 void CloseableLifeTimeManager::g_close_endTryClose(bool bDeliverOwnership
)
242 //this method is called, if the try to close was not successful
243 std::unique_lock
aGuard( m_aAccessMutex
);
244 impl_setOwnership( bDeliverOwnership
, false );
246 m_bInTryClose
= false;
247 m_aEndTryClosingCondition
.set();
249 //Mutex needs to be acquired exactly once
250 //mutex may be released inbetween in special case of impl_apiCallCountReachedNull()
251 impl_unregisterApiCall(aGuard
, false);
254 void CloseableLifeTimeManager::g_close_isNeedToCancelLongLastingCalls( bool bDeliverOwnership
, util::CloseVetoException
const & ex
)
256 //this method is called when no closelistener has had a veto during queryclosing
257 //the method returns false, if nothing stands against closing anymore
258 //it returns true, if some longlasting calls are running, which might be cancelled
259 //it throws the given exception, if long calls are running but not cancelable
261 std::unique_lock
aGuard( m_aAccessMutex
);
262 //this count cannot grow after try of close has started, because we wait in all those methods for end of try closing
263 if( !m_nLongLastingCallCount
)
266 impl_setOwnership( bDeliverOwnership
, true );
268 m_bInTryClose
= false;
269 m_aEndTryClosingCondition
.set();
271 //Mutex needs to be acquired exactly once
272 //mutex may be released inbetween in special case of impl_apiCallCountReachedNull()
273 impl_unregisterApiCall(aGuard
, false);
278 void CloseableLifeTimeManager::g_close_endTryClose_doClose()
280 //this method is called, if the try to close was successful
281 std::unique_lock
aGuard( m_aAccessMutex
);
283 m_bInTryClose
= false;
284 m_aEndTryClosingCondition
.set();
286 //Mutex needs to be acquired exactly once
287 //mutex may be released inbetween in special case of impl_apiCallCountReachedNull()
288 impl_unregisterApiCall(aGuard
, false);
289 impl_doClose(aGuard
);
292 void CloseableLifeTimeManager::impl_setOwnership( bool bDeliverOwnership
, bool bMyVeto
)
294 m_bOwnership
= bDeliverOwnership
&& bMyVeto
;
297 void CloseableLifeTimeManager::impl_apiCallCountReachedNull(std::unique_lock
<std::mutex
>& rGuard
)
299 //Mutex needs to be acquired exactly once
300 //mutex will be released inbetween in impl_doClose()
301 if( m_pCloseable
&& m_bOwnership
)
302 impl_doClose(rGuard
);
305 void CloseableLifeTimeManager::impl_doClose(std::unique_lock
<std::mutex
>& rGuard
)
307 //Mutex needs to be acquired exactly once before calling impl_doClose()
310 return; //behave as passive as possible, if disposed or closed already
311 if( m_bDisposed
|| m_bInDispose
)
312 return; //behave as passive as possible, if disposed or closed already
316 uno::Reference
< util::XCloseable
> xCloseable
;
317 xCloseable
.set(m_pCloseable
);
322 //--call notifyClosing on all registered close listeners
323 if( m_aCloseListeners
.getLength(rGuard
) )
325 lang::EventObject
aEvent( xCloseable
);
326 m_aCloseListeners
.notifyEach(rGuard
, &util::XCloseListener::notifyClosing
, aEvent
);
330 catch( const uno::Exception
& )
332 DBG_UNHANDLED_EXCEPTION("chart2");
338 uno::Reference
< lang::XComponent
> xComponent( xCloseable
, uno::UNO_QUERY
);
341 OSL_ENSURE( m_bClosed
, "a not closed component will be disposed " );
342 xComponent
->dispose();
348 void CloseableLifeTimeManager::g_addCloseListener( const uno::Reference
< util::XCloseListener
> & xListener
)
350 std::unique_lock
aGuard( m_aAccessMutex
);
351 //Mutex needs to be acquired exactly once; will be released inbetween
352 if( !impl_canStartApiCall() )
356 m_aCloseListeners
.addInterface( aGuard
, xListener
);
357 m_bOwnership
= false;
360 bool CloseableLifeTimeManager::impl_canStartApiCall()
362 //Mutex needs to be acquired exactly once before calling this method
363 //the mutex will be released inbetween and reacquired
365 if( impl_isDisposed() )
366 return false; //behave passive if already disposed
368 return false; //behave passive if closing is already done
370 //during try-close most calls need to wait for the decision
371 while( m_bInTryClose
)
373 //if someone tries to close this object at the moment
374 //we need to wait for his end because the result of the preceding call
375 //is relevant for our behaviour here
377 m_aAccessMutex
.unlock();
378 m_aEndTryClosingCondition
.wait(); //@todo??? this may block??? try closing
379 m_aAccessMutex
.lock();
380 if( m_bDisposed
|| m_bInDispose
|| m_bClosed
)
381 return false; //return if closed already
387 bool LifeTimeGuard::startApiCall(bool bLongLastingCall
)
389 //Mutex needs to be acquired exactly once; will be released inbetween
390 //mutex is required due to constructor of LifeTimeGuard
392 OSL_ENSURE( !m_bCallRegistered
, "this method is only allowed ones" );
393 if(m_bCallRegistered
)
396 //Mutex needs to be acquired exactly once; will be released inbetween
397 if( !m_rManager
.impl_canStartApiCall() )
401 m_bCallRegistered
= true;
402 m_bLongLastingCallRegistered
= bLongLastingCall
;
403 m_rManager
.impl_registerApiCall(bLongLastingCall
);
407 LifeTimeGuard::~LifeTimeGuard()
411 //do acquire the mutex if it was cleared before
412 if (!m_guard
.owns_lock())
414 if(m_bCallRegistered
)
416 //Mutex needs to be acquired exactly once
417 //mutex may be released inbetween in special case of impl_apiCallCountReachedNull()
418 m_rManager
.impl_unregisterApiCall(m_guard
, m_bLongLastingCallRegistered
);
421 catch( uno::Exception
& ex
)
423 //@todo ? allow a uno::RuntimeException from dispose to travel through??
424 ex
.Context
.is(); //to avoid compilation warnings
428 }//end namespace apphelper
430 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */