Revision control

Line Code
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
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
/* -*- 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 necessarily already inherits from AtomicRefCounted,
 * so you should not separately inherit from AtomicRefCounted.
 *
 * 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_THREADSAFEWEAKREFERENCE_TYPENAME(ClassName) and
 * 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_THREADSAFEWEAKREFERENCE_TYPENAME(C)
 *     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/Atomics.h"
#include "mozilla/RefCounted.h"
#include "mozilla/RefPtr.h"
#include "mozilla/TypeTraits.h"
#include "mozilla/Unused.h"

#include <limits>

namespace mozilla {

template<typename T> class ThreadSafeWeakPtr;
template<typename T> class SupportsThreadSafeWeakPtr;

#ifdef MOZ_REFCOUNTED_LEAK_CHECKING
  #define MOZ_DECLARE_THREADSAFEWEAKREFERENCE_TYPENAME(T) \
    static const char* threadSafeWeakReferenceTypeName() { return "ThreadSafeWeakReference<" #T ">"; }
#else
  #define MOZ_DECLARE_THREADSAFEWEAKREFERENCE_TYPENAME(T)
#endif

namespace detail {

// A multiple reader, single writer spin-lock.
// This lock maintains an atomic counter which is incremented every time the lock is acquired
// reading. So long as the counter remains positive, it may be incremented for reading multiple
// times. When acquiring the lock for writing, we must ensure the counter is 0 (no readers),
// and if so, set it to a negative value to indicate that no new readers may take the lock.
class ReadWriteSpinLock
{
  // Only need a type large enough to represent the number of simultaneously accessing threads.
  typedef int32_t CounterType;

public:
  // Try to increment the counter for reading, so long as it is positive.
  void readLock()
  {
    for (;;)
    {
      CounterType oldCounter = mCounter & std::numeric_limits<CounterType>::max();
      CounterType newCounter = oldCounter + 1;
      if (mCounter.compareExchange(oldCounter, newCounter)) {
        break;
      }
    }
  }

  // Decrement the counter to remove a read lock.
  void readUnlock()
  {
    mCounter--;
  }

  // Try to acquire the write lock, but only if there are no readers.
  // If successful, sets the counter to a negative value.
  bool tryWriteLock()
  {
    return mCounter.compareExchange(0, std::numeric_limits<CounterType>::min());
  }

  // Reset the counter to 0.
  void writeUnlock()
  {
    mCounter = 0;
  }

private:
  Atomic<CounterType> mCounter;
};

// A shared weak reference that is used to track a SupportsThreadSafeWeakPtr object.
// It guards access to that object via a read-write spinlock.
template<typename T>
class ThreadSafeWeakReference : public external::AtomicRefCounted<ThreadSafeWeakReference<T>>
{
public:
  typedef T ElementType;

  explicit ThreadSafeWeakReference(T* aPtr)
  {
    mPtr = aPtr;
  }

#ifdef MOZ_REFCOUNTED_LEAK_CHECKING
  const char* typeName() const
  {
    // The first time this is called mPtr is null, so don't
    // invoke any methods on mPtr.
    return T::threadSafeWeakReferenceTypeName();
  }
  size_t typeSize() const { return sizeof(*this); }
#endif

private:
  friend class mozilla::SupportsThreadSafeWeakPtr<T>;
  template<typename U> friend class mozilla::ThreadSafeWeakPtr;

  // Does an unsafe read of the raw weak pointer.
  T* get() const
  {
    return mPtr;
  }

  // Creates a new RefPtr to the tracked object.
  // We need to acquire the read lock while we do this, as we need to atomically
  // both read the pointer and then increment the refcount on it within the scope
  // of the lock. This guards against the object being destroyed while in the middle
  // of creating the new RefPtr.
  already_AddRefed<T> getRefPtr()
  {
    mLock.readLock();
    RefPtr<T> result(get());
    mLock.readUnlock();
    return result.forget();
  }

  // Try to detach the weak reference from the tracked object.
  // We need to acquire the write lock while we do this, to ensure that no
  // RefPtr is created to this while detaching. Once acquired, it is safe
  // to check the refcount and verify that this is the last reference to
  // the tracked object, so the weak reference can be safely detached.
  void tryDetach(const SupportsThreadSafeWeakPtr<T>* aOwner)
  {
    if (mLock.tryWriteLock()) {
      if (aOwner->hasOneRef()) {
        mPtr = nullptr;
      }
      mLock.writeUnlock();
    }
  }

  ReadWriteSpinLock mLock;
  Atomic<T*> mPtr;
};

} // namespace detail

template<typename T>
class SupportsThreadSafeWeakPtr : public external::AtomicRefCounted<T>
{
protected:
  typedef external::AtomicRefCounted<T> AtomicRefCounted;
  typedef detail::ThreadSafeWeakReference<T> ThreadSafeWeakReference;

public:
  ~SupportsThreadSafeWeakPtr()
  {
    // Clean up the shared weak reference if one exists.
    if (ThreadSafeWeakReference* ptr = mRef) {
      ptr->Release();
    }
  }

  void Release() const
  {
    // If there is only one remaining reference to the object when trying to release,
    // then attempt to detach it from its weak reference. New references could possibly
    // be created to the object while this happens, so take care to do this atomically
    // inside tryDetach.
    if (AtomicRefCounted::hasOneRef()) {
      if (ThreadSafeWeakReference* ptr = mRef) {
        ptr->tryDetach(this);
      }
    }

    // Once possibly detached, it is now safe to continue to decrement the refcount.
    AtomicRefCounted::Release();
  }

private:
  template<typename U> friend class ThreadSafeWeakPtr;

  // Creates a shared weak reference for the object if one does not exist. Note that the
  // object may be of an actual derived type U, but the weak reference is created for the
  // supplied type T of SupportsThreadSafeWeakPtr<T>.
  already_AddRefed<ThreadSafeWeakReference> getThreadSafeWeakReference()
  {
    static_assert(IsBaseOf<SupportsThreadSafeWeakPtr<T>, T>::value,
                  "T must derive from SupportsThreadSafeWeakPtr<T>");

    if (!mRef) {
      RefPtr<ThreadSafeWeakReference> ptr(new ThreadSafeWeakReference(static_cast<T*>(this)));
      // Only set the new weak reference if one does not exist (== nullptr).
      // If there is already a weak reference, just let this superflous weak reference get
      // destroyed when it goes out of scope.
      if (mRef.compareExchange(nullptr, ptr)) {
        // If successful, forget the refcount so that the weak reference stays alive.
        Unused << ptr.forget();
      }
    }

    // Create a new RefPtr to weak reference.
    RefPtr<ThreadSafeWeakReference> ptr(mRef);
    return ptr.forget();
  }

  Atomic<ThreadSafeWeakReference*> mRef;
};

// A thread-safe variant of a weak pointer
template<typename T>
class ThreadSafeWeakPtr
{
  // Be careful to use the weak reference type T in the SupportsThreadSafeWeakPtr<T> definition.
  typedef typename T::ThreadSafeWeakReference ThreadSafeWeakReference;

public:
  ThreadSafeWeakPtr()
  {}

  ThreadSafeWeakPtr& operator=(const ThreadSafeWeakPtr& aOther)
  {
    mRef = aOther.mRef;
    return *this;
  }

  ThreadSafeWeakPtr(const ThreadSafeWeakPtr& aOther)
    : mRef(aOther.mRef)
  {
  }

  ThreadSafeWeakPtr& operator=(ThreadSafeWeakPtr&& aOther)
  {
    mRef = aOther.mRef.forget();
    return *this;
  }

  ThreadSafeWeakPtr(ThreadSafeWeakPtr&& aOther)
    : mRef(aOther.mRef.forget())
  {
  }

  ThreadSafeWeakPtr& operator=(const RefPtr<T>& aOther)
  {
    if (aOther) {
      // Get the underlying shared weak reference to the object, creating one if necessary.
      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))
  {}

  explicit operator bool() const
  {
    return !!get();
  }

  bool operator==(const ThreadSafeWeakPtr& aOther) const
  {
    return get() == aOther.get();
  }

  bool operator==(const RefPtr<T>& aOther) const
  {
    return get() == aOther.get();
  }

  bool operator==(const T* aOther) const
  {
    return get() == aOther;
  }

  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
  {
    static_assert(IsBaseOf<typename ThreadSafeWeakReference::ElementType, T>::value,
                  "T must derive from ThreadSafeWeakReference::ElementType");
    return mRef ? mRef->getRefPtr().template downcast<T>() : nullptr;
  }

  // Get a pointer to the tracked object, downcasting to the proper type T.
  // Note that this operation is unsafe as it may cause races if downwind
  // code depends on the value not to change after reading.
  T* get() const
  {
    static_assert(IsBaseOf<typename ThreadSafeWeakReference::ElementType, T>::value,
                  "T must derive from ThreadSafeWeakReference::ElementType");
    return mRef ? static_cast<T*>(mRef->get()) : nullptr;
  }

  // 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 */