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