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