blob: 31dd559787448a9236484d4669a33640750dde17 [file] [log] [blame]
Cronet Mainline Eng14c90642023-03-20 09:24:50 -08001// Copyright 2014 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_SCOPED_GENERIC_H_
6#define BASE_SCOPED_GENERIC_H_
7
8#include <stdlib.h>
9
Chidera Olibie181bb752024-04-02 12:07:04 +000010#include <concepts>
Cronet Mainline Eng14c90642023-03-20 09:24:50 -080011#include <type_traits>
12
13#include "base/check.h"
14#include "base/memory/raw_ptr.h"
15
16namespace base {
17
18// This class acts like unique_ptr with a custom deleter (although is slightly
19// less fancy in some of the more escoteric respects) except that it keeps a
20// copy of the object rather than a pointer, and we require that the contained
21// object has some kind of "invalid" value.
22//
23// Defining a scoper based on this class allows you to get a scoper for
24// non-pointer types without having to write custom code for set, reset, and
25// move, etc. and get almost identical semantics that people are used to from
26// unique_ptr.
27//
28// It is intended that you will typedef this class with an appropriate deleter
29// to implement clean up tasks for objects that act like pointers from a
30// resource management standpoint but aren't, such as file descriptors and
31// various types of operating system handles. Using unique_ptr for these
32// things requires that you keep a pointer to the handle valid for the lifetime
33// of the scoper (which is easy to mess up).
34//
35// For an object to be able to be put into a ScopedGeneric, it must support
36// standard copyable semantics and have a specific "invalid" value. The traits
37// must define a free function and also the invalid value to assign for
38// default-constructed and released objects.
39//
40// struct FooScopedTraits {
41// // It's assumed that this is a fast inline function with little-to-no
42// // penalty for duplicate calls. This must be a static function even
43// // for stateful traits.
44// static int InvalidValue() {
45// return 0;
46// }
47//
48// // This free function will not be called if f == InvalidValue()!
49// static void Free(int f) {
50// ::FreeFoo(f);
51// }
52// };
53//
54// using ScopedFoo = ScopedGeneric<int, FooScopedTraits>;
55//
56// A Traits type may choose to track ownership of objects in parallel with
57// ScopedGeneric. To do so, it must implement the Acquire and Release methods,
58// which will be called by ScopedGeneric during ownership transfers and extend
59// the ScopedGenericOwnershipTracking tag type.
60//
61// struct BarScopedTraits : public ScopedGenericOwnershipTracking {
62// using ScopedGenericType = ScopedGeneric<int, BarScopedTraits>;
63// static int InvalidValue() {
64// return 0;
65// }
66//
67// static void Free(int b) {
68// ::FreeBar(b);
69// }
70//
71// static void Acquire(const ScopedGenericType& owner, int b) {
72// ::TrackAcquisition(b, owner);
73// }
74//
75// static void Release(const ScopedGenericType& owner, int b) {
76// ::TrackRelease(b, owner);
77// }
78// };
79//
80// using ScopedBar = ScopedGeneric<int, BarScopedTraits>;
81struct ScopedGenericOwnershipTracking {};
82
83template<typename T, typename Traits>
84class ScopedGeneric {
85 private:
86 // This must be first since it's used inline below.
87 //
88 // Use the empty base class optimization to allow us to have a D
89 // member, while avoiding any space overhead for it when D is an
90 // empty class. See e.g. http://www.cantrip.org/emptyopt.html for a good
91 // discussion of this technique.
92 struct Data : public Traits {
93 explicit Data(const T& in) : generic(in) {}
94 Data(const T& in, const Traits& other) : Traits(other), generic(in) {}
95 T generic;
96 };
97
98 public:
99 typedef T element_type;
100 typedef Traits traits_type;
101
102 ScopedGeneric() : data_(traits_type::InvalidValue()) {}
103
104 // Constructor. Takes responsibility for freeing the resource associated with
105 // the object T.
106 explicit ScopedGeneric(const element_type& value) : data_(value) {
107 TrackAcquire(data_.generic);
108 }
109
110 // Constructor. Allows initialization of a stateful traits object.
111 ScopedGeneric(const element_type& value, const traits_type& traits)
112 : data_(value, traits) {
113 TrackAcquire(data_.generic);
114 }
115
116 // Move constructor. Allows initialization from a ScopedGeneric rvalue.
117 ScopedGeneric(ScopedGeneric<T, Traits>&& rvalue)
118 : data_(rvalue.release(), rvalue.get_traits()) {
119 TrackAcquire(data_.generic);
120 }
121 ScopedGeneric(const ScopedGeneric&) = delete;
122 ScopedGeneric& operator=(const ScopedGeneric&) = delete;
123
124 virtual ~ScopedGeneric() {
125 CHECK(!receiving_); // ScopedGeneric destroyed with active receiver.
126 FreeIfNecessary();
127 }
128
129 // operator=. Allows assignment from a ScopedGeneric rvalue.
130 ScopedGeneric& operator=(ScopedGeneric<T, Traits>&& rvalue) {
131 reset(rvalue.release());
132 return *this;
133 }
134
135 // Frees the currently owned object, if any. Then takes ownership of a new
136 // object, if given. Self-resets are not allowd as on unique_ptr. See
137 // http://crbug.com/162971
138 void reset(const element_type& value = traits_type::InvalidValue()) {
139 if (data_.generic != traits_type::InvalidValue() && data_.generic == value)
140 abort();
141 FreeIfNecessary();
142 data_.generic = value;
143 TrackAcquire(value);
144 }
145
146 // Release the object. The return value is the current object held by this
147 // object. After this operation, this object will hold a null value, and
148 // will not own the object any more.
149 [[nodiscard]] element_type release() {
150 element_type old_generic = data_.generic;
151 data_.generic = traits_type::InvalidValue();
152 TrackRelease(old_generic);
153 return old_generic;
154 }
155
156 // A helper class that provides a T* that can be used to take ownership of
157 // a value returned from a function via out-parameter. When the Receiver is
158 // destructed (which should usually be at the end of the statement in which
159 // receive is called), ScopedGeneric::reset() will be called with the
160 // Receiver's value.
161 //
162 // In the simple case of a function that assigns the value before it returns,
163 // C++'s lifetime extension can be used as follows:
164 //
165 // ScopedFoo foo;
166 // bool result = GetFoo(ScopedFoo::Receiver(foo).get());
167 //
168 // Note that the lifetime of the Receiver is extended until the semicolon,
169 // and ScopedGeneric is assigned the value upon destruction of the Receiver,
170 // so the following code would not work:
171 //
172 // // BROKEN!
173 // ScopedFoo foo;
174 // UseFoo(&foo, GetFoo(ScopedFoo::Receiver(foo).get()));
175 //
176 // In more complicated scenarios, you may need to provide an explicit scope
177 // for the Receiver, as in the following:
178 //
179 // std::vector<ScopedFoo> foos(64);
180 //
181 // {
182 // std::vector<ScopedFoo::Receiver> foo_receivers;
183 // for (auto foo : foos) {
184 // foo_receivers_.emplace_back(foo);
185 // }
186 // for (auto receiver : foo_receivers) {
187 // SubmitGetFooRequest(receiver.get());
188 // }
189 // WaitForFooRequests();
190 // }
191 // UseFoos(foos);
192 class Receiver {
193 public:
194 explicit Receiver(ScopedGeneric& parent) : scoped_generic_(&parent) {
195 // Check if we attempted to construct a Receiver for ScopedGeneric with an
196 // existing Receiver.
197 CHECK(!scoped_generic_->receiving_);
198 scoped_generic_->receiving_ = true;
199 }
200 Receiver(const Receiver&) = delete;
201 Receiver& operator=(const Receiver&) = delete;
202 Receiver(Receiver&& move) {
203 CHECK(!used_); // Moving into already-used Receiver.
204 CHECK(!move.used_); // Moving from already-used Receiver.
205 scoped_generic_ = move.scoped_generic_;
206 move.scoped_generic_ = nullptr;
207 }
208
209 Receiver& operator=(Receiver&& move) {
210 CHECK(!used_); // Moving into already-used Receiver.
211 CHECK(!move.used_); // Moving from already-used Receiver.
212 scoped_generic_ = move.scoped_generic_;
213 move.scoped_generic_ = nullptr;
214 }
215 ~Receiver() {
216 if (scoped_generic_) {
217 CHECK(scoped_generic_->receiving_);
218 scoped_generic_->reset(value_);
219 scoped_generic_->receiving_ = false;
220 }
221 }
222 // We hand out a pointer to a field in Receiver instead of directly to
223 // ScopedGeneric's internal storage in order to make it so that users can't
224 // accidentally silently break ScopedGeneric's invariants. This way, an
225 // incorrect use-after-scope-exit is more detectable by ASan or static
226 // analysis tools, as the pointer is only valid for the lifetime of the
227 // Receiver, not the ScopedGeneric.
228 T* get() {
229 used_ = true;
230 return &value_;
231 }
232
233 private:
234 T value_ = Traits::InvalidValue();
235 raw_ptr<ScopedGeneric<T, Traits>> scoped_generic_;
236 bool used_ = false;
237 };
238
239 const element_type& get() const { return data_.generic; }
240
241 // Returns true if this object doesn't hold the special null value for the
242 // associated data type.
243 bool is_valid() const { return data_.generic != traits_type::InvalidValue(); }
244
245 bool operator==(const element_type& value) const {
246 return data_.generic == value;
247 }
248 bool operator!=(const element_type& value) const {
249 return data_.generic != value;
250 }
251
252 Traits& get_traits() { return data_; }
253 const Traits& get_traits() const { return data_; }
254
255 private:
256 void FreeIfNecessary() {
257 if (data_.generic != traits_type::InvalidValue()) {
258 TrackRelease(data_.generic);
259 data_.Free(data_.generic);
260 data_.generic = traits_type::InvalidValue();
261 }
262 }
263
Chidera Olibie181bb752024-04-02 12:07:04 +0000264 void TrackAcquire(const T& value) {
265 if constexpr (std::derived_from<Traits, ScopedGenericOwnershipTracking>) {
266 if (value != traits_type::InvalidValue()) {
267 data_.Acquire(static_cast<const ScopedGeneric&>(*this), value);
268 }
Cronet Mainline Eng14c90642023-03-20 09:24:50 -0800269 }
270 }
271
Chidera Olibie181bb752024-04-02 12:07:04 +0000272 void TrackRelease(const T& value) {
273 if constexpr (std::derived_from<Traits, ScopedGenericOwnershipTracking>) {
274 if (value != traits_type::InvalidValue()) {
275 data_.Release(static_cast<const ScopedGeneric&>(*this), value);
276 }
Cronet Mainline Eng14c90642023-03-20 09:24:50 -0800277 }
278 }
279
Cronet Mainline Eng14c90642023-03-20 09:24:50 -0800280 // Forbid comparison. If U != T, it totally doesn't make sense, and if U ==
281 // T, it still doesn't make sense because you should never have the same
282 // object owned by two different ScopedGenerics.
283 template <typename T2, typename Traits2> bool operator==(
284 const ScopedGeneric<T2, Traits2>& p2) const;
285 template <typename T2, typename Traits2> bool operator!=(
286 const ScopedGeneric<T2, Traits2>& p2) const;
287
288 Data data_;
289 bool receiving_ = false;
290};
291
292template<class T, class Traits>
293void swap(const ScopedGeneric<T, Traits>& a,
294 const ScopedGeneric<T, Traits>& b) {
295 a.swap(b);
296}
297
298template<class T, class Traits>
299bool operator==(const T& value, const ScopedGeneric<T, Traits>& scoped) {
300 return value == scoped.get();
301}
302
303template<class T, class Traits>
304bool operator!=(const T& value, const ScopedGeneric<T, Traits>& scoped) {
305 return value != scoped.get();
306}
307
308} // namespace base
309
310#endif // BASE_SCOPED_GENERIC_H_