• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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