| /************************************************************** |
| * |
| * 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 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| |
| // MARKER(update_precomp.py): autogen include statement, do not remove |
| #include "precompiled_chart2.hxx" |
| #include "LifeTime.hxx" |
| #include "macros.hxx" |
| #include <osl/diagnose.h> |
| |
| #include <com/sun/star/util/XModifyListener.hpp> |
| #include <com/sun/star/util/XCloseListener.hpp> |
| |
| using namespace ::com::sun::star; |
| |
| namespace apphelper |
| { |
| //-------------------------- |
| |
| LifeTimeManager::LifeTimeManager( lang::XComponent* pComponent, sal_Bool bLongLastingCallsCancelable ) |
| : m_aListenerContainer( m_aAccessMutex ) |
| , m_pComponent(pComponent) |
| , m_bLongLastingCallsCancelable(bLongLastingCallsCancelable) |
| { |
| impl_init(); |
| } |
| |
| void LifeTimeManager::impl_init() |
| { |
| m_bDisposed = sal_False; |
| m_bInDispose = sal_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_ENSURE( sal_False, "This component is already disposed " ); |
| (void)(bAssert); |
| } |
| return sal_True; |
| } |
| return sal_False; |
| } |
| sal_Bool LifeTimeManager |
| ::impl_canStartApiCall() |
| { |
| if( impl_isDisposed() ) |
| return sal_False; //behave passive if already disposed |
| |
| //mutex is acquired |
| return sal_True; |
| } |
| |
| void LifeTimeManager |
| ::impl_registerApiCall(sal_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(sal_Bool bLongLastingCall) |
| { |
| //Mutex needs to be acquired exactly ones |
| //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(); |
| |
| } |
| } |
| |
| sal_Bool LifeTimeManager |
| ::dispose() throw(uno::RuntimeException) |
| { |
| //hold no mutex |
| { |
| osl::Guard< osl::Mutex > aGuard( m_aAccessMutex ); |
| |
| if( m_bDisposed || m_bInDispose ) |
| { |
| OSL_TRACE( "This component is already disposed " ); |
| return sal_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 = |
| uno::Reference< lang::XComponent >(m_pComponent);; |
| if(xComponent.is()) |
| { |
| // notify XCLoseListeners |
| lang::EventObject aEvent( xComponent ); |
| m_aListenerContainer.disposeAndClear( aEvent ); |
| } |
| } |
| |
| //no mutex is acquired |
| { |
| osl::ClearableGuard< osl::Mutex > aGuard( m_aAccessMutex ); |
| OSL_ENSURE( !m_bDisposed, "dispose was called already" ); |
| m_bDisposed = sal_True; |
| aGuard.clear(); |
| } |
| //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 sal_True; |
| //--release all resources and references after calling this method successful |
| } |
| |
| //----------------------------------------------------------------- |
| |
| CloseableLifeTimeManager::CloseableLifeTimeManager( ::com::sun::star::util::XCloseable* pCloseable |
| , ::com::sun::star::lang::XComponent* pComponent |
| , sal_Bool bLongLastingCallsCancelable ) |
| : LifeTimeManager( pComponent, bLongLastingCallsCancelable ) |
| , m_pCloseable(pCloseable) |
| { |
| impl_init(); |
| } |
| |
| CloseableLifeTimeManager::~CloseableLifeTimeManager() |
| { |
| } |
| |
| bool CloseableLifeTimeManager::impl_isDisposedOrClosed( bool bAssert ) |
| { |
| if( impl_isDisposed( bAssert ) ) |
| return sal_True; |
| |
| if( m_bClosed ) |
| { |
| if( bAssert ) |
| { |
| OSL_ENSURE( sal_False, "This object is already closed" ); |
| (void)(bAssert);//avoid warnings |
| } |
| return sal_True; |
| } |
| return sal_False; |
| } |
| |
| sal_Bool CloseableLifeTimeManager |
| ::g_close_startTryClose(sal_Bool bDeliverOwnership) |
| throw ( uno::Exception ) |
| { |
| //no mutex is allowed to be acquired |
| { |
| osl::ResettableGuard< osl::Mutex > aGuard( m_aAccessMutex ); |
| if( impl_isDisposedOrClosed(false) ) |
| return sal_False; |
| |
| //Mutex needs to be acquired exactly ones; will be released inbetween |
| if( !impl_canStartApiCall() ) |
| return sal_False; |
| //mutex is acquired |
| |
| //not closed already -> we try to close again |
| m_bInTryClose = sal_True; |
| m_aEndTryClosingCondition.reset(); |
| |
| impl_registerApiCall(sal_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 = |
| uno::Reference< util::XCloseable >(m_pCloseable);; |
| if(xCloseable.is()) |
| { |
| //--call queryClosing on all registered close listeners |
| ::cppu::OInterfaceContainerHelper* pIC = m_aListenerContainer.getContainer( |
| ::getCppuType((const uno::Reference< util::XCloseListener >*)0) );; |
| if( pIC ) |
| { |
| //lang::EventObject aEvent( static_cast< util::XCloseable*>(xCloseable) ); |
| lang::EventObject aEvent( xCloseable ); |
| ::cppu::OInterfaceIteratorHelper aIt( *pIC ); |
| while( aIt.hasMoreElements() ) |
| { |
| uno::Reference< util::XCloseListener > xCloseListener( aIt.next(), uno::UNO_QUERY ); |
| if(xCloseListener.is()) |
| xCloseListener->queryClosing( aEvent, bDeliverOwnership ); |
| } |
| } |
| } |
| } |
| catch( uno::Exception& ex ) |
| { |
| //no mutex is acquired |
| g_close_endTryClose(bDeliverOwnership, sal_False); |
| (void)(ex); |
| throw; |
| } |
| return sal_True; |
| } |
| |
| void CloseableLifeTimeManager |
| ::g_close_endTryClose(sal_Bool bDeliverOwnership, sal_Bool /* bMyVeto */ ) |
| { |
| //this method is called, if the try to close was not successfull |
| osl::Guard< osl::Mutex > aGuard( m_aAccessMutex ); |
| impl_setOwnership( bDeliverOwnership, sal_False ); |
| |
| m_bInTryClose = sal_False; |
| m_aEndTryClosingCondition.set(); |
| |
| //Mutex needs to be acquired exactly ones |
| //mutex may be released inbetween in special case of impl_apiCallCountReachedNull() |
| impl_unregisterApiCall(sal_False); |
| } |
| |
| sal_Bool CloseableLifeTimeManager |
| ::g_close_isNeedToCancelLongLastingCalls( sal_Bool bDeliverOwnership, util::CloseVetoException& ex ) |
| throw ( util::CloseVetoException ) |
| { |
| //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::Guard< osl::Mutex > 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 sal_False; |
| |
| if(m_bLongLastingCallsCancelable) |
| return sal_True; |
| |
| impl_setOwnership( bDeliverOwnership, sal_True ); |
| |
| m_bInTryClose = sal_False; |
| m_aEndTryClosingCondition.set(); |
| |
| //Mutex needs to be acquired exactly ones |
| //mutex may be released inbetween in special case of impl_apiCallCountReachedNull() |
| impl_unregisterApiCall(sal_False); |
| |
| throw ex; |
| } |
| |
| void CloseableLifeTimeManager |
| ::g_close_endTryClose_doClose() |
| { |
| //this method is called, if the try to close was successfull |
| osl::ResettableGuard< osl::Mutex > aGuard( m_aAccessMutex ); |
| |
| m_bInTryClose = sal_False; |
| m_aEndTryClosingCondition.set(); |
| |
| //Mutex needs to be acquired exactly ones |
| //mutex may be released inbetween in special case of impl_apiCallCountReachedNull() |
| impl_unregisterApiCall(sal_False); |
| impl_doClose(); |
| } |
| |
| void CloseableLifeTimeManager |
| ::impl_setOwnership( sal_Bool bDeliverOwnership, sal_Bool bMyVeto ) |
| { |
| m_bOwnership = bDeliverOwnership && bMyVeto; |
| m_bOwnershipIsWellKnown = sal_True; |
| } |
| sal_Bool CloseableLifeTimeManager |
| ::impl_shouldCloseAtNextChance() |
| { |
| return m_bOwnership; |
| } |
| |
| void CloseableLifeTimeManager |
| ::impl_apiCallCountReachedNull() |
| { |
| //Mutex needs to be acquired exactly ones |
| //mutex will be released inbetween in impl_doClose() |
| if( m_pCloseable && impl_shouldCloseAtNextChance() ) |
| impl_doClose(); |
| } |
| |
| void CloseableLifeTimeManager |
| ::impl_doClose() |
| { |
| //Mutex needs to be acquired exactly ones 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 = sal_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=NULL; |
| try |
| { |
| xCloseable = uno::Reference< util::XCloseable >(m_pCloseable);; |
| if(xCloseable.is()) |
| { |
| //--call notifyClosing on all registered close listeners |
| ::cppu::OInterfaceContainerHelper* pIC = m_aListenerContainer.getContainer( |
| ::getCppuType((const uno::Reference< util::XCloseListener >*)0) );; |
| if( pIC ) |
| { |
| //lang::EventObject aEvent( static_cast< util::XCloseable*>(xCloseable) ); |
| lang::EventObject aEvent( xCloseable ); |
| ::cppu::OInterfaceIteratorHelper aIt( *pIC ); |
| while( aIt.hasMoreElements() ) |
| { |
| uno::Reference< util::XCloseListener > xListener( aIt.next(), uno::UNO_QUERY ); |
| if( xListener.is() ) |
| xListener->notifyClosing( aEvent ); |
| } |
| } |
| } |
| } |
| catch( uno::Exception& ex ) |
| { |
| ASSERT_EXCEPTION( ex ); |
| } |
| |
| if(xCloseable.is()) |
| { |
| uno::Reference< lang::XComponent > xComponent = |
| uno::Reference< lang::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 |
| } |
| |
| sal_Bool CloseableLifeTimeManager |
| ::g_addCloseListener( const uno::Reference< util::XCloseListener > & xListener ) |
| throw(uno::RuntimeException) |
| { |
| osl::Guard< osl::Mutex > aGuard( m_aAccessMutex ); |
| //Mutex needs to be acquired exactly ones; will be released inbetween |
| if( !impl_canStartApiCall() ) |
| return sal_False; |
| //mutex is acquired |
| |
| m_aListenerContainer.addInterface( ::getCppuType((const uno::Reference< util::XCloseListener >*)0),xListener ); |
| m_bOwnership = sal_False; |
| return sal_True; |
| } |
| |
| sal_Bool CloseableLifeTimeManager |
| ::impl_canStartApiCall() |
| { |
| //Mutex needs to be acquired exactly ones before calling this method |
| //the mutex will be released inbetween and reacquired |
| |
| if( impl_isDisposed() ) |
| return sal_False; //behave passive if already disposed |
| if( m_bClosed ) |
| return sal_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 sal_False; //return if closed already |
| } |
| //mutex is acquired |
| return sal_True; |
| } |
| |
| //-------------------------- |
| |
| sal_Bool LifeTimeGuard |
| ::startApiCall(sal_Bool bLongLastingCall) |
| { |
| //Mutex needs to be acquired exactly ones; will be released inbetween |
| //mutex is requiered due to constructor of LifeTimeGuard |
| |
| OSL_ENSURE( !m_bCallRegistered, "this method is only allowed ones" ); |
| if(m_bCallRegistered) |
| return sal_False; |
| |
| //Mutex needs to be acquired exactly ones; will be released inbetween |
| if( !m_rManager.impl_canStartApiCall() ) |
| return sal_False; |
| //mutex is acquired |
| |
| m_bCallRegistered = sal_True; |
| m_bLongLastingCallRegistered = bLongLastingCall; |
| m_rManager.impl_registerApiCall(bLongLastingCall); |
| return sal_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 ones |
| //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 |
| } |
| } |
| |
| /* |
| the XCloseable::close method has to be implemented in the following way: |
| ::close |
| { |
| //hold no mutex |
| |
| if( !m_aLifeTimeManager.g_close_startTryClose( bDeliverOwnership ) ) |
| return; |
| //no mutex is acquired |
| |
| // At the end of this method may we must dispose ourself ... |
| // and may nobody from outside hold a reference to us ... |
| // then it's a good idea to do that by ourself. |
| uno::Reference< uno::XInterface > xSelfHold( static_cast< ::cppu::OWeakObject* >(this) ); |
| |
| //the listeners have had no veto |
| //check wether we self can close |
| { |
| util::CloseVetoException aVetoException = util::CloseVetoException( |
| ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( |
| "the model itself could not be closed" ) ) |
| , static_cast< ::cppu::OWeakObject* >(this)); |
| |
| if( m_aLifeTimeManager.g_close_isNeedToCancelLongLastingCalls( bDeliverOwnership, aVetoException ) ) |
| { |
| ////you can empty this block, if you never start longlasting calls or |
| ////if your longlasting calls are per default not cancelable (check how you have constructed your LifeTimeManager) |
| |
| sal_Bool bLongLastingCallsAreCanceled = sal_False; |
| try |
| { |
| //try to cancel running longlasting calls |
| //// @todo |
| } |
| catch( uno::Exception& ex ) |
| { |
| //// @todo |
| //do not throw anything here!! (without endTryClose) |
| } |
| //if not successful canceled |
| if(!bLongLastingCallsAreCanceled) |
| { |
| m_aLifeTimeManager.g_close_endTryClose( bDeliverOwnership, sal_True ); |
| throw aVetoException; |
| } |
| } |
| |
| } |
| m_aLifeTimeManager.g_close_endTryClose_doClose(); |
| } |
| */ |
| |
| }//end namespace apphelper |