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 BT_ASSERT_MSG(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 BT_ASSERT_MSG(in_use_, "shouldn't get an unallocated ptr"); 89 BT_ASSERT_MSG(ptr_, "attempted to get a destroyed ptr"); 90 return ptr_; 91 } 92 alloc(void * p)93 pw::IntrusivePtr<RecyclingWeakRef> alloc(void* p) { 94 BT_ASSERT(!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 151 private: 152 T* self_ptr_; 153 pw::IntrusivePtr<WeakRef> weak_ptr_ref_; 154 }; 155 156 template <typename T, typename WeakPtrManager> 157 class WeakSelf; 158 159 template <typename T, typename WeakPtrManager = DynamicWeakManager<T>> 160 class WeakPtr { 161 public: 162 // Default-constructed WeakPtrs point nowhere and aren't alive. WeakPtr()163 WeakPtr() : ptr_(nullptr) {} WeakPtr(std::nullptr_t)164 explicit WeakPtr(std::nullptr_t) : WeakPtr() {} 165 166 // Implicit upcast via copy construction. 167 template <typename U, 168 typename = std::enable_if_t<std::is_convertible_v<U*, T*>>> WeakPtr(const WeakPtr<U> & r)169 WeakPtr(const WeakPtr<U>& r) : WeakPtr(r.ptr_) {} 170 171 // Implicit upcast via move construction. 172 template <typename U, 173 typename = std::enable_if_t<std::is_convertible_v<U*, T*>>> WeakPtr(WeakPtr<U> && r)174 WeakPtr(WeakPtr<U>&& r) : WeakPtr(std::move(r.ptr_)) {} 175 is_alive()176 bool is_alive() const { return ptr_ && ptr_->is_alive(); } get()177 T& get() const { 178 BT_ASSERT_MSG(ptr_, "tried to get never-assigned weak pointer"); 179 return *static_cast<T*>(ptr_->get()); 180 } 181 182 T* operator->() const { return &get(); } 183 reset()184 void reset() { ptr_ = nullptr; } 185 186 private: 187 // Allow accessing WeakPtr<U>'s member variables when upcasting. 188 template <typename U, typename RefType> 189 friend class WeakPtr; 190 191 // Only WeakSelf<T> should have access to the constructor. 192 friend class WeakSelf<T, WeakPtrManager>; 193 WeakPtr(pw::IntrusivePtr<typename WeakPtrManager::RefType> ptr)194 explicit WeakPtr(pw::IntrusivePtr<typename WeakPtrManager::RefType> ptr) 195 : ptr_(std::move(ptr)) {} 196 197 pw::IntrusivePtr<typename WeakPtrManager::RefType> ptr_; 198 }; 199 200 // WeakSelf is a class used to create pointers to an object that must be checked 201 // before using - because their target may have been destroyed. These are 202 // termed "weak pointers" and can be vended in one of two ways: (1) inheriting 203 // from the WeakSelf class and initializing it on construction, 204 // 205 // class A: WeakSelf<A> { 206 // A() : WeakSelf(this) {} 207 // auto MakeSelfReferentialCallback() { 208 // auto cb = [weak = GetWeakPtr()] { 209 // if (weak.is_alive()) { 210 // weak->AnotherFunction(); 211 // } 212 // } 213 // return std::move(cb); 214 // } 215 // }; 216 // 217 // (2) making a WeakSelf<T> a member of your class and using it to vend 218 // pointers. 219 // 220 // class A { 221 // public: 222 // A() : weak_factory_(this) {} 223 // auto MakeSelfReferentialCallback() { 224 // auto cb = [weak = weak_factory_.GetWeakPtr()] { 225 // if (weak.is_alive()) { 226 // weak->AnotherFunction(); 227 // } 228 // } 229 // return std::move(cb); 230 // } 231 // private: 232 // // Other members should be defined before weak_factory_ so it is destroyed 233 // first. WeakSelf<A> weak_factory_; 234 // }; 235 // 236 // The first method is preferable if you expect to vend weak pointers outside 237 // the class, as the WeakSelf::GetWeakPtr function is public. However, note that 238 // with the first method, members of the class will be destroyed before the 239 // class is destroyed - it may be undesirable if during destruction, the weak 240 // pointer should be considered dead. This can be mitigated by using 241 // InvalidatePtrs() to invalidate the weak pointers in the destructor. 242 template <typename T, typename WeakPtrManager = DynamicWeakManager<T>> 243 class WeakSelf { 244 public: WeakSelf(T * self_ptr)245 explicit WeakSelf(T* self_ptr) : manager_(self_ptr) {} 246 ~WeakSelf() = default; 247 248 using WeakPtr = WeakPtr<T, WeakPtrManager>; 249 250 // Invalidates all the WeakPtrs that have been vended before now (they will 251 // return false for is_alive) and prevents any new pointers from being vended. 252 // This is effectively the same as calling the destructor, but can be done 253 // early. InvalidatePtrs()254 void InvalidatePtrs() { manager_.InvalidateAll(); } 255 GetWeakPtr()256 WeakPtr GetWeakPtr() { 257 auto weak_ref = manager_.GetWeakRef(); 258 BT_ASSERT_MSG(weak_ref.has_value(), "weak pointer not available"); 259 return WeakPtr(*weak_ref); 260 } 261 262 private: 263 WeakPtrManager manager_; 264 }; 265