xref: /aosp_15_r20/external/cronet/base/scoped_generic.h (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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 
13 #include "base/check.h"
14 #include "base/memory/raw_ptr.h"
15 
16 namespace 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>;
81 struct ScopedGenericOwnershipTracking {};
82 
83 template<typename T, typename Traits>
84 class 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 {
DataData93     explicit Data(const T& in) : generic(in) {}
DataData94     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 
ScopedGeneric()102   ScopedGeneric() : data_(traits_type::InvalidValue()) {}
103 
104   // Constructor. Takes responsibility for freeing the resource associated with
105   // the object T.
ScopedGeneric(const element_type & value)106   explicit ScopedGeneric(const element_type& value) : data_(value) {
107     TrackAcquire(data_.generic);
108   }
109 
110   // Constructor. Allows initialization of a stateful traits object.
ScopedGeneric(const element_type & value,const traits_type & traits)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.
ScopedGeneric(ScopedGeneric<T,Traits> && 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 
~ScopedGeneric()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.
release()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:
Receiver(ScopedGeneric & parent)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;
Receiver(Receiver && move)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     }
~Receiver()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.
get()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 
get()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.
is_valid()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 
get_traits()252   Traits& get_traits() { return data_; }
get_traits()253   const Traits& get_traits() const { return data_; }
254 
255  private:
FreeIfNecessary()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 
TrackAcquire(const T & value)264   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       }
269     }
270   }
271 
TrackRelease(const T & value)272   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       }
277     }
278   }
279 
280   // 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 
292 template<class T, class Traits>
swap(const ScopedGeneric<T,Traits> & a,const ScopedGeneric<T,Traits> & b)293 void swap(const ScopedGeneric<T, Traits>& a,
294           const ScopedGeneric<T, Traits>& b) {
295   a.swap(b);
296 }
297 
298 template<class T, class Traits>
299 bool operator==(const T& value, const ScopedGeneric<T, Traits>& scoped) {
300   return value == scoped.get();
301 }
302 
303 template<class T, class Traits>
304 bool 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_
311