diff options
Diffstat (limited to 'ipc/mscom/WeakRef.cpp')
-rw-r--r-- | ipc/mscom/WeakRef.cpp | 191 |
1 files changed, 191 insertions, 0 deletions
diff --git a/ipc/mscom/WeakRef.cpp b/ipc/mscom/WeakRef.cpp new file mode 100644 index 000000000..f19310264 --- /dev/null +++ b/ipc/mscom/WeakRef.cpp @@ -0,0 +1,191 @@ +/* -*- 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/Assertions.h" +#include "mozilla/DebugOnly.h" +#include "mozilla/RefPtr.h" +#include "nsThreadUtils.h" +#include "nsWindowsHelpers.h" +#include "nsProxyRelease.h" + +namespace mozilla { +namespace mscom { + +WeakReferenceSupport::WeakReferenceSupport(Flags aFlags) + : mRefCnt(1) + , mFlags(aFlags) +{ + ::InitializeCriticalSectionAndSpinCount(&mCS, 4000); +} + +WeakReferenceSupport::~WeakReferenceSupport() +{ + MOZ_ASSERT(mWeakRefs.IsEmpty()); + ::DeleteCriticalSection(&mCS); +} + +HRESULT +WeakReferenceSupport::QueryInterface(REFIID riid, void** ppv) +{ + AutoCriticalSection lock(&mCS); + RefPtr<IUnknown> punk; + if (!ppv) { + return E_INVALIDARG; + } + *ppv = nullptr; + + // Raise the refcount for stabilization purposes during aggregation + RefPtr<IUnknown> kungFuDeathGrip(static_cast<IUnknown*>(this)); + + if (riid == IID_IUnknown || riid == IID_IWeakReferenceSource) { + punk = static_cast<IUnknown*>(this); + } else { + HRESULT hr = ThreadSafeQueryInterface(riid, getter_AddRefs(punk)); + if (FAILED(hr)) { + return hr; + } + } + + if (!punk) { + return E_NOINTERFACE; + } + + punk.forget(ppv); + return S_OK; +} + +ULONG +WeakReferenceSupport::AddRef() +{ + AutoCriticalSection lock(&mCS); + return ++mRefCnt; +} + +ULONG +WeakReferenceSupport::Release() +{ + ULONG newRefCnt; + { // Scope for lock + AutoCriticalSection lock(&mCS); + newRefCnt = --mRefCnt; + if (newRefCnt == 0) { + ClearWeakRefs(); + } + } + 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_ReleaseOnMainThread(self.forget()); + } + } + return newRefCnt; +} + +void +WeakReferenceSupport::ClearWeakRefs() +{ + for (uint32_t i = 0, len = mWeakRefs.Length(); i < len; ++i) { + mWeakRefs[i]->Clear(); + mWeakRefs[i]->Release(); + } + mWeakRefs.Clear(); +} + +HRESULT +WeakReferenceSupport::GetWeakReference(IWeakReference** aOutWeakRef) +{ + if (!aOutWeakRef) { + return E_INVALIDARG; + } + *aOutWeakRef = nullptr; + + AutoCriticalSection lock(&mCS); + RefPtr<WeakRef> weakRef = MakeAndAddRef<WeakRef>(this); + + HRESULT hr = weakRef->QueryInterface(IID_IWeakReference, (void**)aOutWeakRef); + if (FAILED(hr)) { + return hr; + } + + mWeakRefs.AppendElement(weakRef.get()); + weakRef->AddRef(); + return S_OK; +} + +WeakRef::WeakRef(WeakReferenceSupport* aSupport) + : mRefCnt(1) + , mMutex("mozilla::mscom::WeakRef::mMutex") + , mSupport(aSupport) +{ + MOZ_ASSERT(aSupport); +} + +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() +{ + return (ULONG) InterlockedIncrement((LONG*)&mRefCnt); +} + +ULONG +WeakRef::Release() +{ + ULONG newRefCnt = (ULONG) InterlockedDecrement((LONG*)&mRefCnt); + if (newRefCnt == 0) { + delete this; + } + return newRefCnt; +} + +HRESULT +WeakRef::Resolve(REFIID aIid, void** aOutStrongReference) +{ + MutexAutoLock lock(mMutex); + if (!mSupport) { + return E_FAIL; + } + return mSupport->QueryInterface(aIid, aOutStrongReference); +} + +void +WeakRef::Clear() +{ + MutexAutoLock lock(mMutex); + MOZ_ASSERT(mSupport); + mSupport = nullptr; +} + +} // namespace mscom +} // namespace mozilla + |