xref: /aosp_15_r20/external/cronet/base/memory/safe_ref.h (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2021 The Chromium Authors
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 BASE_MEMORY_SAFE_REF_H_
6 #define BASE_MEMORY_SAFE_REF_H_
7 
8 #include <compare>
9 #include <concepts>
10 #include <utility>
11 
12 #include "base/check.h"
13 #include "base/memory/safe_ref_traits.h"
14 #include "base/memory/weak_ptr.h"
15 
16 namespace base {
17 
18 // SafeRef smart pointers are used to represent a non-owning pointer to an
19 // object, where the pointer is always intended to be valid. These are useful in
20 // the same cases that a raw pointer `T*` (or a `T&`) would traditionally be
21 // used, as the owner of the SafeRef knows the lifetime of the pointed-to object
22 // from other means and will not use the pointer after the pointed-to object is
23 // destroyed. However, unlike a `T*` or `T&`, a logic bug will manifest as a
24 // benign crash instead of as a Use-after-Free.
25 //
26 // SafeRef pointers cannot be null (as expressed by the "Ref" suffix instead of
27 // "Ptr"). A SafeRef can be wrapped in an std::optional if it should not always
28 // point to something valid. (A SafePtr sibling type can be introduced if this
29 // is problematic, or if consuming moves are needed!)
30 //
31 // If code wants to track the lifetime of the object directly through its
32 // pointer, and dynamically handle the case of the pointer outliving the object
33 // it points to, then base::WeakPtr should be used instead.
34 //
35 // The SafeRef pointer is constructed from a base::WeakPtrFactory's GetSafeRef()
36 // method. Since it is tied to the base::WeakPtrFactory, it will consider its
37 // pointee invalid when the base::WeakPtrFactory is invalidated, in the same way
38 // as base::WeakPtr does, including after a call to InvalidateWeakPtrs().
39 //
40 // SafeRefTraits are only meant to mark SafeRefs that were found to be dangling,
41 // thus one should not use this flag to disable dangling pointer detection on
42 // SafeRef. This parameter is set to SafeRefTraits::kEmpty by default.
43 //
44 // THREAD SAFETY: SafeRef pointers (like base::WeakPtr) may only be used on the
45 // sequence (or thread) where the associated base::WeakPtrFactory will be
46 // invalidated and/or destroyed. They are safe to passively hold or to destroy
47 // on any thread though.
48 //
49 // This class is expected to one day be replaced by a more flexible and safe
50 // smart pointer abstraction which is not tied to base::WeakPtrFactory, such as
51 // raw_ptr<T> from the MiraclePtr project (though perhaps a non-nullable raw_ref
52 // equivalent).
53 template <typename T, SafeRefTraits Traits /*= SafeRefTraits::kEmpty*/>
54 class SafeRef {
55  public:
56   // No default constructor, since there's no null state. Use an optional
57   // SafeRef if the pointer may not be present.
58 
59   // Copy construction and assignment.
SafeRef(const SafeRef & other)60   SafeRef(const SafeRef& other) : ref_(other.ref_), ptr_(other.ptr_) {
61     // Avoid use-after-move.
62     CHECK(ref_.IsValid());
63   }
64   SafeRef& operator=(const SafeRef& other) {
65     ref_ = other.ref_;
66     ptr_ = other.ptr_;
67     // Avoid use-after-move.
68     CHECK(ref_.IsValid());
69     return *this;
70   }
71 
72   // Move construction and assignment.
SafeRef(SafeRef && other)73   SafeRef(SafeRef&& other)
74       : ref_(std::move(other.ref_)), ptr_(std::move(other.ptr_)) {
75     // Avoid use-after-move.
76     CHECK(ref_.IsValid());
77   }
78   SafeRef& operator=(SafeRef&& other) {
79     ref_ = std::move(other.ref_);
80     ptr_ = std::move(other.ptr_);
81     // Avoid use-after-move.
82     CHECK(ref_.IsValid());
83     return *this;
84   }
85 
86   // Copy conversion from SafeRef<U>.
87   template <typename U>
requires(std::convertible_to<U *,T * >)88     requires(std::convertible_to<U*, T*>)
89   // NOLINTNEXTLINE(google-explicit-constructor)
90   SafeRef(const SafeRef<U>& other)
91       : ref_(other.ref_),
92         ptr_(other.ptr_)  // raw_ptr<U> converts to raw_ptr<T>.
93   {
94     // Avoid use-after-move.
95     CHECK(ref_.IsValid());
96   }
97   template <typename U>
98   SafeRef& operator=(const SafeRef<U>& other) {
99     ref_ = other.ref_;
100     ptr_ = other.ptr_;  // raw_ptr<U> converts to raw_ptr<T>.
101     // Avoid use-after-move.
102     CHECK(ref_.IsValid());
103     return *this;
104   }
105 
106   // Move conversion from SafeRef<U>.
107   template <typename U>
108   // NOLINTNEXTLINE(google-explicit-constructor)
SafeRef(SafeRef<U> && other)109   SafeRef(SafeRef<U>&& other)
110       : ref_(std::move(other.ref_)),
111         ptr_(std::move(other.ptr_))  // raw_ptr<U> converts to raw_ptr<T>.
112   {
113     // Avoid use-after-move.
114     CHECK(ref_.IsValid());
115   }
116   template <typename U>
117   SafeRef& operator=(SafeRef<U>&& other) {
118     ref_ = std::move(other.ref_);
119     ptr_ = std::move(other.ptr_);  // raw_ptr<U> converts to raw_ptr<T>.
120     // Avoid use-after-move.
121     CHECK(ref_.IsValid());
122     return *this;
123   }
124 
125   // Ordered by the pointer, not the pointee.
126   template <typename U>
127   std::strong_ordering operator<=>(const SafeRef<U>& other) const {
128     return ptr_ <=> other.ptr_;
129   }
130 
131   // Provide access to the underlying T as a reference. Will CHECK() if the T
132   // pointee is no longer alive.
133   T& operator*() const {
134     CHECK(ref_.IsValid());
135     return *ptr_;
136   }
137 
138   // Used to call methods on the underlying T. Will CHECK() if the T pointee is
139   // no longer alive.
140   T* operator->() const {
141     CHECK(ref_.IsValid());
142     return &*ptr_;
143   }
144 
145  private:
146   template <typename U, SafeRefTraits PassedTraits>
147   friend class SafeRef;
148   template <typename U>
149   friend SafeRef<U> internal::MakeSafeRefFromWeakPtrInternals(
150       internal::WeakReference&& ref,
151       U* ptr);
152 
153   // Construction from a from a WeakPtr's internals. Will CHECK() if the WeakPtr
154   // is already invalid.
SafeRef(internal::WeakReference && ref,T * ptr)155   explicit SafeRef(internal::WeakReference&& ref, T* ptr)
156       : ref_(std::move(ref)), ptr_(ptr) {
157     CHECK(ref_.IsValid());
158   }
159 
160   internal::WeakReference ref_;
161 
162   static constexpr RawPtrTraits PtrTrait = Traits == SafeRefTraits::kEmpty
163                                                ? RawPtrTraits::kEmpty
164                                                : DanglingUntriaged;
165   // This pointer is only valid when ref_.is_valid() is true.  Otherwise, its
166   // value is undefined (as opposed to nullptr). Unlike WeakPtr, this raw_ptr is
167   // not allowed to dangle.
168   raw_ptr<T, PtrTrait> ptr_;
169 };
170 
171 namespace internal {
172 template <typename T>
MakeSafeRefFromWeakPtrInternals(internal::WeakReference && ref,T * ptr)173 SafeRef<T> MakeSafeRefFromWeakPtrInternals(internal::WeakReference&& ref,
174                                            T* ptr) {
175   return SafeRef<T>(std::move(ref), ptr);
176 }
177 }  // namespace internal
178 
179 }  // namespace base
180 
181 #endif  // BASE_MEMORY_SAFE_REF_H_
182