• 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_SLOT_H_
6 #define BASE_THREADING_SEQUENCE_LOCAL_STORAGE_SLOT_H_
7 
8 #include <memory>
9 #include <type_traits>
10 #include <utility>
11 
12 #include "base/base_export.h"
13 #include "base/threading/sequence_local_storage_map.h"
14 #include "third_party/abseil-cpp/absl/meta/type_traits.h"
15 
16 namespace base {
17 
18 namespace internal {
19 BASE_EXPORT int GetNextSequenceLocalStorageSlotNumber();
20 }
21 
22 // SequenceLocalStorageSlot allows arbitrary values to be stored and retrieved
23 // from a sequence. Values are deleted when the sequence is deleted.
24 //
25 // Example usage:
26 //
27 // int& GetSequenceLocalStorage()
28 //     static SequenceLocalStorageSlot<int> sls_value;
29 //     return sls_value->GetOrCreateValue();
30 // }
31 //
32 // void Read() {
33 //   int value = GetSequenceLocalStorage();
34 //   ...
35 // }
36 //
37 // void Write() {
38 //   GetSequenceLocalStorage() = 42;
39 // }
40 //
41 // void PostTasks() {
42 //   // Since Read() runs on the same sequence as Write(), it
43 //   // will read the value "42". A Read() running on a different
44 //   // sequence would not see that value.
45 //   scoped_refptr<base::SequencedTaskRunner> task_runner = ...;
46 //   task_runner->PostTask(FROM_HERE, base::BindOnce(&Write));
47 //   task_runner->PostTask(FROM_HERE, base::BindOnce(&Read));
48 // }
49 //
50 // SequenceLocalStorageSlot must be used within the scope of a
51 // ScopedSetSequenceLocalStorageMapForCurrentThread object.
52 // Note: this is true on all ThreadPool workers and on threads bound to a
53 // MessageLoop.
54 // SequenceLocalStorageSlot is implemented by either [Generic/Small]
55 // variants depending on the type. SequenceLocalStorageSlot itself
56 // doesn't support forward declared types and thus the variant
57 // [Generic/Small] needs to be specified explicitly.
58 
59 // Generic implementation for SequenceLocalStorageSlot.
60 template <typename T, typename Deleter = std::default_delete<T>>
61 class GenericSequenceLocalStorageSlot {
62  public:
GenericSequenceLocalStorageSlot()63   GenericSequenceLocalStorageSlot()
64       : slot_id_(internal::GetNextSequenceLocalStorageSlotNumber()) {}
65 
66   GenericSequenceLocalStorageSlot(const GenericSequenceLocalStorageSlot&) =
67       delete;
68   GenericSequenceLocalStorageSlot& operator=(
69       const GenericSequenceLocalStorageSlot&) = delete;
70 
71   ~GenericSequenceLocalStorageSlot() = default;
72 
73   explicit operator bool() const {
74     return internal::SequenceLocalStorageMap::GetForCurrentThread().Has(
75         slot_id_);
76   }
77 
78   // Default-constructs the value for the current sequence if not
79   // already constructed. Then, returns the value.
GetOrCreateValue()80   T& GetOrCreateValue() {
81     auto* slot =
82         internal::SequenceLocalStorageMap::GetForCurrentThread().Get(slot_id_);
83     if (!slot) {
84       return emplace();
85     }
86     return slot->external_value.value_as<T>();
87   }
88 
89   // Returns a pointer to the value for the current sequence. May be
90   // nullptr if the value was not constructed on the current sequence.
GetValuePointer()91   T* GetValuePointer() {
92     auto* value =
93         internal::SequenceLocalStorageMap::GetForCurrentThread().Get(slot_id_);
94     if (value) {
95       return std::addressof(value->external_value.value_as<T>());
96     }
97     return nullptr;
98   }
GetValuePointer()99   const T* GetValuePointer() const {
100     return const_cast<GenericSequenceLocalStorageSlot*>(this)
101         ->GetValuePointer();
102   }
103 
104   T* operator->() { return GetValuePointer(); }
105   const T* operator->() const { return GetValuePointer(); }
106 
107   T& operator*() { return *GetValuePointer(); }
108   const T& operator*() const { return *GetValuePointer(); }
109 
reset()110   void reset() {
111     internal::SequenceLocalStorageMap::GetForCurrentThread().Reset(slot_id_);
112   }
113 
114   // Constructs this slot's sequence-local value with |args...| and returns a
115   // pointer to the created object.
116   template <class... Args>
emplace(Args &&...args)117   T& emplace(Args&&... args) {
118     T* value_ptr = new T(std::forward<Args>(args)...);
119     Adopt(value_ptr);
120     return *value_ptr;
121   }
122 
123  private:
124   // Takes ownership of |value_ptr|.
Adopt(T * value_ptr)125   void Adopt(T* value_ptr) {
126     // Since SequenceLocalStorageMap needs to store values of various types
127     // within the same map, the type of value_destructor_pair.value is void*
128     // (std::unique_ptr<void> is invalid). Memory is freed by calling
129     // |value_destructor_pair.destructor| in the destructor of
130     // ValueDestructorPair which is invoked when the value is overwritten by
131     // another call to SequenceLocalStorageMap::Set or when the
132     // SequenceLocalStorageMap is deleted.
133     internal::SequenceLocalStorageMap::ExternalValue value;
134     value.emplace(value_ptr);
135     internal::SequenceLocalStorageMap::ValueDestructorPair
136         value_destructor_pair(
137             std::move(value),
138             internal::SequenceLocalStorageMap::MakeExternalDestructor<
139                 T, Deleter>());
140 
141     internal::SequenceLocalStorageMap::GetForCurrentThread().Set(
142         slot_id_, std::move(value_destructor_pair));
143   }
144 
145   // |slot_id_| is used as a key in SequenceLocalStorageMap
146   const int slot_id_;
147 };
148 
149 // Implementation for SequenceLocalStorageSlot optimized for small and trivial
150 // objects.
151 template <class T>
152 class SmallSequenceLocalStorageSlot {
153  public:
SmallSequenceLocalStorageSlot()154   SmallSequenceLocalStorageSlot()
155       : slot_id_(internal::GetNextSequenceLocalStorageSlotNumber()) {}
156 
157   SmallSequenceLocalStorageSlot(const SmallSequenceLocalStorageSlot&) = delete;
158   SmallSequenceLocalStorageSlot& operator=(
159       const SmallSequenceLocalStorageSlot&) = delete;
160 
161   ~SmallSequenceLocalStorageSlot() = default;
162 
163   explicit operator bool() const {
164     return internal::SequenceLocalStorageMap::GetForCurrentThread().Has(
165         slot_id_);
166   }
167 
168   // Default-constructs the value for the current sequence if not
169   // already constructed. Then, returns the value.
GetOrCreateValue()170   T& GetOrCreateValue() {
171     auto* slot =
172         internal::SequenceLocalStorageMap::GetForCurrentThread().Get(slot_id_);
173     if (!slot) {
174       return emplace();
175     }
176     return slot->inline_value.value_as<T>();
177   }
178 
179   // Returns a pointer to the value for the current sequence. May be
180   // nullptr if the value was not constructed on the current sequence.
GetValuePointer()181   T* GetValuePointer() {
182     auto* slot =
183         internal::SequenceLocalStorageMap::GetForCurrentThread().Get(slot_id_);
184     if (!slot) {
185       return nullptr;
186     }
187     return &slot->inline_value.value_as<T>();
188   }
GetValuePointer()189   const T* GetValuePointer() const {
190     return const_cast<SmallSequenceLocalStorageSlot*>(this)->GetValuePointer();
191   }
192 
193   T* operator->() { return GetValuePointer(); }
194   const T* operator->() const { return GetValuePointer(); }
195 
196   T& operator*() { return *GetValuePointer(); }
197   const T& operator*() const { return *GetValuePointer(); }
198 
reset()199   void reset() {
200     internal::SequenceLocalStorageMap::GetForCurrentThread().Reset(slot_id_);
201   }
202 
203   // Constructs this slot's sequence-local value with |args...| and returns a
204   // pointer to the created object.
205   template <class... Args>
emplace(Args &&...args)206   T& emplace(Args&&... args) {
207     internal::SequenceLocalStorageMap::InlineValue value;
208     value.emplace<T>(std::forward<Args>(args)...);
209     internal::SequenceLocalStorageMap::ValueDestructorPair
210         value_destructor_pair(
211             std::move(value),
212             internal::SequenceLocalStorageMap::MakeInlineDestructor<T>());
213 
214     return internal::SequenceLocalStorageMap::GetForCurrentThread()
215         .Set(slot_id_, std::move(value_destructor_pair))
216         ->inline_value.value_as<T>();
217   }
218 
219  private:
220   // |slot_id_| is used as a key in SequenceLocalStorageMap
221   const int slot_id_;
222 };
223 
224 template <typename T,
225           typename Deleter = std::default_delete<T>,
226           bool IsSmall =
227               sizeof(T) <= sizeof(void*) && absl::is_trivially_relocatable<T>()>
228 struct SequenceLocalStorageSlot;
229 
230 template <typename T, typename Deleter>
231 struct SequenceLocalStorageSlot<T, Deleter, false>
232     : GenericSequenceLocalStorageSlot<T, Deleter> {};
233 
234 template <typename T>
235 struct SequenceLocalStorageSlot<T, std::default_delete<T>, true>
236     : SmallSequenceLocalStorageSlot<T> {};
237 
238 }  // namespace base
239 #endif  // BASE_THREADING_SEQUENCE_LOCAL_STORAGE_SLOT_H_
240