office-gobmx/cppuhelper/source/weak.cxx
Noel Grandin 6ebe316635 tsan: data race
Atomic write of size 4 at 0x7254000f5f08 by thread T38 (mutexes: write M0, write M1, write M2):
0 configmgr::Access::acquireCounting()
1 configmgr::Components::initGlobalBroadcaster(configmgr::Modifications const&, rtl::Reference<configmgr::RootAccess> const&, configmgr::Broadcaster*)
2 configmgr::RootAccess::commitChanges()
3 non-virtual thunk to configmgr::RootAccess::commitChanges()
4 utl::OConfigurationTreeRoot::commit()
5 dbaccess::(anonymous namespace)::DatabaseRegistrations::registerDatabaseLocation(rtl::OUString const&, rtl::OUString const&)
6 non-virtual thunk to dbaccess::(anonymous namespace)::DatabaseRegistrations::registerDatabaseLocation(rtl::OUString const&, rtl::OUString const&)
7 dbaccess::ODatabaseContext::registerDatabaseLocation(rtl::OUString const&, rtl::OUString const&)
8 dbaccess::ODatabaseContext::registerObject(rtl::OUString const&, com::sun::uno::Reference<com::sun::uno::XInterface> const&)
9 non-virtual thunk to dbaccess::ODatabaseContext::registerObject(rtl::OUString const&, com::sun::uno::Reference<com::sun::uno::XInterface> const&)
10 gcc3::callVirtualMethod(void*, unsigned int, void*, _typelib_TypeDescriptionReference*, bool, unsigned long*, unsigned int, unsigned long*, double*)

Previous read of size 4 at 0x7254000f5f08 by main thread (mutexes: write M3, write M4):
0 cppu::OWeakObject::disposeWeakConnectionPoint()
1 cppu::OWeakObject::release()
2 configmgr::RootAccess::release()
3 non-virtual thunk to configmgr::RootAccess::release()
4 com::sun::uno::Reference<com::sun::util::XChangesBatch>::~Reference()
5 SvtLinguConfig::~SvtLinguConfig()
6 SwDoc::GetGCIterator()
7 SwDoc::StartGrammarChecking(bool)
8 sw::DocumentTimerManager::GetNextIdleJob()
9 sw::DocumentTimerManager::IsDocIdle()
10 sw::SwDocIdle::UpdateMinPeriod(unsigned long) const
11 Scheduler::CallbackTaskScheduling()

Change-Id: Idaf2707e5902338a873a325228e9c97e646ea768
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/172874
Tested-by: Jenkins
Reviewed-by: Noel Grandin <noel.grandin@collabora.co.uk>
2024-09-05 09:58:55 +02:00

561 lines
15 KiB
C++

/* -*- 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 <sal/config.h>
#include <sal/log.hxx>
#include <osl/diagnose.h>
#include <cppuhelper/weakagg.hxx>
#include <cppuhelper/exc_hlp.hxx>
#include <cppuhelper/queryinterface.hxx>
#include <com/sun/star/lang/DisposedException.hpp>
#include <atomic>
#include <algorithm>
#include <vector>
#include <mutex>
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<Reference<XReference>> 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<Reference<XReference>> 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<XReference> & 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<XReference>& 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( (atomic_thread_fence(std::memory_order_acquire), 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<XInterface > x( xDelegator );
if (x.is())
x->acquire();
else
OWeakObject::acquire();
}
// XInterface
void OWeakAggObject::release() noexcept
{
Reference<XInterface > 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<XInterface > & 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<XReference*>(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<XReference*>(this));
}
osl_atomic_decrement( &m_aRefCount );
}
OWeakRefListener::~OWeakRefListener()
{
try
{
if (m_XWeakConnectionPoint.is())
{
acquire(); // don't die again
m_XWeakConnectionPoint->removeReference(static_cast<XReference*>(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<XReference*>(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: */