| // Copyright 2019 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef UTIL_WEAK_PTR_H_ |
| #define UTIL_WEAK_PTR_H_ |
| |
| #include <memory> |
| #include <utility> |
| |
| #include "util/osp_logging.h" |
| |
| namespace openscreen { |
| |
| // Weak pointers are pointers to an object that do not affect its lifetime, |
| // and which may be invalidated (i.e. reset to nullptr) by the object, or its |
| // owner, at any time; most commonly when the object is about to be deleted. |
| // |
| // Weak pointers are useful when an object needs to be accessed safely by one |
| // or more objects other than its owner, and those callers can cope with the |
| // object vanishing and e.g. tasks posted to it being silently dropped. |
| // Reference-counting such an object would complicate the ownership graph and |
| // make it harder to reason about the object's lifetime. |
| // |
| // EXAMPLE: |
| // |
| // class Controller { |
| // public: |
| // void SpawnWorker() { new Worker(weak_factory_.GetWeakPtr()); } |
| // void WorkComplete(const Result& result) { ... } |
| // private: |
| // // Member variables should appear before the WeakPtrFactory, to ensure |
| // // that any WeakPtrs to Controller are invalidated before its members |
| // // variable's destructors are executed, rendering them invalid. |
| // WeakPtrFactory<Controller> weak_factory_{this}; |
| // }; |
| // |
| // class Worker { |
| // public: |
| // explicit Worker(WeakPtr<Controller> controller) |
| // : controller_(std::move(controller)) {} |
| // private: |
| // void DidCompleteAsynchronousProcessing(const Result& result) { |
| // if (controller_) |
| // controller_->WorkComplete(result); |
| // delete this; |
| // } |
| // const WeakPtr<Controller> controller_; |
| // }; |
| // |
| // With this implementation a caller may use SpawnWorker() to dispatch multiple |
| // Workers and subsequently delete the Controller, without waiting for all |
| // Workers to have completed. |
| // |
| // ------------------------- IMPORTANT: Thread-safety ------------------------- |
| // |
| // Generally, Open Screen code is meant to be single-threaded. For the few |
| // exceptional cases, the following is relevant: |
| // |
| // WeakPtrs may be created from WeakPtrFactory, and also duplicated/moved on any |
| // thread/sequence. However, they may only be dereferenced on the same |
| // thread/sequence that will ultimately execute the WeakPtrFactory destructor or |
| // call InvalidateWeakPtrs(). Otherwise, use-during-free or use-after-free is |
| // possible. |
| // |
| // openscreen::WeakPtr and WeakPtrFactory are similar, but not identical, to |
| // Chromium's base::WeakPtrFactory. Open Screen WeakPtrs may be safely created |
| // from WeakPtrFactory on any thread/sequence, since they are backed by the |
| // thread-safe bookkeeping of std::shared_ptr<>. |
| |
| template <typename T> |
| class WeakPtrFactory; |
| |
| template <typename T> |
| class WeakPtr { |
| public: |
| WeakPtr() = default; |
| ~WeakPtr() = default; |
| |
| // Copy/Move constructors and assignment operators. |
| WeakPtr(const WeakPtr& other) : impl_(other.impl_) {} |
| |
| WeakPtr(WeakPtr&& other) noexcept : impl_(std::move(other.impl_)) {} |
| |
| WeakPtr& operator=(const WeakPtr& other) { |
| impl_ = other.impl_; |
| return *this; |
| } |
| |
| WeakPtr& operator=(WeakPtr&& other) noexcept { |
| impl_ = std::move(other.impl_); |
| return *this; |
| } |
| |
| // Create/Assign from nullptr. |
| WeakPtr(std::nullptr_t) {} // NOLINT |
| |
| WeakPtr& operator=(std::nullptr_t) { |
| impl_.reset(); |
| return *this; |
| } |
| |
| // Copy/Move constructors and assignment operators with upcast conversion. |
| template <typename U> |
| WeakPtr(const WeakPtr<U>& other) : impl_(other.as_std_weak_ptr()) {} |
| |
| template <typename U> |
| WeakPtr(WeakPtr<U>&& other) noexcept |
| : impl_(std::move(other).as_std_weak_ptr()) {} |
| |
| template <typename U> |
| WeakPtr& operator=(const WeakPtr<U>& other) { |
| impl_ = other.as_std_weak_ptr(); |
| return *this; |
| } |
| |
| template <typename U> |
| WeakPtr& operator=(WeakPtr<U>&& other) noexcept { |
| impl_ = std::move(other).as_std_weak_ptr(); |
| return *this; |
| } |
| |
| // Accessors. |
| T* get() const { return impl_.lock().get(); } |
| |
| T& operator*() const { |
| T* const pointer = get(); |
| OSP_DCHECK(pointer); |
| return *pointer; |
| } |
| |
| T* operator->() const { |
| T* const pointer = get(); |
| OSP_DCHECK(pointer); |
| return pointer; |
| } |
| |
| // Allow conditionals to test validity, e.g. if (weak_ptr) {...} |
| explicit operator bool() const { return get() != nullptr; } |
| |
| // Conversion to std::weak_ptr<T>. It is unsafe to convert in the other |
| // direction. See comments for private constructors, below. |
| const std::weak_ptr<T>& as_std_weak_ptr() const& { return impl_; } |
| std::weak_ptr<T> as_std_weak_ptr() && { return std::move(impl_); } |
| |
| private: |
| friend class WeakPtrFactory<T>; |
| |
| // Called by WeakPtrFactory<T> and the WeakPtr<T> upcast conversion |
| // constructors and assigners. These are purposely not being exposed publicly |
| // because that would allow a WeakPtr<T> to be valid/invalid by a different |
| // ownership/threading model than the intended one (see top-level comments). |
| template <typename U> |
| explicit WeakPtr(const std::weak_ptr<U>& other) : impl_(other) {} |
| |
| template <typename U> |
| explicit WeakPtr(std::weak_ptr<U>&& other) noexcept |
| : impl_(std::move(other)) {} |
| |
| std::weak_ptr<T> impl_; |
| }; |
| |
| // Allow callers to compare WeakPtrs against nullptr to test validity. |
| template <typename T> |
| bool operator!=(const WeakPtr<T>& weak_ptr, std::nullptr_t) { |
| return weak_ptr.get() != nullptr; |
| } |
| template <typename T> |
| bool operator!=(std::nullptr_t, const WeakPtr<T>& weak_ptr) { |
| return weak_ptr.get() != nullptr; |
| } |
| template <typename T> |
| bool operator==(const WeakPtr<T>& weak_ptr, std::nullptr_t) { |
| return weak_ptr.get() == nullptr; |
| } |
| template <typename T> |
| bool operator==(std::nullptr_t, const WeakPtr<T>& weak_ptr) { |
| return weak_ptr == nullptr; |
| } |
| |
| template <typename T> |
| class WeakPtrFactory { |
| public: |
| explicit WeakPtrFactory(T* instance) { Reset(instance); } |
| WeakPtrFactory(WeakPtrFactory&& other) noexcept = default; |
| WeakPtrFactory& operator=(WeakPtrFactory&& other) noexcept = default; |
| |
| // Thread-safe: WeakPtrs may be created on any thread/seuence. They may also |
| // be copied and moved on any thread/sequence. However, they MUST only be |
| // dereferenced on the same thread/sequence that calls the destructor or |
| // InvalidateWeakPtrs(). |
| WeakPtr<T> GetWeakPtr() const { |
| return WeakPtr<T>(std::weak_ptr<T>(bookkeeper_)); |
| } |
| |
| // Destruction and Invalidation: These must be called on the same |
| // thread/sequence that dereferences any WeakPtrs to avoid use-after-free |
| // bugs. |
| ~WeakPtrFactory() = default; |
| void InvalidateWeakPtrs() { Reset(bookkeeper_.get()); } |
| |
| private: |
| WeakPtrFactory(const WeakPtrFactory& other) = delete; |
| WeakPtrFactory& operator=(const WeakPtrFactory& other) = delete; |
| |
| void Reset(T* instance) { |
| // T is owned externally to WeakPtrFactory. Thus, provide a no-op Deleter. |
| bookkeeper_ = {instance, [](T* instance) {}}; |
| } |
| |
| // Manages the std::weak_ptr's referring to T. Does not own T. |
| std::shared_ptr<T> bookkeeper_; |
| }; |
| |
| } // namespace openscreen |
| |
| #endif // UTIL_WEAK_PTR_H_ |