• 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 #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