Avoid potential negative array index access to cached text.
[LibreOffice.git] / chart2 / source / tools / LifeTime.cxx
blob45550b93efdd70adca062562950d6139581187e6
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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;
35 namespace apphelper
38 LifeTimeManager::LifeTimeManager( lang::XComponent* pComponent )
39 : m_pComponent(pComponent)
41 m_bDisposed = false;
42 m_bInDispose = false;
43 m_nAccessCount = 0;
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 )
57 if( bAssert )
59 OSL_FAIL( "This component is already disposed " );
61 return true;
63 return false;
66 bool LifeTimeManager::impl_canStartApiCall()
68 if( impl_isDisposed() )
69 return false; //behave passive if already disposed
71 //mutex is acquired
72 return true;
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
79 m_nAccessCount++;
80 if(m_nAccessCount==1)
81 //@todo? is it ok to wake some threads here while we have acquired the mutex?
82 m_aNoAccessCountCondition.reset();
84 if(bLongLastingCall)
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" );
96 m_nAccessCount--;
97 if(bLongLastingCall)
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()
113 //hold no mutex
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
123 m_bInDispose = true;
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);
129 if(xComponent.is())
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" );
141 m_bDisposed = true;
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
151 return true;
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)
160 m_bClosed = false;
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 ) )
173 return true;
175 if( m_bClosed )
177 if( bAssert )
179 OSL_FAIL( "This object is already closed" );
181 return true;
183 return false;
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) )
192 return false;
194 //Mutex needs to be acquired exactly once; will be released inbetween
195 if( !impl_canStartApiCall() )
196 return false;
197 //mutex is acquired
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);
216 if(xCloseable.is())
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);
235 throw;
237 return true;
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 )
264 return;
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);
275 throw ex;
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()
309 if(m_bClosed)
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
314 m_bClosed = true;
316 uno::Reference< util::XCloseable > xCloseable;
317 xCloseable.set(m_pCloseable);
320 if(xCloseable.is())
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");
335 rGuard.unlock();
336 if(xCloseable.is())
338 uno::Reference< lang::XComponent > xComponent( xCloseable, uno::UNO_QUERY );
339 if(xComponent.is())
341 OSL_ENSURE( m_bClosed, "a not closed component will be disposed " );
342 xComponent->dispose();
345 rGuard.lock();
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() )
353 return;
354 //mutex is acquired
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
367 if( m_bClosed )
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
383 //mutex is acquired
384 return true;
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)
394 return false;
396 //Mutex needs to be acquired exactly once; will be released inbetween
397 if( !m_rManager.impl_canStartApiCall() )
398 return false;
399 //mutex is acquired
401 m_bCallRegistered = true;
402 m_bLongLastingCallRegistered = bLongLastingCall;
403 m_rManager.impl_registerApiCall(bLongLastingCall);
404 return true;
407 LifeTimeGuard::~LifeTimeGuard()
411 //do acquire the mutex if it was cleared before
412 if (!m_guard.owns_lock())
413 m_guard.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: */