// 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 #include #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 weak_factory_{this}; // }; // // class Worker { // public: // explicit Worker(WeakPtr controller) // : controller_(std::move(controller)) {} // private: // void DidCompleteAsynchronousProcessing(const Result& result) { // if (controller_) // controller_->WorkComplete(result); // delete this; // } // const WeakPtr 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 class WeakPtrFactory; template 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 WeakPtr(const WeakPtr& other) : impl_(other.as_std_weak_ptr()) {} template WeakPtr(WeakPtr&& other) noexcept : impl_(std::move(other).as_std_weak_ptr()) {} template WeakPtr& operator=(const WeakPtr& other) { impl_ = other.as_std_weak_ptr(); return *this; } template WeakPtr& operator=(WeakPtr&& 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. It is unsafe to convert in the other // direction. See comments for private constructors, below. const std::weak_ptr& as_std_weak_ptr() const& { return impl_; } std::weak_ptr as_std_weak_ptr() && { return std::move(impl_); } private: friend class WeakPtrFactory; // Called by WeakPtrFactory and the WeakPtr upcast conversion // constructors and assigners. These are purposely not being exposed publicly // because that would allow a WeakPtr to be valid/invalid by a different // ownership/threading model than the intended one (see top-level comments). template explicit WeakPtr(const std::weak_ptr& other) : impl_(other) {} template explicit WeakPtr(std::weak_ptr&& other) noexcept : impl_(std::move(other)) {} std::weak_ptr impl_; }; // Allow callers to compare WeakPtrs against nullptr to test validity. template bool operator!=(const WeakPtr& weak_ptr, std::nullptr_t) { return weak_ptr.get() != nullptr; } template bool operator!=(std::nullptr_t, const WeakPtr& weak_ptr) { return weak_ptr.get() != nullptr; } template bool operator==(const WeakPtr& weak_ptr, std::nullptr_t) { return weak_ptr.get() == nullptr; } template bool operator==(std::nullptr_t, const WeakPtr& weak_ptr) { return weak_ptr == nullptr; } template 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 GetWeakPtr() const { return WeakPtr(std::weak_ptr(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 bookkeeper_; }; } // namespace openscreen #endif // UTIL_WEAK_PTR_H_