| // Copyright 2024 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. | |
| // The API in this header provides ability for objects to create weak pointers | |
| // to themselves. Unlike a regular pointer, a weak pointer is aware as to | |
| // whether the object it points to is still alive or has been deleted. Unlike | |
| // std::weak_ptr, QuicheWeakPtr does not require the referred object to be owned | |
| // by an std::shared_ptr, and is not thread-safe. Conceptually, this is similar | |
| // to base::WeakPtrFactory in Chromium, though the API and the implementation | |
| // are dramatically simplified. | |
| // | |
| // Example usage: | |
| // | |
| // class MyClass { | |
| // public: | |
| // void PerformAsyncOperation() { | |
| // ScheduleOperation([weak_this = weak_factory_.Create()] { | |
| // MyClass* self = weak_this.GetIfAvaliable(); | |
| // if (self == nullptr) { | |
| // return; | |
| // } | |
| // self->OnOperationComplete(); | |
| // }); | |
| // } | |
| // | |
| // private: | |
| // quiche::QuicheWeakPtrFactory<MyClass> weak_factory_; // Must be last | |
| // }; | |
| #ifndef QUICHE_COMMON_QUICHE_WEAK_PTR_H_ | |
| #define QUICHE_COMMON_QUICHE_WEAK_PTR_H_ | |
| #include <cstdint> | |
| #include <memory> | |
| #include "absl/base/nullability.h" | |
| #include "quiche/common/platform/api/quiche_export.h" | |
| #include "quiche/common/platform/api/quiche_logging.h" | |
| namespace quiche { | |
| template <typename T> | |
| class QuicheWeakPtrFactory; | |
| // QuicheWeakPtr contains a pointer to an object that may or may not be alive, | |
| // or nullptr. Two non-null weak pointers are equal if both of them were | |
| // originally created from the same factory object, even if that object is gone. | |
| template <typename T> | |
| class QUICHE_NO_EXPORT QuicheWeakPtr final { | |
| public: | |
| // Initializes a null weak pointer. | |
| QuicheWeakPtr() = default; | |
| // Returns the pointer to the underlying object if it is alive, or nullptr | |
| // otherwise. | |
| T* absl_nullable GetIfAvailable() const { | |
| return control_block_ != nullptr ? control_block_->Get() : nullptr; | |
| } | |
| // Returns true if the underlying object is alive. | |
| bool IsValid() const { | |
| return control_block_ != nullptr ? control_block_->IsValid() : false; | |
| } | |
| // Two weak pointers are equal if they point to the same control block. | |
| bool operator==(const QuicheWeakPtr&) const = default; | |
| bool operator!=(const QuicheWeakPtr&) const = default; | |
| // Hashing matches the operator== semantics. | |
| template <typename H> | |
| friend H AbslHashValue(H h, const QuicheWeakPtr& ptr) { | |
| return H::combine(std::move(h), ptr.control_block_.get()); | |
| } | |
| private: | |
| friend class QuicheWeakPtrFactory<T>; | |
| // An object shared by all of the weak pointers pointing to a given object. | |
| // Initially it points to the object itself; when the object is destryoed, the | |
| // contained pointer is set to nullptr. | |
| class ControlBlock { | |
| public: | |
| explicit ControlBlock(T* absl_nonnull object) : object_(object) {} | |
| T* absl_nullable Get() const { return object_; } | |
| void Clear() { object_ = nullptr; } | |
| bool IsValid() const { return object_ != nullptr; } | |
| private: | |
| T* absl_nullable object_; | |
| }; | |
| explicit QuicheWeakPtr(std::shared_ptr<ControlBlock> block) | |
| : control_block_(std::move(block)) {} | |
| absl_nullable std::shared_ptr<ControlBlock> control_block_ = nullptr; | |
| }; | |
| // QuicheWeakPtrFactory generates weak pointers to the parent object, and cleans | |
| // up the state when the parent object is destroyed. In order to do that | |
| // correctly, it MUST be the last field in the object holding it. | |
| template <typename T> | |
| class QUICHE_NO_EXPORT QuicheWeakPtrFactory final { | |
| public: | |
| explicit QuicheWeakPtrFactory(T* absl_nonnull object) | |
| : control_block_(std::make_shared<ControlBlock>(object)) {} | |
| ~QuicheWeakPtrFactory() { control_block_->Clear(); } | |
| QuicheWeakPtrFactory(const QuicheWeakPtrFactory&) = delete; | |
| QuicheWeakPtrFactory& operator=(const QuicheWeakPtrFactory&) = delete; | |
| // QuicheWeakPtrFactory is attached to the parent object; moving either the | |
| // parent object or the factory would almost certainly be a mistake, since all | |
| // of the existing WeakPtrs would point to the old object. | |
| QuicheWeakPtrFactory(QuicheWeakPtrFactory&&) = delete; | |
| QuicheWeakPtrFactory& operator=(QuicheWeakPtrFactory&&) = delete; | |
| // Creates a weak pointer to the parent object. | |
| QuicheWeakPtr<T> Create() const { return QuicheWeakPtr<T>(control_block_); } | |
| private: | |
| using ControlBlock = typename QuicheWeakPtr<T>::ControlBlock; | |
| absl_nonnull std::shared_ptr<ControlBlock> control_block_; | |
| }; | |
| } // namespace quiche | |
| #endif // QUICHE_COMMON_QUICHE_WEAK_PTR_H_ |