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 #ifndef nsTWeakRef_h__
8 #define nsTWeakRef_h__
9 
10 #ifndef nsDebug_h___
11 #include "nsDebug.h"
12 #endif
13 
14 /**
15  * A weak reference class for use with generic C++ objects.  NOT THREADSAFE!
16  *
17  * Example usage:
18  *
19  *   class A {
20  *   public:
21  *     A() : mWeakSelf(this) {
22  *     }
23  *     ~A() {
24  *       mWeakSelf.forget();
25  *     }
26  *     void Bar() { printf("Bar!\n"); }
27  *     const nsTWeakRef<A> &AsWeakRef() const { return mWeakSelf; }
28  *   private:
29  *     nsTWeakRef<A> mWeakSelf;
30  *   };
31  *
32  *   class B {
33  *   public:
34  *     void SetA(const nsTWeakRef<A> &a) {
35  *       mA = a;
36  *     }
37  *     void Foo() {
38  *       if (mA)
39  *         mA->Bar();
40  *     }
41  *   private:
42  *     nsTWeakRef<A> mA;
43  *   };
44  *
45  *   void Test() {
46  *     B b;
47  *     {
48  *       A a;
49  *       b.SetA(a.AsWeakRef());
50  *       b.Foo();  // prints "Bar!"
51  *     }
52  *     b.Foo();  // prints nothing because |a| has already been destroyed
53  *   }
54  *
55  * One can imagine much more complex examples, especially when asynchronous
56  * event processing is involved.
57  *
58  * Keep in mind that you should only ever need a class like this when you have
59  * multiple instances of B, such that it is not possible for A and B to simply
60  * have pointers to one another.
61  */
62 template<class Type>
63 class nsTWeakRef
64 {
65 public:
~nsTWeakRef()66   ~nsTWeakRef()
67   {}
68 
69   /**
70    * Construct from an object pointer (may be null).
71    */
72   explicit nsTWeakRef(Type* aObj = nullptr)
73   {
74     if (aObj) {
75       mRef = new Inner(aObj);
76     } else {
77       mRef = nullptr;
78     }
79   }
80 
81   /**
82    * Construct from another weak reference object.
83    */
nsTWeakRef(const nsTWeakRef<Type> & aOther)84   explicit nsTWeakRef(const nsTWeakRef<Type>& aOther) : mRef(aOther.mRef)
85   {}
86 
87   /**
88    * Assign from an object pointer.
89    */
90   nsTWeakRef<Type>& operator=(Type* aObj)
91   {
92     if (aObj) {
93       mRef = new Inner(aObj);
94     } else {
95       mRef = nullptr;
96     }
97     return *this;
98   }
99 
100   /**
101    * Assign from another weak reference object.
102    */
103   nsTWeakRef<Type>& operator=(const nsTWeakRef<Type>& aOther)
104   {
105     mRef = aOther.mRef;
106     return *this;
107   }
108 
109   /**
110    * Get the referenced object.  This method may return null if the reference
111    * has been cleared or if an out-of-memory error occurred at assignment.
112    */
get()113   Type* get() const { return mRef ? mRef->mObj : nullptr; }
114 
115   /**
116    * Called to "null out" the weak reference.  Typically, the object referenced
117    * by this weak reference calls this method when it is being destroyed.
118    * @returns The former referenced object.
119    */
forget()120   Type* forget()
121   {
122     Type* obj;
123     if (mRef) {
124       obj = mRef->mObj;
125       mRef->mObj = nullptr;
126       mRef = nullptr;
127     } else {
128       obj = nullptr;
129     }
130     return obj;
131   }
132 
133   /**
134    * Allow |*this| to be treated as a |Type*| for convenience.
135    */
136   operator Type*() const { return get(); }
137 
138   /**
139    * Allow |*this| to be treated as a |Type*| for convenience.  Use with
140    * caution since this method will crash if the referenced object is null.
141    */
142   Type* operator->() const MOZ_NO_ADDREF_RELEASE_ON_RETURN
143   {
144     NS_ASSERTION(mRef && mRef->mObj,
145                  "You can't dereference a null weak reference with operator->().");
146     return get();
147   }
148 
149 private:
150 
151   struct Inner
152   {
153     int     mCnt;
154     Type*   mObj;
155 
InnerInner156     explicit Inner(Type* aObj)
157       : mCnt(1)
158       , mObj(aObj)
159     {
160     }
AddRefInner161     void AddRef()
162     {
163       ++mCnt;
164     }
ReleaseInner165     void Release()
166     {
167       if (--mCnt == 0) {
168         delete this;
169       }
170     }
171   };
172 
173   RefPtr<Inner> mRef;
174 };
175 
176 #endif  // nsTWeakRef_h__
177