xref: /aosp_15_r20/external/cronet/base/threading/sequence_local_storage_map.h (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2017 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_THREADING_SEQUENCE_LOCAL_STORAGE_MAP_H_
6 #define BASE_THREADING_SEQUENCE_LOCAL_STORAGE_MAP_H_
7 
8 #include "base/auto_reset.h"
9 #include "base/base_export.h"
10 #include "base/containers/flat_map.h"
11 #include "base/memory/raw_ptr_exclusion.h"
12 #include "third_party/abseil-cpp/absl/meta/type_traits.h"
13 
14 namespace base {
15 namespace internal {
16 
17 // A SequenceLocalStorageMap holds (slot_id) -> (value, destructor) items for a
18 // sequence. When a task runs, it is expected that a pointer to its sequence's
19 // SequenceLocalStorageMap is set in TLS using
20 // ScopedSetSequenceLocalStorageMapForCurrentThread. When a
21 // SequenceLocalStorageMap is destroyed, it invokes the destructors associated
22 // with values stored within it.
23 // The Get() and Set() methods should not be accessed directly.
24 // Use SequenceLocalStorageSlot to Get() and Set() values in the current
25 // sequence's SequenceLocalStorageMap.
26 class BASE_EXPORT SequenceLocalStorageMap {
27  public:
28   SequenceLocalStorageMap();
29 
30   SequenceLocalStorageMap(const SequenceLocalStorageMap&) = delete;
31   SequenceLocalStorageMap& operator=(const SequenceLocalStorageMap&) = delete;
32 
33   ~SequenceLocalStorageMap();
34 
35   // Returns the SequenceLocalStorage bound to the current thread. It is invalid
36   // to call this outside the scope of a
37   // ScopedSetSequenceLocalStorageForCurrentThread.
38   static SequenceLocalStorageMap& GetForCurrentThread();
39 
40   // Indicates whether the current thread has a SequenceLocalStorageMap
41   // available and thus whether it can safely call GetForCurrentThread and
42   // dereference SequenceLocalStorageSlots.
43   static bool IsSetForCurrentThread();
44 
45   // A `Value` holds an `ExternalValue` or an `InlineValue`. `InlineValue` is
46   // most efficient, but can only be used with types that have a size and an
47   // alignment smaller than a pointer and are trivially relocatable.
48   struct BASE_EXPORT ExternalValue {
49     // `value_` is not a raw_ptr<...> for performance reasons
50     // (based on analysis of sampling profiler data and tab_search:top100:2020).
51     RAW_PTR_EXCLUSION void* value;
52 
53     template <class T>
emplaceExternalValue54     void emplace(T* ptr) {
55       value = static_cast<void*>(ptr);
56     }
57 
58     template <class T, class Deleter>
DestroyExternalValue59     void Destroy() {
60       Deleter()(std::addressof(value_as<T>()));
61     }
62 
63     template <typename T>
value_asExternalValue64     T& value_as() {
65       return *static_cast<T*>(value);
66     }
67 
68     template <typename T>
value_asExternalValue69     const T& value_as() const {
70       return *static_cast<const T*>(value);
71     }
72   };
73 
74   struct BASE_EXPORT alignas(sizeof(void*)) InlineValue {
75     // Holds a T if small.
76     char bytes[sizeof(void*)];
77 
78     template <class T, class... Args>
emplaceInlineValue79     void emplace(Args&&... args) {
80       static_assert(sizeof(T) <= sizeof(void*),
81                     "Type T is too big for storage inline.");
82       static_assert(absl::is_trivially_relocatable<T>(),
83                     "T doesn't qualify as trivially relocatable, which "
84                     "precludes it from storage inline.");
85       static_assert(std::alignment_of<T>::value <= sizeof(T),
86                     "Type T has alignment requirements that preclude its "
87                     "storage inline.");
88       new (&bytes) T(std::forward<Args>(args)...);
89     }
90 
91     template <class T>
DestroyInlineValue92     void Destroy() {
93       value_as<T>().~T();
94     }
95 
96     template <typename T>
value_asInlineValue97     T& value_as() {
98       return *reinterpret_cast<T*>(bytes);
99     }
100 
101     template <typename T>
value_asInlineValue102     const T& value_as() const {
103       return *reinterpret_cast<const T*>(bytes);
104     }
105   };
106 
107   // There's no need for a tagged union (absl::variant) since the value
108   // type is implicitly determined by T being stored.
109   union Value {
110     ExternalValue external_value;
111     InlineValue inline_value;
112   };
113 
114   using DestructorFunc = void(Value*);
115 
116   template <class T, class Deleter>
MakeExternalDestructor()117   static DestructorFunc* MakeExternalDestructor() {
118     return [](Value* value) { value->external_value.Destroy<T, Deleter>(); };
119   }
120   template <class T>
MakeInlineDestructor()121   static DestructorFunc* MakeInlineDestructor() {
122     return [](Value* value) { value->inline_value.Destroy<T>(); };
123   }
124 
125   // Holds a value alongside its destructor. Calls the destructor on the
126   // value upon destruction.
127   class BASE_EXPORT ValueDestructorPair {
128    public:
129     ValueDestructorPair();
130     ValueDestructorPair(ExternalValue value, DestructorFunc* destructor);
131     ValueDestructorPair(InlineValue value, DestructorFunc* destructor);
132 
133     ValueDestructorPair(const ValueDestructorPair&) = delete;
134     ValueDestructorPair& operator=(const ValueDestructorPair&) = delete;
135 
136     ~ValueDestructorPair();
137 
138     ValueDestructorPair(ValueDestructorPair&& value_destructor_pair);
139 
140     ValueDestructorPair& operator=(ValueDestructorPair&& value_destructor_pair);
141 
142     explicit operator bool() const;
143 
get()144     Value* get() { return destructor_ != nullptr ? &value_ : nullptr; }
get()145     const Value* get() const {
146       return destructor_ != nullptr ? &value_ : nullptr;
147     }
148 
149     Value* operator->() { return get(); }
150     const Value* operator->() const { return get(); }
151 
152    private:
153     Value value_;
154     // `destructor_` is not a raw_ptr<...> for performance reasons
155     // (based on analysis of sampling profiler data and tab_search:top100:2020).
156     RAW_PTR_EXCLUSION DestructorFunc* destructor_;
157   };
158 
159   // Returns true if a value is stored in |slot_id|.
160   bool Has(int slot_id) const;
161 
162   // Resets the value stored in |slot_id|.
163   void Reset(int slot_id);
164 
165   // Returns the value stored in |slot_id| or nullptr if no value was stored.
166   Value* Get(int slot_id);
167 
168   // Stores |value_destructor_pair| in |slot_id|. Overwrites and destroys any
169   // previously stored value.
170   Value* Set(int slot_id, ValueDestructorPair value_destructor_pair);
171 
172  private:
173   // Map from slot id to ValueDestructorPair.
174   // flat_map was chosen because there are expected to be relatively few entries
175   // in the map. For low number of entries, flat_map is known to perform better
176   // than other map implementations.
177   base::flat_map<int, ValueDestructorPair> sls_map_;
178 };
179 
180 // Within the scope of this object,
181 // SequenceLocalStorageMap::GetForCurrentThread() will return a reference to the
182 // SequenceLocalStorageMap object passed to the constructor. There can be only
183 // one ScopedSetSequenceLocalStorageMapForCurrentThread instance per scope.
184 class BASE_EXPORT
185     [[maybe_unused,
186       nodiscard]] ScopedSetSequenceLocalStorageMapForCurrentThread {
187  public:
188   ScopedSetSequenceLocalStorageMapForCurrentThread(
189       SequenceLocalStorageMap* sequence_local_storage);
190 
191   ScopedSetSequenceLocalStorageMapForCurrentThread(
192       const ScopedSetSequenceLocalStorageMapForCurrentThread&) = delete;
193   ScopedSetSequenceLocalStorageMapForCurrentThread& operator=(
194       const ScopedSetSequenceLocalStorageMapForCurrentThread&) = delete;
195 
196   ~ScopedSetSequenceLocalStorageMapForCurrentThread();
197 
198  private:
199   const base::AutoReset<SequenceLocalStorageMap*> resetter_;
200 };
201 }  // namespace internal
202 }  // namespace base
203 
204 #endif  // BASE_THREADING_SEQUENCE_LOCAL_STORAGE_MAP_H_
205