1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 /* Weak pointer functionality, implemented as a mixin for use with any class. */
8 
9 /**
10  * SupportsWeakPtr lets you have a pointer to an object 'Foo' without affecting
11  * its lifetime. It works by creating a single shared reference counted object
12  * (WeakReference) that each WeakPtr will access 'Foo' through. This lets 'Foo'
13  * clear the pointer in the WeakReference without having to know about all of
14  * the WeakPtrs to it and allows the WeakReference to live beyond the lifetime
15  * of 'Foo'.
16  *
17  * PLEASE NOTE: This weak pointer implementation is not thread-safe.
18  *
19  * Note that when deriving from SupportsWeakPtr you should add
20  * MOZ_DECLARE_WEAKREFERENCE_TYPENAME(ClassName) to the public section of your
21  * class, where ClassName is the name of your class.
22  *
23  * The overhead of WeakPtr is that accesses to 'Foo' becomes an additional
24  * dereference, and an additional heap allocated pointer sized object shared
25  * between all of the WeakPtrs.
26  *
27  * Example of usage:
28  *
29  *   // To have a class C support weak pointers, inherit from
30  *   // SupportsWeakPtr<C>.
31  *   class C : public SupportsWeakPtr<C>
32  *   {
33  *   public:
34  *     MOZ_DECLARE_WEAKREFERENCE_TYPENAME(C)
35  *     int mNum;
36  *     void act();
37  *   };
38  *
39  *   C* ptr = new C();
40  *
41  *   // Get weak pointers to ptr. The first time a weak pointer
42  *   // is obtained, a reference counted WeakReference object is created that
43  *   // can live beyond the lifetime of 'ptr'. The WeakReference
44  *   // object will be notified of 'ptr's destruction.
45  *   WeakPtr<C> weak = ptr;
46  *   WeakPtr<C> other = ptr;
47  *
48  *   // Test a weak pointer for validity before using it.
49  *   if (weak) {
50  *     weak->mNum = 17;
51  *     weak->act();
52  *   }
53  *
54  *   // Destroying the underlying object clears weak pointers to it.
55  *   delete ptr;
56  *
57  *   MOZ_ASSERT(!weak, "Deleting |ptr| clears weak pointers to it.");
58  *   MOZ_ASSERT(!other, "Deleting |ptr| clears all weak pointers to it.");
59  *
60  * WeakPtr is typesafe and may be used with any class. It is not required that
61  * the class be reference-counted or allocated in any particular way.
62  *
63  * The API was loosely inspired by Chromium's weak_ptr.h:
64  * http://src.chromium.org/svn/trunk/src/base/memory/weak_ptr.h
65  */
66 
67 #ifndef mozilla_WeakPtr_h
68 #define mozilla_WeakPtr_h
69 
70 #include "mozilla/ArrayUtils.h"
71 #include "mozilla/Assertions.h"
72 #include "mozilla/Attributes.h"
73 #include "mozilla/RefCounted.h"
74 #include "mozilla/RefPtr.h"
75 #include "mozilla/TypeTraits.h"
76 
77 #include <string.h>
78 
79 namespace mozilla {
80 
81 template <typename T> class WeakPtr;
82 template <typename T> class SupportsWeakPtr;
83 
84 #ifdef MOZ_REFCOUNTED_LEAK_CHECKING
85 #define MOZ_DECLARE_WEAKREFERENCE_TYPENAME(T) \
86   static const char* weakReferenceTypeName() { return "WeakReference<" #T ">"; }
87 #else
88 #define MOZ_DECLARE_WEAKREFERENCE_TYPENAME(T)
89 #endif
90 
91 namespace detail {
92 
93 // This can live beyond the lifetime of the class derived from
94 // SupportsWeakPtr.
95 template<class T>
96 class WeakReference : public ::mozilla::RefCounted<WeakReference<T> >
97 {
98 public:
WeakReference(T * p)99   explicit WeakReference(T* p) : mPtr(p) {}
100 
get()101   T* get() const { return mPtr; }
102 
103 #ifdef MOZ_REFCOUNTED_LEAK_CHECKING
typeName()104   const char* typeName() const
105   {
106     // The first time this is called mPtr is null, so don't
107     // invoke any methods on mPtr.
108     return T::weakReferenceTypeName();
109   }
typeSize()110   size_t typeSize() const { return sizeof(*this); }
111 #endif
112 
113 private:
114   friend class mozilla::SupportsWeakPtr<T>;
115 
detach()116   void detach() { mPtr = nullptr; }
117 
118   T* MOZ_NON_OWNING_REF mPtr;
119 };
120 
121 } // namespace detail
122 
123 template <typename T>
124 class SupportsWeakPtr
125 {
126 protected:
~SupportsWeakPtr()127   ~SupportsWeakPtr()
128   {
129     static_assert(IsBaseOf<SupportsWeakPtr<T>, T>::value,
130                   "T must derive from SupportsWeakPtr<T>");
131     if (mSelfReferencingWeakPtr) {
132       mSelfReferencingWeakPtr.mRef->detach();
133     }
134   }
135 
136 private:
SelfReferencingWeakPtr()137   const WeakPtr<T>& SelfReferencingWeakPtr()
138   {
139     if (!mSelfReferencingWeakPtr) {
140       mSelfReferencingWeakPtr.mRef = new detail::WeakReference<T>(static_cast<T*>(this));
141     }
142     return mSelfReferencingWeakPtr;
143   }
144 
SelfReferencingWeakPtr()145   const WeakPtr<const T>& SelfReferencingWeakPtr() const
146   {
147     const WeakPtr<T>& p = const_cast<SupportsWeakPtr*>(this)->SelfReferencingWeakPtr();
148     return reinterpret_cast<const WeakPtr<const T>&>(p);
149   }
150 
151   friend class WeakPtr<T>;
152   friend class WeakPtr<const T>;
153 
154   WeakPtr<T> mSelfReferencingWeakPtr;
155 };
156 
157 template <typename T>
158 class WeakPtr
159 {
160   typedef detail::WeakReference<T> WeakReference;
161 
162 public:
163   WeakPtr& operator=(const WeakPtr& aOther)
164   {
165     mRef = aOther.mRef;
166     return *this;
167   }
168 
WeakPtr(const WeakPtr & aOther)169   WeakPtr(const WeakPtr& aOther)
170   {
171     *this = aOther;
172   }
173 
174   WeakPtr& operator=(T* aOther)
175   {
176     if (aOther) {
177       *this = aOther->SelfReferencingWeakPtr();
178     } else if (!mRef || mRef->get()) {
179       // Ensure that mRef is dereferenceable in the uninitialized state.
180       mRef = new WeakReference(nullptr);
181     }
182     return *this;
183   }
184 
WeakPtr(T * aOther)185   MOZ_IMPLICIT WeakPtr(T* aOther)
186   {
187     *this = aOther;
188   }
189 
190   // Ensure that mRef is dereferenceable in the uninitialized state.
WeakPtr()191   WeakPtr() : mRef(new WeakReference(nullptr)) {}
192 
193   operator T*() const { return mRef->get(); }
194   T& operator*() const { return *mRef->get(); }
195 
196   T* operator->() const MOZ_NO_ADDREF_RELEASE_ON_RETURN { return mRef->get(); }
197 
get()198   T* get() const { return mRef->get(); }
199 
200 private:
201   friend class SupportsWeakPtr<T>;
202 
WeakPtr(const RefPtr<WeakReference> & aOther)203   explicit WeakPtr(const RefPtr<WeakReference>& aOther) : mRef(aOther) {}
204 
205   RefPtr<WeakReference> mRef;
206 };
207 
208 } // namespace mozilla
209 
210 #endif /* mozilla_WeakPtr_h */
211