1 // Copyright 2019 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef UTIL_WEAK_PTR_H_ 6 #define UTIL_WEAK_PTR_H_ 7 8 #include <memory> 9 #include <utility> 10 11 #include "util/osp_logging.h" 12 13 namespace openscreen { 14 15 // Weak pointers are pointers to an object that do not affect its lifetime, 16 // and which may be invalidated (i.e. reset to nullptr) by the object, or its 17 // owner, at any time; most commonly when the object is about to be deleted. 18 // 19 // Weak pointers are useful when an object needs to be accessed safely by one 20 // or more objects other than its owner, and those callers can cope with the 21 // object vanishing and e.g. tasks posted to it being silently dropped. 22 // Reference-counting such an object would complicate the ownership graph and 23 // make it harder to reason about the object's lifetime. 24 // 25 // EXAMPLE: 26 // 27 // class Controller { 28 // public: 29 // void SpawnWorker() { new Worker(weak_factory_.GetWeakPtr()); } 30 // void WorkComplete(const Result& result) { ... } 31 // private: 32 // // Member variables should appear before the WeakPtrFactory, to ensure 33 // // that any WeakPtrs to Controller are invalidated before its members 34 // // variable's destructors are executed, rendering them invalid. 35 // WeakPtrFactory<Controller> weak_factory_{this}; 36 // }; 37 // 38 // class Worker { 39 // public: 40 // explicit Worker(WeakPtr<Controller> controller) 41 // : controller_(std::move(controller)) {} 42 // private: 43 // void DidCompleteAsynchronousProcessing(const Result& result) { 44 // if (controller_) 45 // controller_->WorkComplete(result); 46 // delete this; 47 // } 48 // const WeakPtr<Controller> controller_; 49 // }; 50 // 51 // With this implementation a caller may use SpawnWorker() to dispatch multiple 52 // Workers and subsequently delete the Controller, without waiting for all 53 // Workers to have completed. 54 // 55 // ------------------------- IMPORTANT: Thread-safety ------------------------- 56 // 57 // Generally, Open Screen code is meant to be single-threaded. For the few 58 // exceptional cases, the following is relevant: 59 // 60 // WeakPtrs may be created from WeakPtrFactory, and also duplicated/moved on any 61 // thread/sequence. However, they may only be dereferenced on the same 62 // thread/sequence that will ultimately execute the WeakPtrFactory destructor or 63 // call InvalidateWeakPtrs(). Otherwise, use-during-free or use-after-free is 64 // possible. 65 // 66 // openscreen::WeakPtr and WeakPtrFactory are similar, but not identical, to 67 // Chromium's base::WeakPtrFactory. Open Screen WeakPtrs may be safely created 68 // from WeakPtrFactory on any thread/sequence, since they are backed by the 69 // thread-safe bookkeeping of std::shared_ptr<>. 70 71 template <typename T> 72 class WeakPtrFactory; 73 74 template <typename T> 75 class WeakPtr { 76 public: 77 WeakPtr() = default; 78 ~WeakPtr() = default; 79 80 // Copy/Move constructors and assignment operators. WeakPtr(const WeakPtr & other)81 WeakPtr(const WeakPtr& other) : impl_(other.impl_) {} 82 WeakPtr(WeakPtr && other)83 WeakPtr(WeakPtr&& other) noexcept : impl_(std::move(other.impl_)) {} 84 85 WeakPtr& operator=(const WeakPtr& other) { 86 impl_ = other.impl_; 87 return *this; 88 } 89 90 WeakPtr& operator=(WeakPtr&& other) noexcept { 91 impl_ = std::move(other.impl_); 92 return *this; 93 } 94 95 // Create/Assign from nullptr. WeakPtr(std::nullptr_t)96 WeakPtr(std::nullptr_t) {} // NOLINT 97 98 WeakPtr& operator=(std::nullptr_t) { 99 impl_.reset(); 100 return *this; 101 } 102 103 // Copy/Move constructors and assignment operators with upcast conversion. 104 template <typename U> WeakPtr(const WeakPtr<U> & other)105 WeakPtr(const WeakPtr<U>& other) : impl_(other.as_std_weak_ptr()) {} 106 107 template <typename U> WeakPtr(WeakPtr<U> && other)108 WeakPtr(WeakPtr<U>&& other) noexcept 109 : impl_(std::move(other).as_std_weak_ptr()) {} 110 111 template <typename U> 112 WeakPtr& operator=(const WeakPtr<U>& other) { 113 impl_ = other.as_std_weak_ptr(); 114 return *this; 115 } 116 117 template <typename U> 118 WeakPtr& operator=(WeakPtr<U>&& other) noexcept { 119 impl_ = std::move(other).as_std_weak_ptr(); 120 return *this; 121 } 122 123 // Accessors. get()124 T* get() const { return impl_.lock().get(); } 125 126 T& operator*() const { 127 T* const pointer = get(); 128 OSP_DCHECK(pointer); 129 return *pointer; 130 } 131 132 T* operator->() const { 133 T* const pointer = get(); 134 OSP_DCHECK(pointer); 135 return pointer; 136 } 137 138 // Allow conditionals to test validity, e.g. if (weak_ptr) {...} 139 explicit operator bool() const { return get() != nullptr; } 140 141 // Conversion to std::weak_ptr<T>. It is unsafe to convert in the other 142 // direction. See comments for private constructors, below. as_std_weak_ptr()143 const std::weak_ptr<T>& as_std_weak_ptr() const& { return impl_; } as_std_weak_ptr()144 std::weak_ptr<T> as_std_weak_ptr() && { return std::move(impl_); } 145 146 private: 147 friend class WeakPtrFactory<T>; 148 149 // Called by WeakPtrFactory<T> and the WeakPtr<T> upcast conversion 150 // constructors and assigners. These are purposely not being exposed publicly 151 // because that would allow a WeakPtr<T> to be valid/invalid by a different 152 // ownership/threading model than the intended one (see top-level comments). 153 template <typename U> WeakPtr(const std::weak_ptr<U> & other)154 explicit WeakPtr(const std::weak_ptr<U>& other) : impl_(other) {} 155 156 template <typename U> WeakPtr(std::weak_ptr<U> && other)157 explicit WeakPtr(std::weak_ptr<U>&& other) noexcept 158 : impl_(std::move(other)) {} 159 160 std::weak_ptr<T> impl_; 161 }; 162 163 // Allow callers to compare WeakPtrs against nullptr to test validity. 164 template <typename T> 165 bool operator!=(const WeakPtr<T>& weak_ptr, std::nullptr_t) { 166 return weak_ptr.get() != nullptr; 167 } 168 template <typename T> 169 bool operator!=(std::nullptr_t, const WeakPtr<T>& weak_ptr) { 170 return weak_ptr.get() != nullptr; 171 } 172 template <typename T> 173 bool operator==(const WeakPtr<T>& weak_ptr, std::nullptr_t) { 174 return weak_ptr.get() == nullptr; 175 } 176 template <typename T> 177 bool operator==(std::nullptr_t, const WeakPtr<T>& weak_ptr) { 178 return weak_ptr == nullptr; 179 } 180 181 template <typename T> 182 class WeakPtrFactory { 183 public: WeakPtrFactory(T * instance)184 explicit WeakPtrFactory(T* instance) { Reset(instance); } 185 WeakPtrFactory(WeakPtrFactory&& other) noexcept = default; 186 WeakPtrFactory& operator=(WeakPtrFactory&& other) noexcept = default; 187 188 // Thread-safe: WeakPtrs may be created on any thread/seuence. They may also 189 // be copied and moved on any thread/sequence. However, they MUST only be 190 // dereferenced on the same thread/sequence that calls the destructor or 191 // InvalidateWeakPtrs(). GetWeakPtr()192 WeakPtr<T> GetWeakPtr() const { 193 return WeakPtr<T>(std::weak_ptr<T>(bookkeeper_)); 194 } 195 196 // Destruction and Invalidation: These must be called on the same 197 // thread/sequence that dereferences any WeakPtrs to avoid use-after-free 198 // bugs. 199 ~WeakPtrFactory() = default; InvalidateWeakPtrs()200 void InvalidateWeakPtrs() { Reset(bookkeeper_.get()); } 201 202 private: 203 WeakPtrFactory(const WeakPtrFactory& other) = delete; 204 WeakPtrFactory& operator=(const WeakPtrFactory& other) = delete; 205 Reset(T * instance)206 void Reset(T* instance) { 207 // T is owned externally to WeakPtrFactory. Thus, provide a no-op Deleter. 208 bookkeeper_ = {instance, [](T* instance) {}}; 209 } 210 211 // Manages the std::weak_ptr's referring to T. Does not own T. 212 std::shared_ptr<T> bookkeeper_; 213 }; 214 215 } // namespace openscreen 216 217 #endif // UTIL_WEAK_PTR_H_ 218