summaryrefslogtreecommitdiffstats
path: root/mfbt/ThreadSafeWeakPtr.h
blob: d5176f5ffa2a81768a19fbab2eda1eef8d189740 (plain)
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 */