From d1ceaf72031728dd87ff56df592a65f88a866f2d Mon Sep 17 00:00:00 2001 From: Sascha Ballach Date: Wed, 22 Jan 2003 14:18:07 +0000 Subject: #106337#; make notification synchron --- comphelper/source/misc/accessibleeventnotifier.cxx | 426 ++++----------------- 1 file changed, 67 insertions(+), 359 deletions(-) (limited to 'comphelper') diff --git a/comphelper/source/misc/accessibleeventnotifier.cxx b/comphelper/source/misc/accessibleeventnotifier.cxx index 2fd5f79f53cd..32ab2995ee5e 100644 --- a/comphelper/source/misc/accessibleeventnotifier.cxx +++ b/comphelper/source/misc/accessibleeventnotifier.cxx @@ -2,9 +2,9 @@ * * $RCSfile: accessibleeventnotifier.cxx,v $ * - * $Revision: 1.1 $ + * $Revision: 1.2 $ * - * last change: $Author: fs $ $Date: 2002-12-06 12:56:46 $ + * last change: $Author: sab $ $Date: 2003-01-22 15:18:07 $ * * The Contents of this file are made available subject to the terms of * either of the following licenses @@ -85,184 +85,8 @@ namespace comphelper //= AccessibleEventNotifier //===================================================================== //--------------------------------------------------------------------- - ::osl::Mutex AccessibleEventNotifier::s_aMutex; - AccessibleEventNotifier* AccessibleEventNotifier::s_pNotifier = NULL; - - //--------------------------------------------------------------------- - AccessibleEventNotifier::AccessibleEventNotifier( ) - :m_bTerminateRequested( sal_False ) - { - // no events so far - m_aEventGuard.reset(); - } - - //--------------------------------------------------------------------- - AccessibleEventNotifier::~AccessibleEventNotifier( ) - { - OSL_ENSURE( m_aClients.empty() && m_aDisposedClients.empty(), - "AccessibleEventNotifier::~AccessibleEventNotifier: not correctly terminated - resource leak!" ); - } - - //--------------------------------------------------------------------- - namespace - { - static void lcl_copyInterfaceContainer( const ::cppu::OInterfaceContainerHelper& _rSource, ::cppu::OInterfaceContainerHelper& _rDest ) - { - _rDest.clear(); - Sequence< Reference< XInterface > > aInterfaces( _rSource.getElements() ); - - const Reference< XInterface >* pInterfaces = aInterfaces.getConstArray(); - const Reference< XInterface >* pInterfacesEnd = pInterfaces + aInterfaces.getLength(); - for ( ; pInterfaces != pInterfacesEnd; ++pInterfaces ) - _rDest.addInterface( *pInterfaces ); - } - } - //--------------------------------------------------------------------- - void SAL_CALL AccessibleEventNotifier::run() - { - sal_Bool bTerminate = sal_False; - - do - { - // notify the events we have in the queue - // --- ------------------------------------------- - { - ::osl::MutexGuard aGuard( s_aMutex ); - - // continue with all events we have so far - while ( !m_aEvents.empty() ) - { - // the first event in the queue - ClientEvent aEvent = m_aEvents.front(); - m_aEvents.pop_front(); - - // special handling for "disposing" - if ( aEvent.second.EventId < 0 ) - { - // look up in the map for "disposed clients" - ClientMap::iterator aPos = m_aDisposedClients.find( aEvent.first ); - OSL_ENSURE( m_aDisposedClients.end() != aPos, - "AccessibleEventNotifier::run: could not find this client!" ); - - if ( m_aDisposedClients.end() != aPos ) - { - EventObject aDisposalEvent; - aDisposalEvent.Source = aEvent.second.Source; - - // want to call the listeners with a released mutex - // thus we have to copy the container, so that we can savely use the copy while - // our mutex is released - - ::cppu::OInterfaceContainerHelper aCopy( s_aMutex ); - lcl_copyInterfaceContainer( *aPos->second, aCopy ); - - // we do not need the entry in the "disposed clients" map anymore - // because the "disposed" event is the _last_ one to be fired for a client - delete aPos->second; - m_aDisposedClients.erase( aPos ); - - // now do the notification, and do it with the _copy_ after releasing the mutex - // --- ------------------------ - { - MutexRelease aReleaseOnce( s_aMutex ); - aCopy.disposeAndClear( aDisposalEvent ); - - // clear the aDisposalEvent while our mutex is _not_ acquired - // this ensures that we do not - by accident - release the last reference - // of the foreign component while our mutex is locked - aDisposalEvent.Source.clear(); - } - // --- ----------------------- - - // cleanup the thread if we do not have clients anymore - implCleanupNotifier( ); - } - } - else - { - // look up the client for this event - ClientMap::iterator aClientPos; - if ( implLookupClient( aEvent.first, aClientPos ) ) - { - // copy the listener sequence. We do _not_ want to call into the listeners - // with our mutex locked - Sequence< Reference< XInterface > > aListeners( aClientPos->second->getElements() ); - // default handling: loop through all listeners, and notify them - - const Reference< XInterface >* pListeners = aListeners.getConstArray(); - const Reference< XInterface >* pListenersEnd = pListeners + aListeners.getLength(); - - // --- ------------------------ - { - // release the mutex within this block - MutexRelease aReleaseOnce( s_aMutex ); - - while ( pListeners != pListenersEnd ) - { - try - { - static_cast< XAccessibleEventListener* >( pListeners->get() )->notifyEvent( aEvent.second ); - } - catch( const Exception& e ) - { - e; - // silent this - // no assertion, because a broken access remote bridge or something like this - // can cause this exception - } - ++pListeners; - } - } - // --- ----------------------- - } - else - OSL_ENSURE( sal_False, "AccessibleEventNotifier::run: invalid client id found for accessible event!" ); - } - - // --- -------------------------------- - { - MutexRelease aReleaseOnce( s_aMutex ); - // clear the event - // do this with our own mutex released, as clearing the event includes releasing the reference - // to the css.lang.EventObject.Source - in case this release is non-trivial (i.e. the last - // reference to the object), we certainly do _not_ want to do this while our - // mutex is locked - aEvent = ClientEvent(); - } - // --- ------------------------------- - } - - // reset the condition - will be set as soon as a new event arrives - m_aEventGuard.reset(); - } - // --- ------------------------------------------ - - // wait (sleep) 'til a new event arrives - m_aEventGuard.wait(); - - // --- ------------------------------------------- - { - ::osl::MutexGuard aGuard( s_aMutex ); - bTerminate = m_bTerminateRequested; - } - // --- ------------------------------------------ - } - while ( !bTerminate ); - } - - //--------------------------------------------------------------------- - void SAL_CALL AccessibleEventNotifier::terminate() - { - AccessibleEventNotifier_BASE::terminate(); - // base class does not call onTerminated - just in case we want to do any cleanup there ... - onTerminated(); - } - - //--------------------------------------------------------------------- - void SAL_CALL AccessibleEventNotifier::onTerminated() - { - delete this; - } + ::osl::Mutex AccessibleEventNotifier::s_aMutex; + AccessibleEventNotifier::ClientMap AccessibleEventNotifier::s_aClients; //--------------------------------------------------------------------- AccessibleEventNotifier::TClientId AccessibleEventNotifier::generateId() @@ -275,8 +99,8 @@ namespace comphelper // Note that the following relies on the fact the elements in the map are traveled with // ascending keys (aka client ids) - for ( ClientMap::const_iterator aLookup = m_aClients.begin(); - aLookup != m_aClients.end(); + for ( ClientMap::const_iterator aLookup = s_aClients.begin(); + aLookup != s_aClients.end(); ++aLookup ) { @@ -285,15 +109,8 @@ namespace comphelper if ( nCurrent - nBiggestUsedId > 1 ) { // found a "gap" - TClientId nCandidate = nBiggestUsedId + 1; - - // ensure that the id is really free - it's possible that the id is still in the "disposed clients" - // map - if ( m_aDisposedClients.end() == m_aDisposedClients.find( nCandidate ) ) - { // yep, it's really available - nFreeId = nCandidate; - break; - } + nFreeId = nBiggestUsedId + 1; + break; } nBiggestUsedId = nCurrent; @@ -302,7 +119,7 @@ namespace comphelper if ( !nFreeId ) nFreeId = nBiggestUsedId + 1; - OSL_ENSURE( m_aClients.end() == m_aClients.find( nFreeId ), + OSL_ENSURE( s_aClients.end() == s_aClients.find( nFreeId ), "AccessibleEventNotifier::generateId: algorithm broken!" ); return nFreeId; @@ -312,30 +129,19 @@ namespace comphelper AccessibleEventNotifier::TClientId AccessibleEventNotifier::registerClient( ) { ::osl::MutexGuard aGuard( s_aMutex ); - if ( !s_pNotifier ) - { // the first client -> create the thread - - // create the thread object - s_pNotifier = new AccessibleEventNotifier; - - // run the thread - s_pNotifier->create(); - // note that the thread will start running, and the first thing it will do is stopping - // in run, waiting for the mutex - } // generate a new client id - TClientId nNewClientId = s_pNotifier->generateId( ); + TClientId nNewClientId = generateId( ); // the event listeners for the new client EventListeners* pNewListeners = new EventListeners( s_aMutex ); // note that we're using our own mutex here, so the listener containers for all // our clients share this same mutex. - // Shouldn't be any problem: the only situation where this is used is when the - // thread is firing events, and there the mutex is locked, anyway. + // this is a reminiscense to the days where the notifier was asynchronous. Today this is + // completely nonsense, and potentially slowing down the Office me thinks ... // add the client - s_pNotifier->m_aClients.insert( ClientMap::value_type( nNewClientId, pNewListeners ) ); + s_aClients.insert( ClientMap::value_type( nNewClientId, pNewListeners ) ); // outta here return nNewClientId; @@ -344,151 +150,49 @@ namespace comphelper //--------------------------------------------------------------------- sal_Bool AccessibleEventNotifier::implLookupClient( const TClientId _nClient, ClientMap::iterator& _rPos ) { - OSL_ENSURE( s_pNotifier, "AccessibleEventNotifier::implLookupClient: illegal call: thread not running!" ); - if ( !s_pNotifier ) - return sal_False; - // look up this client - _rPos = s_pNotifier->m_aClients.find( _nClient ); - OSL_ENSURE( s_pNotifier->m_aClients.end() != _rPos, "AccessibleEventNotifier::implLookupClient: invalid client id (did you register your client?)!" ); + _rPos = s_aClients.find( _nClient ); + OSL_ENSURE( s_aClients.end() != _rPos, "AccessibleEventNotifier::implLookupClient: invalid client id (did you register your client?)!" ); - return ( s_pNotifier->m_aClients.end() != _rPos ); - } - - //--------------------------------------------------------------------- - void AccessibleEventNotifier::implRemoveEventsForClient( const TClientId _nClient, - ::std::vector< Reference< XInterface > >& _rEnsureAlive ) - { - OSL_ENSURE( s_pNotifier, "AccessibleEventNotifier::implRemoveEventsForClient: invalid call, save your documents before it crashes!" ); - - EventQueue::iterator aEventLoop = s_pNotifier->m_aEvents.begin(); - while ( aEventLoop != s_pNotifier->m_aEvents.end() ) - { - if ( _nClient == aEventLoop->first ) - { - // this is an event queued for the same client - // -> remove it from the queue - EventQueue::iterator aErasePos( aEventLoop ); - ++aEventLoop; - - // keep the object alive until we can free our own mutex - _rEnsureAlive.push_back( aErasePos->second.Source ); - - // erase the event - s_pNotifier->m_aEvents.erase( aErasePos ); - } - else - ++aEventLoop; - } - } - - //--------------------------------------------------------------------- - void AccessibleEventNotifier::implCleanupNotifier( ) - { - OSL_PRECOND( s_pNotifier, "AccessibleEventNotifier::implCleanupNotifier: invalid call!" ); - - if ( s_pNotifier->m_aClients.empty() && s_pNotifier->m_aDisposedClients.empty() ) - { - // killing me softly .... - - // tell the instance it should terminate - s_pNotifier->m_bTerminateRequested = sal_True; - - // awake it - // (it is sleeping currently - if it were not, it would be in the section - // guarded by s_aMutex (see run), which is impossible as - // our thread here has this mutex currently ... - s_pNotifier->m_aEventGuard.set(); - - // reset the notifier holder - thus, the thread may continue to run the few microseconds - // it will need to finally terminate, but if in the meantime new clients - // are registered, we will not burden this (terminating) notifier with it, - // but create a new one. - // Note that the instance will delete itself in onTerminated - s_pNotifier = NULL; - } - - OSL_POSTCOND( !s_pNotifier || !s_pNotifier->m_aClients.empty() || !s_pNotifier->m_aDisposedClients.empty(), - "AccessibleEventNotifier::implCleanupNotifier: post condition violated!" ); + return ( s_aClients.end() != _rPos ); } //--------------------------------------------------------------------- void AccessibleEventNotifier::revokeClient( const TClientId _nClient ) { - // below, we will destroy some AccessibleEventObject instances - // their Source member refers a foreign component (the broadcaster), which we - // will release with this destruction. In case that is the _last_ release, it - // would be potentially deadly if we call it while our own mutex is locked. - // So we ensure that all these objects are alive _until_ our mutex is released. - - ::std::vector< Reference< XInterface > > aEnsureAlive; - - // ----- --------------------------------------------- - { - ::osl::MutexGuard aGuard( s_aMutex ); - - ClientMap::iterator aClientPos; - if ( !implLookupClient( _nClient, aClientPos ) ) - // already asserted in implLookupClient - return; - - // remove it from the clients map - delete aClientPos->second; - s_pNotifier->m_aClients.erase( aClientPos ); - - // remove any other events which are pending for this client - implRemoveEventsForClient( _nClient, aEnsureAlive ); + ::osl::MutexGuard aGuard( s_aMutex ); - // cleanup the thread if we do not have clients anymore - implCleanupNotifier( ); - } - // ----- --------------------------------------------- + ClientMap::iterator aClientPos; + if ( !implLookupClient( _nClient, aClientPos ) ) + // already asserted in implLookupClient + return; - // here, aEnsureAlive is cleared, and here it doesn't matter anymore if it's the last - // reference to the contained components, as our mutex is not locked here .... + // remove it from the clients map + delete aClientPos->second; + s_aClients.erase( aClientPos ); } //--------------------------------------------------------------------- void AccessibleEventNotifier::revokeClientNotifyDisposing( const TClientId _nClient, const Reference< XInterface >& _rxEventSource ) SAL_THROW( ( ) ) { - ::std::vector< Reference< XInterface > > aEnsureAlive; + ::osl::MutexGuard aGuard( s_aMutex ); - // ----- --------------------------------------------- - { - ::osl::MutexGuard aGuard( s_aMutex ); + ClientMap::iterator aClientPos; + if ( !implLookupClient( _nClient, aClientPos ) ) + // already asserted in implLookupClient + return; - ClientMap::iterator aClientPos; - if ( !implLookupClient( _nClient, aClientPos ) ) - // already asserted in implLookupClient - return; + // notify the "disposing" event for this client + EventObject aDisposalEvent; + aDisposalEvent.Source = _rxEventSource; - // move the client from the "regular clients" to the "disposed clients" map - // from then on, no events for this client will be accepted anymore - #ifdef _DEBUG - ::std::pair< ClientMap::iterator, bool > aInsertResult = - #endif - s_pNotifier->m_aDisposedClients.insert( ClientMap::value_type( _nClient, aClientPos->second ) ); - OSL_ENSURE( aInsertResult.second, "AccessibleEventNotifier::revokeClientNotifyDisposing: client was already disposed!" ); - // is this asserts, then there already was an entry for _nClient in m_aDisposedClients, which means - // somebody already called notifyDisposing with this id - s_pNotifier->m_aClients.erase( aClientPos ); - - // before we add the "disposing" event to the queue, we remove all other events for this client - implRemoveEventsForClient( _nClient, aEnsureAlive ); - - // push back a "disposing" event for this client - AccessibleEventObject aDisposalEvent; - aDisposalEvent.Source = _rxEventSource; - aDisposalEvent.EventId = -1; // this indicates "disposal" - - // add the event to the queue - implPushBackEvent( _nClient, aDisposalEvent ); - } - // ----- -------------------------------------------- + // now do the notification + aClientPos->second->disposeAndClear( aDisposalEvent ); - // here, aEnsureAlive is cleared, and here it doesn't matter anymore if it's the last - // reference to the contained components, as our mutex is not locked here .... + // we do not need the entry in the clients map anymore + delete aClientPos->second; + s_aClients.erase( aClientPos ); } //--------------------------------------------------------------------- @@ -542,38 +246,42 @@ namespace comphelper //--------------------------------------------------------------------- void AccessibleEventNotifier::addEvent( const TClientId _nClient, const AccessibleEventObject& _rEvent ) SAL_THROW( ( ) ) { - ::osl::MutexGuard aGuard( s_aMutex ); - - ClientMap::iterator aClientPos; - if ( !implLookupClient( _nClient, aClientPos ) ) - // already asserted in implLookupClient - return; + Sequence< Reference< XInterface > > aListeners; - // add the event to the queue - implPushBackEvent( _nClient, _rEvent ); - } + // --- ------------------------------- + { + ::osl::MutexGuard aGuard( s_aMutex ); - //--------------------------------------------------------------------- - void AccessibleEventNotifier::implPushBackEvent( const TClientId _nClient, const AccessibleEventObject& _rEvent ) - { - OSL_PRECOND( s_pNotifier, "AccessibleEventNotifier::implPushBackEvent: invalid call!" ); + ClientMap::iterator aClientPos; + if ( !implLookupClient( _nClient, aClientPos ) ) + // already asserted in implLookupClient + return; - // add the event to the queue - s_pNotifier->m_aEvents.push_back( ClientEvent( _nClient, _rEvent ) ); + // since we're synchronous, again, we want to notify immediately + aListeners = aClientPos->second->getElements(); + } + // --- ------------------------------ - // wake up the thread - s_pNotifier->m_aEventGuard.set(); + // default handling: loop through all listeners, and notify them + const Reference< XInterface >* pListeners = aListeners.getConstArray(); + const Reference< XInterface >* pListenersEnd = pListeners + aListeners.getLength(); + while ( pListeners != pListenersEnd ) + { + try + { + static_cast< XAccessibleEventListener* >( pListeners->get() )->notifyEvent( _rEvent ); + } + catch( const Exception& e ) + { + e; + // silent this + // no assertion, because a broken access remote bridge or something like this + // can cause this exception + } + ++pListeners; + } } //......................................................................... } // namespace comphelper //......................................................................... - - -/************************************************************************* - * history: - * $Log: not supported by cvs2svn $ - * - * Revision 1.0 05.12.2002 11:05:26 fs - ************************************************************************/ - -- cgit