• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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