1 // Copyright 2023 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 
15 #pragma once
16 #include <pw_intrusive_ptr/intrusive_ptr.h>
17 
18 #include <optional>
19 
20 #include "pw_bluetooth_sapphire/internal/host/common/assert.h"
21 
22 template <typename T>
23 class DynamicWeakManager;
24 
25 // WeakRef is an intrusively-counted reference to an object that may or may not
26 // still exist. Check is_alive() before using the get() function to get a
27 // reference to the object.
28 //
29 // This is not thread-safe: get() must be used on the thread the WeakPtr was
30 // created on (but can be passed through other threads while not being used)
31 class WeakRef : public pw::RefCounted<WeakRef> {
32  public:
33   ~WeakRef() = default;
34 
35   // Returns true if the object referred is alive.
36   // If this returns true, WeakRef<T>::Get() will work.
is_alive()37   bool is_alive() { return !!ptr_; }
38 
39   // Get a reference to the alive object.
get()40   void* get() {
41     PW_CHECK(ptr_, "attempted to get a destroyed ptr");
42     return ptr_;
43   }
44 
set(void * p)45   void set(void* p) { ptr_ = p; }
46 
maybe_unset(const void * doomed)47   void maybe_unset(const void* doomed) {
48     if (ptr_ == doomed) {
49       ptr_ = nullptr;
50     }
51   }
52 
53  private:
54   template <class T>
55   friend class DynamicWeakManager;
56 
WeakRef(void * ptr)57   explicit WeakRef(void* ptr) : ptr_(ptr) {}
58 
59   // Pointer to the existent object if it is alive, otherwise a nullptr. We use
60   // a void* to avoid templating and to avoid having separate variables for the
61   // flag and the pointer. Avoiding templating enables us to support upcasting,
62   // as WeakRef type remains the same for the upcasted WeakPtr.
63   void* ptr_;
64 };
65 
66 // RecyclingWeakRef is a version of WeakRef which avoids deletion after the last
67 // count is destructed, instead marking itself as not in use, for reuse by a
68 // WeakManager that maintains a pool of RecyclingWeakRefs for static memory
69 // usage.
70 //
71 // For an example, see OnlyTwoStaticManager in the unit tests for WeakSelf.
72 class RecyclingWeakRef : public pw::Recyclable<RecyclingWeakRef>,
73                          public pw::RefCounted<RecyclingWeakRef> {
74  public:
RecyclingWeakRef()75   RecyclingWeakRef() : ptr_(nullptr) {}
76   ~RecyclingWeakRef() = default;
77 
78   // Returns true if the object referred is alive.
79   // If this returns true, Get() will work.
80   // If this returns true, in_use will also return true.
is_alive()81   bool is_alive() { return !!ptr_; }
82 
83   // Returns true if this ref is in use.
84   // This can return true while is_alive returns false.
is_in_use()85   bool is_in_use() { return in_use_; }
86 
get()87   void* get() {
88     PW_CHECK(in_use_, "shouldn't get an unallocated ptr");
89     PW_CHECK(ptr_, "attempted to get a destroyed ptr");
90     return ptr_;
91   }
92 
alloc(void * p)93   pw::IntrusivePtr<RecyclingWeakRef> alloc(void* p) {
94     PW_CHECK(!in_use_);
95     in_use_ = true;
96     ptr_ = p;
97     return pw::IntrusivePtr<RecyclingWeakRef>(this);
98   }
99 
maybe_unset(const void * doomed)100   void maybe_unset(const void* doomed) {
101     if (in_use_ && ptr_ == doomed) {
102       ptr_ = nullptr;
103     }
104   }
105 
106  private:
107   friend class pw::Recyclable<RecyclingWeakRef>;
108   // This method is called by Recyclable on the last reference drop.
pw_recycle()109   void pw_recycle() {
110     ptr_ = nullptr;
111     in_use_ = false;
112   }
113 
114   // True if this pointer is in use.
115   bool in_use_ = false;
116 
117   // Pointer to the existent object if it is alive, otherwise a nullptr.
118   void* ptr_;
119 };
120 
121 // Default Manager for Weak Pointers. Each object that derives from WeakSelf
122 // holds one manager object. This indirection is used to enable shared static
123 // memory weak pointers across multiple copies of the same class of objects.
124 //
125 // The default manager allocates a single weak pointer for each object that
126 // acquires at least one weak reference, and holds the weak reference alive
127 // until the object referenced is destroyed.
128 template <typename T>
129 class DynamicWeakManager {
130  public:
DynamicWeakManager(T * self_ptr)131   explicit DynamicWeakManager(T* self_ptr)
132       : self_ptr_(self_ptr), weak_ptr_ref_(nullptr) {}
133 
134   using RefType = WeakRef;
135 
~DynamicWeakManager()136   ~DynamicWeakManager() { InvalidateAll(); }
137 
GetWeakRef()138   std::optional<pw::IntrusivePtr<RefType>> GetWeakRef() {
139     if (weak_ptr_ref_ == nullptr) {
140       weak_ptr_ref_ = pw::IntrusivePtr(new RefType(self_ptr_));
141     }
142     return weak_ptr_ref_;
143   }
144 
InvalidateAll()145   void InvalidateAll() {
146     if (weak_ptr_ref_) {
147       weak_ptr_ref_->maybe_unset(self_ptr_);
148     }
149   }
150 
HasWeakRef()151   bool HasWeakRef() const { return weak_ptr_ref_ != nullptr; }
152 
153  private:
154   T* self_ptr_;
155   pw::IntrusivePtr<WeakRef> weak_ptr_ref_;
156 };
157 
158 template <typename T, typename WeakPtrManager>
159 class WeakSelf;
160 
161 template <typename T, typename WeakPtrManager = DynamicWeakManager<T>>
162 class WeakPtr {
163  public:
164   // Default-constructed WeakPtrs point nowhere and aren't alive.
WeakPtr()165   WeakPtr() : ptr_(nullptr) {}
WeakPtr(std::nullptr_t)166   explicit WeakPtr(std::nullptr_t) : WeakPtr() {}
167 
168   // Implicit upcast via copy construction.
169   template <typename U,
170             typename = std::enable_if_t<std::is_convertible_v<U*, T*>>>
WeakPtr(const WeakPtr<U> & r)171   WeakPtr(const WeakPtr<U>& r) : WeakPtr(r.ptr_) {}
172 
173   // Implicit upcast via move construction.
174   template <typename U,
175             typename = std::enable_if_t<std::is_convertible_v<U*, T*>>>
WeakPtr(WeakPtr<U> && r)176   WeakPtr(WeakPtr<U>&& r) : WeakPtr(std::move(r.ptr_)) {}
177 
is_alive()178   bool is_alive() const { return ptr_ && ptr_->is_alive(); }
get()179   T& get() const {
180     PW_CHECK(ptr_, "tried to get never-assigned weak pointer");
181     return *static_cast<T*>(ptr_->get());
182   }
183 
184   T* operator->() const { return &get(); }
185 
reset()186   void reset() { ptr_ = nullptr; }
187 
188  private:
189   // Allow accessing WeakPtr<U>'s member variables when upcasting.
190   template <typename U, typename RefType>
191   friend class WeakPtr;
192 
193   // Only WeakSelf<T> should have access to the constructor.
194   friend class WeakSelf<T, WeakPtrManager>;
195 
196   // Copy construct from an existing `IntrusivePtr`.
WeakPtr(const pw::IntrusivePtr<typename WeakPtrManager::RefType> & ptr)197   explicit WeakPtr(
198       const pw::IntrusivePtr<typename WeakPtrManager::RefType>& ptr)
199       : ptr_(ptr) {}
200 
201   // Move construct from an existing `IntrusivePtr`.
WeakPtr(pw::IntrusivePtr<typename WeakPtrManager::RefType> && ptr)202   explicit WeakPtr(pw::IntrusivePtr<typename WeakPtrManager::RefType>&& ptr)
203       : ptr_(std::move(ptr)) {}
204 
205   pw::IntrusivePtr<typename WeakPtrManager::RefType> ptr_;
206 };
207 
208 // WeakSelf is a class used to create pointers to an object that must be checked
209 // before using - because their target may have been destroyed.  These are
210 // termed "weak pointers" and can be vended in one of two ways: (1) inheriting
211 // from the WeakSelf class and initializing it on construction,
212 //
213 // class A: WeakSelf<A> {
214 //    A() : WeakSelf(this) {}
215 //    auto MakeSelfReferentialCallback() {
216 //       auto cb = [weak = GetWeakPtr()] {
217 //          if (weak.is_alive()) {
218 //             weak->AnotherFunction();
219 //          }
220 //       }
221 //       return std::move(cb);
222 //     }
223 //  };
224 //
225 // (2) making a WeakSelf<T> a member of your class and using it to vend
226 // pointers.
227 //
228 // class A {
229 //  public:
230 //    A() : weak_factory_(this) {}
231 //    auto MakeSelfReferentialCallback() {
232 //       auto cb = [weak = weak_factory_.GetWeakPtr()] {
233 //          if (weak.is_alive()) {
234 //             weak->AnotherFunction();
235 //          }
236 //       }
237 //       return std::move(cb);
238 //     }
239 //   private:
240 //    // Other members should be defined before weak_factory_ so it is destroyed
241 //    first. WeakSelf<A> weak_factory_;
242 //  };
243 //
244 // The first method is preferable if you expect to vend weak pointers outside
245 // the class, as the WeakSelf::GetWeakPtr function is public. However, note that
246 // with the first method, members of the class will be destroyed before the
247 // class is destroyed - it may be undesirable if during destruction, the weak
248 // pointer should be considered dead.  This can be mitigated by using
249 // InvalidatePtrs() to invalidate the weak pointers in the destructor.
250 template <typename T, typename WeakPtrManager = DynamicWeakManager<T>>
251 class WeakSelf {
252  public:
WeakSelf(T * self_ptr)253   explicit WeakSelf(T* self_ptr) : manager_(self_ptr) {}
254   ~WeakSelf() = default;
255 
256   using WeakPtr = WeakPtr<T, WeakPtrManager>;
257 
258   // Invalidates all the WeakPtrs that have been vended before now (they will
259   // return false for is_alive) and prevents any new pointers from being vended.
260   // This is effectively the same as calling the destructor, but can be done
261   // early.
InvalidatePtrs()262   void InvalidatePtrs() { manager_.InvalidateAll(); }
263 
GetWeakPtr()264   WeakPtr GetWeakPtr() {
265     auto weak_ref = manager_.GetWeakRef();
266     PW_CHECK(weak_ref.has_value(), "weak pointer not available");
267     return WeakPtr(*std::move(weak_ref));
268   }
269 
270  private:
271   WeakPtrManager manager_;
272 };
273