diff options
Diffstat (limited to 'chart2/source/tools/LifeTime.cxx')
-rw-r--r-- | chart2/source/tools/LifeTime.cxx | 441 |
1 files changed, 441 insertions, 0 deletions
diff --git a/chart2/source/tools/LifeTime.cxx b/chart2/source/tools/LifeTime.cxx new file mode 100644 index 000000000..7a84f4f64 --- /dev/null +++ b/chart2/source/tools/LifeTime.cxx @@ -0,0 +1,441 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <LifeTime.hxx> +#include <osl/diagnose.h> + +#include <com/sun/star/lang/XComponent.hpp> +#include <com/sun/star/util/CloseVetoException.hpp> +#include <com/sun/star/util/XCloseListener.hpp> +#include <com/sun/star/util/XCloseable.hpp> +#include <tools/diagnose_ex.h> +#include <sal/log.hxx> + +using namespace ::com::sun::star; + +namespace apphelper +{ + +LifeTimeManager::LifeTimeManager( lang::XComponent* pComponent ) + : m_aListenerContainer( m_aAccessMutex ) + , m_pComponent(pComponent) +{ + m_bDisposed = false; + m_bInDispose = false; + m_nAccessCount = 0; + m_nLongLastingCallCount = 0; + m_aNoAccessCountCondition.set(); + m_aNoLongLastingCallCountCondition.set(); +} + +LifeTimeManager::~LifeTimeManager() +{ +} + +bool LifeTimeManager::impl_isDisposed( bool bAssert ) +{ + if( m_bDisposed || m_bInDispose ) + { + if( bAssert ) + { + OSL_FAIL( "This component is already disposed " ); + } + return true; + } + return false; +} + +bool LifeTimeManager::impl_canStartApiCall() +{ + if( impl_isDisposed() ) + return false; //behave passive if already disposed + + //mutex is acquired + return true; +} + +void LifeTimeManager::impl_registerApiCall(bool bLongLastingCall) +{ + //only allowed if not disposed + //do not acquire the mutex here because it will be acquired already + m_nAccessCount++; + if(m_nAccessCount==1) + //@todo? is it ok to wake some threads here while we have acquired the mutex? + m_aNoAccessCountCondition.reset(); + + if(bLongLastingCall) + m_nLongLastingCallCount++; + if(m_nLongLastingCallCount==1) + m_aNoLongLastingCallCountCondition.reset(); +} + +void LifeTimeManager::impl_unregisterApiCall(bool bLongLastingCall) +{ + //Mutex needs to be acquired exactly once + //mutex may be released inbetween in special case of impl_apiCallCountReachedNull() + + OSL_ENSURE( m_nAccessCount>0, "access count mismatch" ); + m_nAccessCount--; + if(bLongLastingCall) + m_nLongLastingCallCount--; + if( m_nLongLastingCallCount==0 ) + { + m_aNoLongLastingCallCountCondition.set(); + } + if( m_nAccessCount== 0) + { + m_aNoAccessCountCondition.set(); + impl_apiCallCountReachedNull(); + + } +} + +bool LifeTimeManager::dispose() +{ + //hold no mutex + { + osl::MutexGuard aGuard( m_aAccessMutex ); + + if( m_bDisposed || m_bInDispose ) + { + SAL_WARN("chart2", "This component is already disposed " ); + return false; //behave passive if already disposed + } + + m_bInDispose = true; + //adding any listener is not allowed anymore + //new calls will not be accepted + //still running calls have the freedom to finish their work without crash + } + //no mutex is acquired + + //--do the disposing of listeners after calling this method + { + uno::Reference< lang::XComponent > xComponent(m_pComponent); + if(xComponent.is()) + { + // notify XCLoseListeners + lang::EventObject aEvent( xComponent ); + m_aListenerContainer.disposeAndClear( aEvent ); + } + } + + //no mutex is acquired + { + osl::MutexGuard aGuard( m_aAccessMutex ); + OSL_ENSURE( !m_bDisposed, "dispose was called already" ); + m_bDisposed = true; + } + //no mutex is acquired + + //wait until all still running calls have finished + //the accessCount cannot grow anymore, because all calls will return after checking m_bDisposed + m_aNoAccessCountCondition.wait(); + + //we are the only ones working on our data now + + return true; + //--release all resources and references after calling this method successful +} + +CloseableLifeTimeManager::CloseableLifeTimeManager( css::util::XCloseable* pCloseable + , css::lang::XComponent* pComponent ) + : LifeTimeManager( pComponent ) + , m_pCloseable(pCloseable) +{ + m_bClosed = false; + m_bInTryClose = false; + m_bOwnership = false; + m_aEndTryClosingCondition.set(); +} + +CloseableLifeTimeManager::~CloseableLifeTimeManager() +{ +} + +bool CloseableLifeTimeManager::impl_isDisposedOrClosed( bool bAssert ) +{ + if( impl_isDisposed( bAssert ) ) + return true; + + if( m_bClosed ) + { + if( bAssert ) + { + OSL_FAIL( "This object is already closed" ); + } + return true; + } + return false; +} + +bool CloseableLifeTimeManager::g_close_startTryClose(bool bDeliverOwnership) +{ + //no mutex is allowed to be acquired + { + osl::MutexGuard aGuard( m_aAccessMutex ); + if( impl_isDisposedOrClosed(false) ) + return false; + + //Mutex needs to be acquired exactly once; will be released inbetween + if( !impl_canStartApiCall() ) + return false; + //mutex is acquired + + //not closed already -> we try to close again + m_bInTryClose = true; + m_aEndTryClosingCondition.reset(); + + impl_registerApiCall(false); + } + + //no mutex is acquired + + //only remove listener calls will be worked on until end of tryclose + //all other new calls will wait till end of try close // @todo? is that really ok + + //?? still running calls have the freedom to finish their work without crash + + try + { + uno::Reference< util::XCloseable > xCloseable(m_pCloseable); + if(xCloseable.is()) + { + //--call queryClosing on all registered close listeners + ::comphelper::OInterfaceContainerHelper2* pIC = m_aListenerContainer.getContainer( + cppu::UnoType<util::XCloseListener>::get()); + if( pIC ) + { + lang::EventObject aEvent( xCloseable ); + ::comphelper::OInterfaceIteratorHelper2 aIt( *pIC ); + while( aIt.hasMoreElements() ) + { + static_cast< util::XCloseListener* >( aIt.next() ) + ->queryClosing( aEvent, bDeliverOwnership ); + } + } + } + } + catch( const uno::Exception& ) + { + //no mutex is acquired + g_close_endTryClose(bDeliverOwnership); + throw; + } + return true; +} + +void CloseableLifeTimeManager::g_close_endTryClose(bool bDeliverOwnership ) +{ + //this method is called, if the try to close was not successful + osl::MutexGuard aGuard( m_aAccessMutex ); + impl_setOwnership( bDeliverOwnership, false ); + + m_bInTryClose = false; + m_aEndTryClosingCondition.set(); + + //Mutex needs to be acquired exactly once + //mutex may be released inbetween in special case of impl_apiCallCountReachedNull() + impl_unregisterApiCall(false); +} + +void CloseableLifeTimeManager::g_close_isNeedToCancelLongLastingCalls( bool bDeliverOwnership, util::CloseVetoException const & ex ) +{ + //this method is called when no closelistener has had a veto during queryclosing + //the method returns false, if nothing stands against closing anymore + //it returns true, if some longlasting calls are running, which might be cancelled + //it throws the given exception, if long calls are running but not cancelable + + osl::MutexGuard aGuard( m_aAccessMutex ); + //this count cannot grow after try of close has started, because we wait in all those methods for end of try closing + if( !m_nLongLastingCallCount ) + return; + + impl_setOwnership( bDeliverOwnership, true ); + + m_bInTryClose = false; + m_aEndTryClosingCondition.set(); + + //Mutex needs to be acquired exactly once + //mutex may be released inbetween in special case of impl_apiCallCountReachedNull() + impl_unregisterApiCall(false); + + throw ex; +} + +void CloseableLifeTimeManager::g_close_endTryClose_doClose() +{ + //this method is called, if the try to close was successful + osl::MutexGuard aGuard( m_aAccessMutex ); + + m_bInTryClose = false; + m_aEndTryClosingCondition.set(); + + //Mutex needs to be acquired exactly once + //mutex may be released inbetween in special case of impl_apiCallCountReachedNull() + impl_unregisterApiCall(false); + impl_doClose(); +} + +void CloseableLifeTimeManager::impl_setOwnership( bool bDeliverOwnership, bool bMyVeto ) +{ + m_bOwnership = bDeliverOwnership && bMyVeto; +} + +void CloseableLifeTimeManager::impl_apiCallCountReachedNull() +{ + //Mutex needs to be acquired exactly once + //mutex will be released inbetween in impl_doClose() + if( m_pCloseable && m_bOwnership ) + impl_doClose(); +} + +void CloseableLifeTimeManager::impl_doClose() +{ + //Mutex needs to be acquired exactly once before calling impl_doClose() + + if(m_bClosed) + return; //behave as passive as possible, if disposed or closed already + if( m_bDisposed || m_bInDispose ) + return; //behave as passive as possible, if disposed or closed already + + m_bClosed = true; + + NegativeGuard< osl::Mutex > aNegativeGuard( m_aAccessMutex ); + //mutex is not acquired, mutex will be reacquired at the end of this method automatically + + uno::Reference< util::XCloseable > xCloseable; + try + { + xCloseable.set(m_pCloseable); + if(xCloseable.is()) + { + //--call notifyClosing on all registered close listeners + ::comphelper::OInterfaceContainerHelper2* pIC = m_aListenerContainer.getContainer( + cppu::UnoType<util::XCloseListener>::get()); + if( pIC ) + { + lang::EventObject aEvent( xCloseable ); + ::comphelper::OInterfaceIteratorHelper2 aIt( *pIC ); + while( aIt.hasMoreElements() ) + { + static_cast< util::XCloseListener* >( aIt.next() )->notifyClosing( aEvent ); + } + } + } + } + catch( const uno::Exception& ) + { + DBG_UNHANDLED_EXCEPTION("chart2"); + } + + if(xCloseable.is()) + { + uno::Reference< lang::XComponent > xComponent( xCloseable, uno::UNO_QUERY ); + if(xComponent.is()) + { + OSL_ENSURE( m_bClosed, "a not closed component will be disposed " ); + xComponent->dispose(); + } + } + //mutex will be reacquired in destructor of aNegativeGuard +} + +void CloseableLifeTimeManager::g_addCloseListener( const uno::Reference< util::XCloseListener > & xListener ) +{ + osl::MutexGuard aGuard( m_aAccessMutex ); + //Mutex needs to be acquired exactly once; will be released inbetween + if( !impl_canStartApiCall() ) + return; + //mutex is acquired + + m_aListenerContainer.addInterface( cppu::UnoType<util::XCloseListener>::get(),xListener ); + m_bOwnership = false; +} + +bool CloseableLifeTimeManager::impl_canStartApiCall() +{ + //Mutex needs to be acquired exactly once before calling this method + //the mutex will be released inbetween and reacquired + + if( impl_isDisposed() ) + return false; //behave passive if already disposed + if( m_bClosed ) + return false; //behave passive if closing is already done + + //during try-close most calls need to wait for the decision + while( m_bInTryClose ) + { + //if someone tries to close this object at the moment + //we need to wait for his end because the result of the preceding call + //is relevant for our behaviour here + + m_aAccessMutex.release(); + m_aEndTryClosingCondition.wait(); //@todo??? this may block??? try closing + m_aAccessMutex.acquire(); + if( m_bDisposed || m_bInDispose || m_bClosed ) + return false; //return if closed already + } + //mutex is acquired + return true; +} + +bool LifeTimeGuard::startApiCall(bool bLongLastingCall) +{ + //Mutex needs to be acquired exactly once; will be released inbetween + //mutex is required due to constructor of LifeTimeGuard + + OSL_ENSURE( !m_bCallRegistered, "this method is only allowed ones" ); + if(m_bCallRegistered) + return false; + + //Mutex needs to be acquired exactly once; will be released inbetween + if( !m_rManager.impl_canStartApiCall() ) + return false; + //mutex is acquired + + m_bCallRegistered = true; + m_bLongLastingCallRegistered = bLongLastingCall; + m_rManager.impl_registerApiCall(bLongLastingCall); + return true; +} + +LifeTimeGuard::~LifeTimeGuard() +{ + try + { + //do acquire the mutex if it was cleared before + osl::MutexGuard g(m_rManager.m_aAccessMutex); + if(m_bCallRegistered) + { + //Mutex needs to be acquired exactly once + //mutex may be released inbetween in special case of impl_apiCallCountReachedNull() + m_rManager.impl_unregisterApiCall(m_bLongLastingCallRegistered); + } + } + catch( uno::Exception& ex ) + { + //@todo ? allow a uno::RuntimeException from dispose to travel through?? + ex.Context.is(); //to avoid compilation warnings + } +} + +}//end namespace apphelper + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |