/* -*- 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 #include #include #include #include #include #include #include #include #include using namespace com::sun::star::uno; namespace cppu { // due to static Reflection destruction from usr, there must be a mutex leak (#73272#) // this is used to lock all instances of OWeakConnectionPoint and OWeakRefListener as well as OWeakObject::m_pWeakConnectionPoint static std::mutex * gpWeakMutex = new std::mutex; //-- OWeakConnectionPoint ---------------------------------------------------- class OWeakConnectionPoint: public XAdapter { public: /** Hold the weak object without an acquire (only the pointer). */ explicit OWeakConnectionPoint( OWeakObject* pObj ) : m_aRefCount( 0 ) , m_pObject(pObj) {} // noncopyable OWeakConnectionPoint(const OWeakConnectionPoint&) = delete; const OWeakConnectionPoint& operator=(const OWeakConnectionPoint&) = delete; // XInterface Any SAL_CALL queryInterface( const Type & rType ) override; void SAL_CALL acquire() noexcept override; void SAL_CALL release() noexcept override; // XAdapter css::uno::Reference< css::uno::XInterface > SAL_CALL queryAdapted() override; void SAL_CALL addReference( const css::uno::Reference< css::uno::XReference >& xRef ) override; void SAL_CALL removeReference( const css::uno::Reference< css::uno::XReference >& xRef ) override; /// Called from the weak object if the reference count goes to zero. /// /// @throws css::uno::RuntimeException void dispose(); private: virtual ~OWeakConnectionPoint() {} /// The reference counter. oslInterlockedCount m_aRefCount; /// The weak object OWeakObject* m_pObject; /// The container to hold the weak references std::vector> m_aReferences; }; // XInterface Any SAL_CALL OWeakConnectionPoint::queryInterface( const Type & rType ) { return ::cppu::queryInterface( rType, static_cast< XAdapter * >( this ), static_cast< XInterface * >( this ) ); } // XInterface void SAL_CALL OWeakConnectionPoint::acquire() noexcept { #ifdef DBG_UTIL // catch things early which have been deleted and then re-acquired assert(m_aRefCount != -1); #endif osl_atomic_increment( &m_aRefCount ); } // XInterface void SAL_CALL OWeakConnectionPoint::release() noexcept { if (! osl_atomic_decrement( &m_aRefCount )) { #ifdef DBG_UTIL m_aRefCount = -1; #endif delete this; } } void OWeakConnectionPoint::dispose() { std::vector> aCopy; { // only hold the mutex while we access the field std::scoped_lock aGuard(*cppu::gpWeakMutex); // OWeakObject is not the only owner of this, so clear m_pObject // so that queryAdapted() won't use it now that it's dead m_pObject = nullptr; // other code is going to call removeReference while we are doing this, so we need a // copy, but since we are disposing and going away, we can just take the original data aCopy.swap(m_aReferences); } Any ex; for (const Reference & i : aCopy ) { try { i->dispose(); } catch (css::lang::DisposedException &) {} catch (RuntimeException &) { ex = cppu::getCaughtException(); } } if (ex.hasValue()) { cppu::throwException(ex); } } // XInterface Reference< XInterface > SAL_CALL OWeakConnectionPoint::queryAdapted() { Reference< XInterface > ret; { std::scoped_lock guard(*gpWeakMutex); if (!m_pObject) return ret; oslInterlockedCount n = osl_atomic_increment( &m_pObject->m_refCount ); if (n <= 1) { // Another thread wait in the dispose method at the guard osl_atomic_decrement( &m_pObject->m_refCount ); return ret; } } // n is now > 1 // The reference is incremented. The object cannot be destroyed. // Release the guard at the earliest point. // WeakObject has a (XInterface *) cast operator ret = *m_pObject; osl_atomic_decrement( &m_pObject->m_refCount ); return ret; } // XInterface void SAL_CALL OWeakConnectionPoint::addReference(const Reference< XReference >& rRef) { std::scoped_lock aGuard(*gpWeakMutex); m_aReferences.push_back( rRef ); } // XInterface void SAL_CALL OWeakConnectionPoint::removeReference(const Reference< XReference >& rRef) { std::scoped_lock aGuard(*gpWeakMutex); // Search from end because the thing that last added a ref is most likely to be the // first to remove a ref. // It's not really valid to compare the pointer directly, but it's faster. auto it = std::find_if(m_aReferences.rbegin(), m_aReferences.rend(), [&rRef](const Reference& rxRef) { return rxRef.get() == rRef.get(); }); if (it != m_aReferences.rend()) { m_aReferences.erase( it.base()-1 ); return; } // interface not found, use the correct compare method it = std::find(m_aReferences.rbegin(), m_aReferences.rend(), rRef); if ( it != m_aReferences.rend() ) m_aReferences.erase( it.base()-1 ); } //-- OWeakObject ------------------------------------------------------- // XInterface Any SAL_CALL OWeakObject::queryInterface( const Type & rType ) { return ::cppu::queryInterface( rType, static_cast< XWeak * >( this ), static_cast< XInterface * >( this ) ); } // XInterface void SAL_CALL OWeakObject::acquire() noexcept { osl_atomic_increment( &m_refCount ); } // XInterface void SAL_CALL OWeakObject::release() noexcept { if (osl_atomic_decrement( &m_refCount ) == 0) { // notify/clear all weak-refs before object's dtor is executed // (which may check weak-refs to this object): disposeWeakConnectionPoint(); // destroy object: delete this; } } void OWeakObject::disposeWeakConnectionPoint() { OSL_PRECOND( m_refCount == 0, "OWeakObject::disposeWeakConnectionPoint: only to be called with a ref count of 0!" ); if (m_pWeakConnectionPoint != nullptr) { OWeakConnectionPoint * const p = m_pWeakConnectionPoint; m_pWeakConnectionPoint = nullptr; try { p->dispose(); } catch (RuntimeException const& exc) { SAL_WARN( "cppuhelper", exc ); } p->release(); } } OWeakObject::~OWeakObject() COVERITY_NOEXCEPT_FALSE { } // XWeak Reference< XAdapter > SAL_CALL OWeakObject::queryAdapter() { if (!m_pWeakConnectionPoint) { // only acquire mutex if member is not created std::scoped_lock aGuard( *gpWeakMutex ); if( !m_pWeakConnectionPoint ) { OWeakConnectionPoint * p = new OWeakConnectionPoint(this); p->acquire(); m_pWeakConnectionPoint = p; } } return m_pWeakConnectionPoint; } //-- OWeakAggObject ---------------------------------------------------- OWeakAggObject::~OWeakAggObject() { } // XInterface void OWeakAggObject::acquire() noexcept { Reference x( xDelegator ); if (x.is()) x->acquire(); else OWeakObject::acquire(); } // XInterface void OWeakAggObject::release() noexcept { Reference x( xDelegator ); if (x.is()) x->release(); else OWeakObject::release(); } // XInterface Any OWeakAggObject::queryInterface( const Type & rType ) { Reference< XInterface > x( xDelegator ); // harden ref return (x.is() ? x->queryInterface( rType ) : queryAggregation( rType )); } // XAggregation Any OWeakAggObject::queryAggregation( const Type & rType ) { return ::cppu::queryInterface( rType, static_cast< XInterface * >( static_cast< OWeakObject * >( this ) ), static_cast< XAggregation * >( this ), static_cast< XWeak * >( this ) ); } // XAggregation void OWeakAggObject::setDelegator( const Reference & rDelegator ) { xDelegator = rDelegator; } } namespace com::sun::star::uno { //-- OWeakRefListener ----------------------------------------------------- class OWeakRefListener final : public XReference { public: explicit OWeakRefListener(const Reference< XInterface >& xInt); explicit OWeakRefListener(const Reference< XWeak >& xInt); virtual ~OWeakRefListener(); // noncopyable OWeakRefListener(const OWeakRefListener&) = delete; const OWeakRefListener& operator=(const OWeakRefListener&) = delete; // XInterface Any SAL_CALL queryInterface( const Type & rType ) override; void SAL_CALL acquire() noexcept override; void SAL_CALL release() noexcept override; // XReference void SAL_CALL dispose() override; /// The reference counter. oslInterlockedCount m_aRefCount; /// The connection point of the weak object, guarded by getWeakMutex() Reference< XAdapter > m_XWeakConnectionPoint; }; OWeakRefListener::OWeakRefListener(const Reference< XInterface >& xInt) : m_aRefCount( 1 ) { try { Reference< XWeak > xWeak( Reference< XWeak >::query( xInt ) ); if (xWeak.is()) { m_XWeakConnectionPoint = xWeak->queryAdapter(); if (m_XWeakConnectionPoint.is()) { m_XWeakConnectionPoint->addReference(static_cast(this)); } } } catch (RuntimeException &) { OSL_ASSERT( false ); } // assert here, but no unexpected() osl_atomic_decrement( &m_aRefCount ); } OWeakRefListener::OWeakRefListener(const Reference< XWeak >& xWeak) : m_aRefCount( 1 ) { m_XWeakConnectionPoint = xWeak->queryAdapter(); if (m_XWeakConnectionPoint.is()) { m_XWeakConnectionPoint->addReference(static_cast(this)); } osl_atomic_decrement( &m_aRefCount ); } OWeakRefListener::~OWeakRefListener() { try { if (m_XWeakConnectionPoint.is()) { acquire(); // don't die again m_XWeakConnectionPoint->removeReference(static_cast(this)); } } catch (RuntimeException &) { OSL_ASSERT( false ); } // assert here, but no unexpected() } // XInterface Any SAL_CALL OWeakRefListener::queryInterface( const Type & rType ) { return ::cppu::queryInterface( rType, static_cast< XReference * >( this ), static_cast< XInterface * >( this ) ); } // XInterface void SAL_CALL OWeakRefListener::acquire() noexcept { osl_atomic_increment( &m_aRefCount ); } // XInterface void SAL_CALL OWeakRefListener::release() noexcept { if( ! osl_atomic_decrement( &m_aRefCount ) ) delete this; } void SAL_CALL OWeakRefListener::dispose() { Reference< XAdapter > xAdp; { std::scoped_lock guard(*cppu::gpWeakMutex); if( m_XWeakConnectionPoint.is() ) { xAdp = m_XWeakConnectionPoint; m_XWeakConnectionPoint.clear(); } } if( xAdp.is() ) xAdp->removeReference(static_cast(this)); } //-- WeakReferenceHelper ---------------------------------------------------------- WeakReferenceHelper::WeakReferenceHelper(const Reference< XInterface >& xInt) : m_pImpl( nullptr ) { if (xInt.is()) { m_pImpl = new OWeakRefListener(xInt); m_pImpl->acquire(); } } WeakReferenceHelper::WeakReferenceHelper(const Reference< XWeak >& xWeak) : m_pImpl( nullptr ) { if (xWeak.is()) { m_pImpl = new OWeakRefListener(xWeak); m_pImpl->acquire(); } } WeakReferenceHelper::WeakReferenceHelper(const WeakReferenceHelper& rWeakRef) : m_pImpl( nullptr ) { Reference< XInterface > xInt( rWeakRef.get() ); if (xInt.is()) { m_pImpl = new OWeakRefListener(xInt); m_pImpl->acquire(); } } void WeakReferenceHelper::clear() { try { if (m_pImpl) { m_pImpl->dispose(); m_pImpl->release(); m_pImpl = nullptr; } } catch (RuntimeException &) { OSL_ASSERT( false ); } // assert here, but no unexpected() } WeakReferenceHelper& WeakReferenceHelper::operator=(const WeakReferenceHelper& rWeakRef) { if (this == &rWeakRef) { return *this; } Reference< XInterface > xInt( rWeakRef.get() ); return operator = ( xInt ); } WeakReferenceHelper & WeakReferenceHelper::operator =( WeakReferenceHelper && other) { clear(); std::swap(m_pImpl, other.m_pImpl); return *this; } WeakReferenceHelper & SAL_CALL WeakReferenceHelper::operator= (const Reference< XInterface > & xInt) { try { clear(); if (xInt.is()) { m_pImpl = new OWeakRefListener(xInt); m_pImpl->acquire(); } } catch (RuntimeException &) { OSL_ASSERT( false ); } // assert here, but no unexpected() return *this; } WeakReferenceHelper & WeakReferenceHelper::operator= (const Reference< XWeak > & xWeak) { clear(); if (xWeak) { m_pImpl = new OWeakRefListener(xWeak); m_pImpl->acquire(); } return *this; } WeakReferenceHelper::~WeakReferenceHelper() { clear(); } Reference< XInterface > WeakReferenceHelper::get() const { try { Reference< XAdapter > xAdp; { // must lock to access m_XWeakConnectionPoint std::scoped_lock guard(*cppu::gpWeakMutex); if( m_pImpl && m_pImpl->m_XWeakConnectionPoint.is() ) xAdp = m_pImpl->m_XWeakConnectionPoint; } if (xAdp.is()) return xAdp->queryAdapted(); } catch (RuntimeException &) { OSL_ASSERT( false ); } // assert here, but no unexpected() return Reference< XInterface >(); } } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */