summaryrefslogtreecommitdiffstats
path: root/mfbt/ThreadSafeWeakPtr.h
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--mfbt/ThreadSafeWeakPtr.h309
1 files changed, 309 insertions, 0 deletions
diff --git a/mfbt/ThreadSafeWeakPtr.h b/mfbt/ThreadSafeWeakPtr.h
new file mode 100644
index 0000000000..d5176f5ffa
--- /dev/null
+++ b/mfbt/ThreadSafeWeakPtr.h
@@ -0,0 +1,309 @@
+/* -*- 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/. */
+
+/* A thread-safe weak pointer */
+
+/**
+ * Derive from SupportsThreadSafeWeakPtr to allow thread-safe weak pointers to
+ * an atomically refcounted derived class. These thread-safe weak pointers may
+ * be safely accessed and converted to strong pointers on multiple threads.
+ *
+ * Note that SupportsThreadSafeWeakPtr defines the same member functions as
+ * AtomicRefCounted, so you should not separately inherit from it.
+ *
+ * ThreadSafeWeakPtr and its implementation is distinct from the normal WeakPtr
+ * which is not thread-safe. The interface discipline and implementation details
+ * are different enough that these two implementations are separated for now for
+ * efficiency reasons. If you don't actually need to use weak pointers on
+ * multiple threads, you can just use WeakPtr instead.
+ *
+ * When deriving from SupportsThreadSafeWeakPtr, you should add
+ * MOZ_DECLARE_REFCOUNTED_TYPENAME(ClassName) to the public section of your
+ * class, where ClassName is the name of your class.
+ *
+ * Example usage:
+ *
+ * class C : public SupportsThreadSafeWeakPtr<C>
+ * {
+ * public:
+ * MOZ_DECLARE_REFCOUNTED_TYPENAME(C)
+ * void doStuff();
+ * };
+ *
+ * ThreadSafeWeakPtr<C> weak;
+ * {
+ * RefPtr<C> strong = new C;
+ * if (strong) {
+ * strong->doStuff();
+ * }
+ * // Make a new weak reference to the object from the strong reference.
+ * weak = strong;
+ * }
+ * MOZ_ASSERT(!bool(weak), "Weak pointers are cleared after all "
+ * "strong references are released.");
+ *
+ * // Convert the weak reference to a strong reference for usage.
+ * RefPtr<C> other(weak);
+ * if (other) {
+ * other->doStuff();
+ * }
+ */
+
+#ifndef mozilla_ThreadSafeWeakPtr_h
+#define mozilla_ThreadSafeWeakPtr_h
+
+#include "mozilla/Assertions.h"
+#include "mozilla/RefCountType.h"
+#include "mozilla/RefCounted.h"
+#include "mozilla/RefPtr.h"
+
+namespace mozilla {
+
+template <typename T>
+class ThreadSafeWeakPtr;
+
+template <typename T>
+class SupportsThreadSafeWeakPtr;
+
+namespace detail {
+
+class SupportsThreadSafeWeakPtrBase {};
+
+// A shared weak reference that is used to track a SupportsThreadSafeWeakPtr
+// object. This object owns the reference count for the tracked object, and can
+// perform atomic refcount upgrades.
+class ThreadSafeWeakReference
+ : public external::AtomicRefCounted<ThreadSafeWeakReference> {
+ public:
+ explicit ThreadSafeWeakReference(SupportsThreadSafeWeakPtrBase* aPtr)
+ : mPtr(aPtr) {}
+
+#ifdef MOZ_REFCOUNTED_LEAK_CHECKING
+ const char* typeName() const { return "ThreadSafeWeakReference"; }
+ size_t typeSize() const { return sizeof(*this); }
+#endif
+
+ private:
+ template <typename U>
+ friend class mozilla::SupportsThreadSafeWeakPtr;
+ template <typename U>
+ friend class mozilla::ThreadSafeWeakPtr;
+
+ // Number of strong references to the underlying data structure.
+ //
+ // Other than the initial strong `AddRef` call incrementing this value to 1,
+ // which must occur before any weak references are taken, once this value
+ // reaches `0` again it cannot be changed.
+ RC<MozRefCountType, AtomicRefCount> mStrongCnt{0};
+
+ // Raw pointer to the tracked object. It is never valid to read this value
+ // outside of `ThreadSafeWeakPtr::getRefPtr()`.
+ SupportsThreadSafeWeakPtrBase* MOZ_NON_OWNING_REF mPtr;
+};
+
+} // namespace detail
+
+// For usage documentation for SupportsThreadSafeWeakPtr, see the header-level
+// documentation.
+//
+// To understand the layout of SupportsThreadSafeWeakPtr, consider the following
+// simplified declaration:
+//
+// class MyType: SupportsThreadSafeWeakPtr { uint32_t mMyData; ... }
+//
+// Which will result in the following layout:
+//
+// +--------------------+
+// | MyType | <===============================================+
+// +--------------------+ I
+// | RefPtr mWeakRef o======> +-------------------------------------+ I
+// | uint32_t mMyData | | ThreadSafeWeakReference | I
+// +--------------------+ +-------------------------------------+ I
+// | RC mRefCount | I
+// | RC mStrongCount | I
+// | SupportsThreadSafeWeakPtrBase* mPtr o====+
+// +-------------------------------------+
+//
+// The mRefCount inherited from AtomicRefCounted<ThreadSafeWeakReference> is the
+// weak count. This means MyType implicitly holds a weak reference, so if the
+// weak count ever hits 0, we know all strong *and* weak references are gone,
+// and it's safe to free the ThreadSafeWeakReference. MyType's AddRef and
+// Release implementations otherwise only manipulate mStrongCount.
+//
+// It's necessary to keep the counts in a separate allocation because we need
+// to be able to delete MyType while weak references still exist. This ensures
+// that weak references can still access all the state necessary to check if
+// they can be upgraded (mStrongCount).
+template <typename T>
+class SupportsThreadSafeWeakPtr : public detail::SupportsThreadSafeWeakPtrBase {
+ protected:
+ using ThreadSafeWeakReference = detail::ThreadSafeWeakReference;
+
+ // The `this` pointer will not have subclasses initialized yet, but it will
+ // also not be read until a weak pointer is upgraded, which should be after
+ // this point.
+ SupportsThreadSafeWeakPtr() : mWeakRef(new ThreadSafeWeakReference(this)) {
+ static_assert(std::is_base_of_v<SupportsThreadSafeWeakPtr, T>,
+ "T must derive from SupportsThreadSafeWeakPtr");
+ }
+
+ public:
+ // Compatibility with RefPtr
+ MozExternalRefCountType AddRef() const {
+ auto& refCnt = mWeakRef->mStrongCnt;
+ MOZ_ASSERT(int32_t(refCnt) >= 0);
+ MozRefCountType cnt = ++refCnt;
+ detail::RefCountLogger::logAddRef(static_cast<const T*>(this), cnt);
+ return cnt;
+ }
+
+ MozExternalRefCountType Release() const {
+ auto& refCnt = mWeakRef->mStrongCnt;
+ MOZ_ASSERT(int32_t(refCnt) > 0);
+ detail::RefCountLogger::ReleaseLogger logger(static_cast<const T*>(this));
+ MozRefCountType cnt = --refCnt;
+ logger.logRelease(cnt);
+ if (0 == cnt) {
+ // Because we have atomically decremented the refcount above, only one
+ // thread can get a 0 count here. Thus, it is safe to access and destroy
+ // |this| here.
+ // No other thread can acquire a strong reference to |this| anymore
+ // through our weak pointer, as upgrading a weak pointer always uses
+ // |IncrementIfNonzero|, meaning the refcount can't leave a zero reference
+ // state.
+ // NOTE: We can't update our refcount to the marker `DEAD` value here, as
+ // it may still be read by mWeakRef.
+ delete static_cast<const T*>(this);
+ }
+ return cnt;
+ }
+
+ using HasThreadSafeRefCnt = std::true_type;
+
+ // Compatibility with wtf::RefPtr
+ void ref() { AddRef(); }
+ void deref() { Release(); }
+ MozRefCountType refCount() const { return mWeakRef->mStrongCnt; }
+ bool hasOneRef() const { return refCount() == 1; }
+
+ private:
+ template <typename U>
+ friend class ThreadSafeWeakPtr;
+
+ ThreadSafeWeakReference* getThreadSafeWeakReference() const {
+ return mWeakRef;
+ }
+
+ const RefPtr<ThreadSafeWeakReference> mWeakRef;
+};
+
+// A thread-safe variant of a weak pointer
+template <typename T>
+class ThreadSafeWeakPtr {
+ using ThreadSafeWeakReference = detail::ThreadSafeWeakReference;
+
+ public:
+ ThreadSafeWeakPtr() = default;
+
+ ThreadSafeWeakPtr& operator=(const ThreadSafeWeakPtr& aOther) = default;
+ ThreadSafeWeakPtr(const ThreadSafeWeakPtr& aOther) = default;
+
+ ThreadSafeWeakPtr& operator=(ThreadSafeWeakPtr&& aOther) = default;
+ ThreadSafeWeakPtr(ThreadSafeWeakPtr&& aOther) = default;
+
+ ThreadSafeWeakPtr& operator=(const RefPtr<T>& aOther) {
+ if (aOther) {
+ // Get the underlying shared weak reference to the object.
+ mRef = aOther->getThreadSafeWeakReference();
+ } else {
+ mRef = nullptr;
+ }
+ return *this;
+ }
+
+ explicit ThreadSafeWeakPtr(const RefPtr<T>& aOther) { *this = aOther; }
+
+ ThreadSafeWeakPtr& operator=(decltype(nullptr)) {
+ mRef = nullptr;
+ return *this;
+ }
+
+ explicit ThreadSafeWeakPtr(decltype(nullptr)) {}
+
+ // Use the explicit `IsNull()` or `IsDead()` methods instead.
+ explicit operator bool() const = delete;
+
+ // Check if the ThreadSafeWeakPtr was created wrapping a null pointer.
+ bool IsNull() const { return !mRef; }
+
+ // Check if the managed object is nullptr or has already been destroyed. Once
+ // IsDead returns true, this ThreadSafeWeakPtr can never be upgraded again
+ // (until it has been re-assigned), but a false return value does NOT imply
+ // that any future upgrade will be successful.
+ bool IsDead() const { return IsNull() || size_t(mRef->mStrongCnt) == 0; }
+
+ bool operator==(const ThreadSafeWeakPtr& aOther) const {
+ return mRef == aOther.mRef;
+ }
+
+ bool operator==(const RefPtr<T>& aOther) const {
+ return *this == aOther.get();
+ }
+
+ friend bool operator==(const RefPtr<T>& aStrong,
+ const ThreadSafeWeakPtr& aWeak) {
+ return aWeak == aStrong.get();
+ }
+
+ bool operator==(const T* aOther) const {
+ if (!mRef) {
+ return !aOther;
+ }
+ return aOther && aOther->getThreadSafeWeakReference() == mRef;
+ }
+
+ template <typename U>
+ bool operator!=(const U& aOther) const {
+ return !(*this == aOther);
+ }
+
+ // Convert the weak pointer to a strong RefPtr.
+ explicit operator RefPtr<T>() const { return getRefPtr(); }
+
+ private:
+ // Gets a new strong reference of the proper type T to the tracked object.
+ already_AddRefed<T> getRefPtr() const {
+ if (!mRef) {
+ return nullptr;
+ }
+ // Increment our strong reference count only if it is nonzero, meaning that
+ // the object is still alive.
+ MozRefCountType cnt = mRef->mStrongCnt.IncrementIfNonzero();
+ if (cnt == 0) {
+ return nullptr;
+ }
+
+ RefPtr<T> ptr = already_AddRefed<T>(static_cast<T*>(mRef->mPtr));
+ detail::RefCountLogger::logAddRef(ptr.get(), cnt);
+ return ptr.forget();
+ }
+
+ // A shared weak reference to an object. Note that this may be null so as to
+ // save memory (at the slight cost of an extra null check) if no object is
+ // being tracked.
+ RefPtr<ThreadSafeWeakReference> mRef;
+};
+
+} // namespace mozilla
+
+template <typename T>
+inline already_AddRefed<T> do_AddRef(
+ const mozilla::ThreadSafeWeakPtr<T>& aObj) {
+ RefPtr<T> ref(aObj);
+ return ref.forget();
+}
+
+#endif /* mozilla_ThreadSafeWeakPtr_h */