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