1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 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 */
|