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