mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-28 15:23:51 +00:00
1559fd3d00
This fix is completely speculative, but I have strong reason to believe that we are having lifetime issues, and that refcount stabilization might be coming into play. The situation is this: Suppose we're aggregating an object, so we pass |this| as the outer IUnknown. The inner object might perform AddRef() and Release() on |this| during its initialization. But if we're in the process of creating the outer object, that refcount might not yet have been incremented by 1, so the inner object's invocation of the outer object's Release() could trigger a deletion. The way around this is to temporarily bump the refcount when aggregating another object. The key, though, is to not do this via AddRef() and Release(), but by direct maniuplation of the refcount variable, so that we don't trigger any of the self-deletion stuff. MozReview-Commit-ID: 3WA2AJvb6jY --HG-- extra : rebase_source : ab05a52760541a4ab11f1245a5ddeae938998047
261 lines
5.5 KiB
C++
261 lines
5.5 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
/* 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/. */
|
|
|
|
#define INITGUID
|
|
#include "mozilla/mscom/WeakRef.h"
|
|
|
|
#include "mozilla/DebugOnly.h"
|
|
#include "mozilla/Mutex.h"
|
|
#include "nsThreadUtils.h"
|
|
#include "nsWindowsHelpers.h"
|
|
#include "nsProxyRelease.h"
|
|
|
|
static void
|
|
InitializeCS(CRITICAL_SECTION& aCS)
|
|
{
|
|
DWORD flags = 0;
|
|
#if defined(RELEASE_OR_BETA)
|
|
flags |= CRITICAL_SECTION_NO_DEBUG_INFO;
|
|
#endif
|
|
InitializeCriticalSectionEx(&aCS, 4000, flags);
|
|
}
|
|
|
|
namespace mozilla {
|
|
namespace mscom {
|
|
|
|
namespace detail {
|
|
|
|
SharedRef::SharedRef(WeakReferenceSupport* aSupport)
|
|
: mSupport(aSupport)
|
|
{
|
|
::InitializeCS(mCS);
|
|
}
|
|
|
|
SharedRef::~SharedRef()
|
|
{
|
|
::DeleteCriticalSection(&mCS);
|
|
}
|
|
|
|
void
|
|
SharedRef::Lock()
|
|
{
|
|
::EnterCriticalSection(&mCS);
|
|
}
|
|
|
|
void
|
|
SharedRef::Unlock()
|
|
{
|
|
::LeaveCriticalSection(&mCS);
|
|
}
|
|
|
|
HRESULT
|
|
SharedRef::ToStrongRef(IWeakReferenceSource** aOutStrongReference)
|
|
{
|
|
RefPtr<IWeakReferenceSource> strongRef;
|
|
|
|
{ // Scope for lock
|
|
AutoCriticalSection lock(&mCS);
|
|
if (!mSupport) {
|
|
return E_POINTER;
|
|
}
|
|
strongRef = mSupport;
|
|
}
|
|
|
|
strongRef.forget(aOutStrongReference);
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
SharedRef::Resolve(REFIID aIid, void** aOutStrongReference)
|
|
{
|
|
RefPtr<WeakReferenceSupport> strongRef;
|
|
|
|
{ // Scope for lock
|
|
AutoCriticalSection lock(&mCS);
|
|
if (!mSupport) {
|
|
return E_POINTER;
|
|
}
|
|
strongRef = mSupport;
|
|
}
|
|
|
|
return strongRef->QueryInterface(aIid, aOutStrongReference);
|
|
}
|
|
|
|
void
|
|
SharedRef::Clear()
|
|
{
|
|
AutoCriticalSection lock(&mCS);
|
|
mSupport = nullptr;
|
|
}
|
|
|
|
} // namespace detail
|
|
|
|
typedef BaseAutoLock<detail::SharedRef> SharedRefAutoLock;
|
|
typedef BaseAutoUnlock<detail::SharedRef> SharedRefAutoUnlock;
|
|
|
|
WeakReferenceSupport::WeakReferenceSupport(Flags aFlags)
|
|
: mRefCnt(0)
|
|
, mFlags(aFlags)
|
|
{
|
|
mSharedRef = new detail::SharedRef(this);
|
|
}
|
|
|
|
HRESULT
|
|
WeakReferenceSupport::QueryInterface(REFIID riid, void** ppv)
|
|
{
|
|
RefPtr<IUnknown> punk;
|
|
if (!ppv) {
|
|
return E_INVALIDARG;
|
|
}
|
|
*ppv = nullptr;
|
|
|
|
// Raise the refcount for stabilization purposes during aggregation
|
|
StabilizeRefCount stabilize(*this);
|
|
|
|
if (riid == IID_IUnknown || riid == IID_IWeakReferenceSource) {
|
|
punk = static_cast<IUnknown*>(this);
|
|
} else {
|
|
HRESULT hr = WeakRefQueryInterface(riid, getter_AddRefs(punk));
|
|
if (FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
if (!punk) {
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
punk.forget(ppv);
|
|
return S_OK;
|
|
}
|
|
|
|
WeakReferenceSupport::StabilizeRefCount::StabilizeRefCount(WeakReferenceSupport& aObject)
|
|
: mObject(aObject)
|
|
{
|
|
SharedRefAutoLock lock(*mObject.mSharedRef);
|
|
++mObject.mRefCnt;
|
|
}
|
|
|
|
WeakReferenceSupport::StabilizeRefCount::~StabilizeRefCount()
|
|
{
|
|
// We directly access these fields instead of calling Release() because we
|
|
// want to adjust the ref count without the other side effects (such as
|
|
// deleting this if the count drops back to zero, which may happen during
|
|
// an initial QI during object creation).
|
|
SharedRefAutoLock lock(*mObject.mSharedRef);
|
|
--mObject.mRefCnt;
|
|
}
|
|
|
|
ULONG
|
|
WeakReferenceSupport::AddRef()
|
|
{
|
|
SharedRefAutoLock lock(*mSharedRef);
|
|
ULONG result = ++mRefCnt;
|
|
NS_LOG_ADDREF(this, result, "mscom::WeakReferenceSupport", sizeof(*this));
|
|
return result;
|
|
}
|
|
|
|
ULONG
|
|
WeakReferenceSupport::Release()
|
|
{
|
|
ULONG newRefCnt;
|
|
{ // Scope for lock
|
|
SharedRefAutoLock lock(*mSharedRef);
|
|
newRefCnt = --mRefCnt;
|
|
if (newRefCnt == 0) {
|
|
mSharedRef->Clear();
|
|
}
|
|
}
|
|
NS_LOG_RELEASE(this, newRefCnt, "mscom::WeakReferenceSupport");
|
|
if (newRefCnt == 0) {
|
|
if (mFlags != Flags::eDestroyOnMainThread || NS_IsMainThread()) {
|
|
delete this;
|
|
} else {
|
|
// We need to delete this object on the main thread, but we aren't on the
|
|
// main thread right now, so we send a reference to ourselves to the main
|
|
// thread to be re-released there.
|
|
RefPtr<WeakReferenceSupport> self = this;
|
|
NS_ReleaseOnMainThreadSystemGroup(
|
|
"WeakReferenceSupport", self.forget());
|
|
}
|
|
}
|
|
return newRefCnt;
|
|
}
|
|
|
|
HRESULT
|
|
WeakReferenceSupport::GetWeakReference(IWeakReference** aOutWeakRef)
|
|
{
|
|
if (!aOutWeakRef) {
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
RefPtr<WeakRef> weakRef = MakeAndAddRef<WeakRef>(mSharedRef);
|
|
return weakRef->QueryInterface(IID_IWeakReference, (void**)aOutWeakRef);
|
|
}
|
|
|
|
WeakRef::WeakRef(RefPtr<detail::SharedRef>& aSharedRef)
|
|
: mRefCnt(0)
|
|
, mSharedRef(aSharedRef)
|
|
{
|
|
MOZ_ASSERT(aSharedRef);
|
|
}
|
|
|
|
HRESULT
|
|
WeakRef::QueryInterface(REFIID riid, void** ppv)
|
|
{
|
|
IUnknown* punk = nullptr;
|
|
if (!ppv) {
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (riid == IID_IUnknown || riid == IID_IWeakReference) {
|
|
punk = static_cast<IUnknown*>(this);
|
|
}
|
|
|
|
*ppv = punk;
|
|
if (!punk) {
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
punk->AddRef();
|
|
return S_OK;
|
|
}
|
|
|
|
ULONG
|
|
WeakRef::AddRef()
|
|
{
|
|
ULONG result = ++mRefCnt;
|
|
NS_LOG_ADDREF(this, result, "mscom::WeakRef", sizeof(*this));
|
|
return result;
|
|
}
|
|
|
|
ULONG
|
|
WeakRef::Release()
|
|
{
|
|
ULONG newRefCnt = --mRefCnt;
|
|
NS_LOG_RELEASE(this, newRefCnt, "mscom::WeakRef");
|
|
if (newRefCnt == 0) {
|
|
delete this;
|
|
}
|
|
return newRefCnt;
|
|
}
|
|
|
|
HRESULT
|
|
WeakRef::ToStrongRef(IWeakReferenceSource** aOutStrongReference)
|
|
{
|
|
return mSharedRef->ToStrongRef(aOutStrongReference);
|
|
}
|
|
|
|
HRESULT
|
|
WeakRef::Resolve(REFIID aIid, void** aOutStrongReference)
|
|
{
|
|
return mSharedRef->Resolve(aIid, aOutStrongReference);
|
|
}
|
|
|
|
} // namespace mscom
|
|
} // namespace mozilla
|
|
|