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