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