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 #include "base/threading/sequence_local_storage_map.h"
6
7 #include <memory>
8 #include <utility>
9
10 #include "base/compiler_specific.h"
11 #include "base/memory/raw_ptr.h"
12 #include "partition_alloc/buildflags.h"
13 #include "testing/gtest/include/gtest/gtest.h"
14
15 namespace base {
16 namespace internal {
17
18 namespace {
19
20 constexpr int kSlotId = 1;
21
22 class TRIVIAL_ABI SetOnDestroy {
23 public:
SetOnDestroy(bool * was_destroyed_ptr)24 SetOnDestroy(bool* was_destroyed_ptr)
25 : was_destroyed_ptr_(was_destroyed_ptr) {
26 DCHECK(was_destroyed_ptr_);
27 DCHECK(!(*was_destroyed_ptr_));
28 }
29
30 SetOnDestroy(const SetOnDestroy&) = delete;
31 SetOnDestroy& operator=(const SetOnDestroy&) = delete;
32
SetOnDestroy(SetOnDestroy && other)33 SetOnDestroy(SetOnDestroy&& other) {
34 using std::swap;
35 swap(was_destroyed_ptr_, other.was_destroyed_ptr_);
36 }
operator =(SetOnDestroy && other)37 SetOnDestroy& operator=(SetOnDestroy&& other) {
38 using std::swap;
39 swap(was_destroyed_ptr_, other.was_destroyed_ptr_);
40 return *this;
41 }
42
~SetOnDestroy()43 ~SetOnDestroy() {
44 if (!was_destroyed_ptr_) {
45 return;
46 }
47 DCHECK(!(*was_destroyed_ptr_));
48 *was_destroyed_ptr_ = true;
49 }
50
51 private:
52 #if PA_BUILDFLAG(ENABLE_BACKUP_REF_PTR_INSTANCE_TRACER)
53 // In instance tracer mode, raw_ptr is larger than a void*, but values stored
54 // inline in a SequenceLocalStorageMap must be at most sizeof(void*).
55 RAW_PTR_EXCLUSION bool* was_destroyed_ptr_;
56 #else
57 raw_ptr<bool> was_destroyed_ptr_;
58 #endif
59 };
60
61 template <typename T, typename... Args>
CreateExternalValueDestructorPair(Args...args)62 SequenceLocalStorageMap::ValueDestructorPair CreateExternalValueDestructorPair(
63 Args... args) {
64 internal::SequenceLocalStorageMap::ExternalValue value;
65 value.emplace(new T(args...));
66 auto* destructor =
67 SequenceLocalStorageMap::MakeExternalDestructor<T,
68 std::default_delete<T>>();
69
70 SequenceLocalStorageMap::ValueDestructorPair value_destructor_pair{
71 std::move(value), destructor};
72
73 return value_destructor_pair;
74 }
75
76 template <typename T, typename... Args>
CreateInlineValueDestructorPair(Args...args)77 SequenceLocalStorageMap::ValueDestructorPair CreateInlineValueDestructorPair(
78 Args... args) {
79 internal::SequenceLocalStorageMap::InlineValue value;
80 value.emplace<T>(args...);
81 auto* destructor = SequenceLocalStorageMap::MakeInlineDestructor<T>();
82
83 SequenceLocalStorageMap::ValueDestructorPair value_destructor_pair{
84 std::move(value), destructor};
85
86 return value_destructor_pair;
87 }
88
89 } // namespace
90
91 // Verify that setting a value in the SequenceLocalStorageMap, then getting
92 // it will yield the same value.
TEST(SequenceLocalStorageMapTest,SetGetExternal)93 TEST(SequenceLocalStorageMapTest, SetGetExternal) {
94 SequenceLocalStorageMap sequence_local_storage_map;
95 ScopedSetSequenceLocalStorageMapForCurrentThread
96 scoped_sequence_local_storage_map(&sequence_local_storage_map);
97
98 SequenceLocalStorageMap::ValueDestructorPair value_destructor_pair =
99 CreateExternalValueDestructorPair<int>(5);
100
101 sequence_local_storage_map.Set(kSlotId, std::move(value_destructor_pair));
102
103 EXPECT_EQ(
104 sequence_local_storage_map.Get(kSlotId)->external_value.value_as<int>(),
105 5);
106 }
107
TEST(SequenceLocalStorageMapTest,SetGetInline)108 TEST(SequenceLocalStorageMapTest, SetGetInline) {
109 SequenceLocalStorageMap sequence_local_storage_map;
110 ScopedSetSequenceLocalStorageMapForCurrentThread
111 scoped_sequence_local_storage_map(&sequence_local_storage_map);
112
113 SequenceLocalStorageMap::ValueDestructorPair value_destructor_pair =
114 CreateInlineValueDestructorPair<int>(5);
115
116 sequence_local_storage_map.Set(kSlotId, std::move(value_destructor_pair));
117
118 EXPECT_EQ(
119 sequence_local_storage_map.Get(kSlotId)->inline_value.value_as<int>(), 5);
120 }
121
122 // Verify that the destructor is called on a value stored in the
123 // SequenceLocalStorageMap when SequenceLocalStorageMap is destroyed.
TEST(SequenceLocalStorageMapTest,DestructorExternal)124 TEST(SequenceLocalStorageMapTest, DestructorExternal) {
125 bool set_on_destruction = false;
126
127 {
128 SequenceLocalStorageMap sequence_local_storage_map;
129 ScopedSetSequenceLocalStorageMapForCurrentThread
130 scoped_sequence_local_storage_map(&sequence_local_storage_map);
131
132 SequenceLocalStorageMap::ValueDestructorPair value_destructor_pair =
133 CreateExternalValueDestructorPair<SetOnDestroy>(&set_on_destruction);
134
135 sequence_local_storage_map.Set(kSlotId, std::move(value_destructor_pair));
136 }
137
138 EXPECT_TRUE(set_on_destruction);
139 }
140
141 // Verify that overwriting a value already in the SequenceLocalStorageMap
142 // calls value's destructor.
TEST(SequenceLocalStorageMapTest,DestructorCalledOnSetOverwriteExternal)143 TEST(SequenceLocalStorageMapTest, DestructorCalledOnSetOverwriteExternal) {
144 bool set_on_destruction = false;
145 bool set_on_destruction2 = false;
146 {
147 SequenceLocalStorageMap sequence_local_storage_map;
148 ScopedSetSequenceLocalStorageMapForCurrentThread
149 scoped_sequence_local_storage_map(&sequence_local_storage_map);
150
151 SequenceLocalStorageMap::ValueDestructorPair value_destructor_pair =
152 CreateExternalValueDestructorPair<SetOnDestroy>(&set_on_destruction);
153 SequenceLocalStorageMap::ValueDestructorPair value_destructor_pair2 =
154 CreateExternalValueDestructorPair<SetOnDestroy>(&set_on_destruction2);
155
156 sequence_local_storage_map.Set(kSlotId, std::move(value_destructor_pair));
157
158 ASSERT_FALSE(set_on_destruction);
159
160 // Overwrites the old value in the slot.
161 sequence_local_storage_map.Set(kSlotId, std::move(value_destructor_pair2));
162
163 // Destructor should've been called for the old value in the slot, and not
164 // yet called for the new value.
165 EXPECT_TRUE(set_on_destruction);
166 EXPECT_FALSE(set_on_destruction2);
167 }
168 EXPECT_TRUE(set_on_destruction2);
169 }
170
171 template <typename T>
DestructorInline()172 void DestructorInline() {
173 if constexpr (!absl::is_trivially_relocatable<T>()) {
174 // Test disabled because there is no reliable way to detect SetOnDestroy
175 // is trivially relocatble.
176 // See https://github.com/llvm/llvm-project/issues/69394
177 GTEST_SKIP();
178 } else {
179 bool set_on_destruction = false;
180
181 {
182 SequenceLocalStorageMap sequence_local_storage_map;
183 ScopedSetSequenceLocalStorageMapForCurrentThread
184 scoped_sequence_local_storage_map(&sequence_local_storage_map);
185
186 SequenceLocalStorageMap::ValueDestructorPair value_destructor_pair =
187 CreateInlineValueDestructorPair<T>(&set_on_destruction);
188
189 sequence_local_storage_map.Set(kSlotId, std::move(value_destructor_pair));
190 }
191
192 EXPECT_TRUE(set_on_destruction);
193 }
194 }
TEST(SequenceLocalStorageMapTest,DestructorInline)195 TEST(SequenceLocalStorageMapTest, DestructorInline) {
196 DestructorInline<SetOnDestroy>();
197 }
198
199 template <typename T>
DestructorCalledOnSetOverwriteInline()200 void DestructorCalledOnSetOverwriteInline() {
201 if constexpr (!absl::is_trivially_relocatable<T>()) {
202 // Test disabled because there is no reliable way to detect SetOnDestroy
203 // is trivially relocatble.
204 // See https://github.com/llvm/llvm-project/issues/69394
205 GTEST_SKIP();
206 } else {
207 bool set_on_destruction = false;
208 bool set_on_destruction2 = false;
209 {
210 SequenceLocalStorageMap sequence_local_storage_map;
211 ScopedSetSequenceLocalStorageMapForCurrentThread
212 scoped_sequence_local_storage_map(&sequence_local_storage_map);
213
214 SequenceLocalStorageMap::ValueDestructorPair value_destructor_pair =
215 CreateInlineValueDestructorPair<T>(&set_on_destruction);
216 SequenceLocalStorageMap::ValueDestructorPair value_destructor_pair2 =
217 CreateInlineValueDestructorPair<T>(&set_on_destruction2);
218
219 sequence_local_storage_map.Set(kSlotId, std::move(value_destructor_pair));
220
221 ASSERT_FALSE(set_on_destruction);
222
223 // Overwrites the old value in the slot.
224 sequence_local_storage_map.Set(kSlotId,
225 std::move(value_destructor_pair2));
226
227 // Destructor should've been called for the old value in the slot, and not
228 // yet called for the new value.
229 EXPECT_TRUE(set_on_destruction);
230 EXPECT_FALSE(set_on_destruction2);
231 }
232 EXPECT_TRUE(set_on_destruction2);
233 }
234 }
TEST(SequenceLocalStorageMapTest,DestructorCalledOnSetOverwriteInline)235 TEST(SequenceLocalStorageMapTest, DestructorCalledOnSetOverwriteInline) {
236 DestructorCalledOnSetOverwriteInline<SetOnDestroy>();
237 }
238
239 } // namespace internal
240 } // namespace base
241