• 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 #ifdef UNSAFE_BUFFERS_BUILD
6 // TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
7 #pragma allow_unsafe_buffers
8 #endif
9 
10 #include "base/threading/sequence_local_storage_slot.h"
11 
12 #include <utility>
13 
14 #include "base/memory/ptr_util.h"
15 #include "base/threading/sequence_local_storage_map.h"
16 #include "testing/gtest/include/gtest/gtest.h"
17 
18 namespace base {
19 
20 namespace {
21 
22 template <class T>
23 class SequenceLocalStorageSlotTest : public testing::Test {
24  public:
25   SequenceLocalStorageSlotTest(const SequenceLocalStorageSlotTest&) = delete;
26   SequenceLocalStorageSlotTest& operator=(const SequenceLocalStorageSlotTest&) =
27       delete;
28 
29  protected:
SequenceLocalStorageSlotTest()30   SequenceLocalStorageSlotTest()
31       : scoped_sequence_local_storage_(&sequence_local_storage_) {}
32 
33   internal::SequenceLocalStorageMap sequence_local_storage_;
34   internal::ScopedSetSequenceLocalStorageMapForCurrentThread
35       scoped_sequence_local_storage_;
36 };
37 
38 }  // namespace
39 
40 struct GenericSLS {
41   template <class T>
42   using Type = GenericSequenceLocalStorageSlot<T>;
43 };
44 
45 struct SmallSLS {
46   template <class T>
47   using Type = GenericSequenceLocalStorageSlot<T>;
48 };
49 
50 using StorageTypes = testing::Types<GenericSLS, SmallSLS>;
51 TYPED_TEST_SUITE(SequenceLocalStorageSlotTest, StorageTypes);
52 
53 // Verify that a value stored with emplace() can be retrieved with operator*().
TYPED_TEST(SequenceLocalStorageSlotTest,GetEmplace)54 TYPED_TEST(SequenceLocalStorageSlotTest, GetEmplace) {
55   using SLSType = typename TypeParam::template Type<int>;
56   SLSType slot;
57   slot.emplace(5);
58   EXPECT_EQ(*slot, 5);
59 }
60 
61 // Verify that inserting an object in a SequenceLocalStorageSlot creates a copy
62 // of that object independent of the original one.
TYPED_TEST(SequenceLocalStorageSlotTest,EmplaceObjectIsIndependent)63 TYPED_TEST(SequenceLocalStorageSlotTest, EmplaceObjectIsIndependent) {
64   using SLSType = typename TypeParam::template Type<bool>;
65   bool should_be_false = false;
66 
67   SLSType slot;
68 
69   slot.emplace(should_be_false);
70 
71   EXPECT_FALSE(*slot);
72   *slot = true;
73   EXPECT_TRUE(*slot);
74 
75   EXPECT_NE(should_be_false, *slot);
76 }
77 
78 // Verify that multiple slots work and that calling emplace after overwriting
79 // a value in a slot yields the new value.
TYPED_TEST(SequenceLocalStorageSlotTest,GetEmplaceMultipleSlots)80 TYPED_TEST(SequenceLocalStorageSlotTest, GetEmplaceMultipleSlots) {
81   using SLSType = typename TypeParam::template Type<int>;
82   SLSType slot1;
83   SLSType slot2;
84   SLSType slot3;
85   EXPECT_FALSE(slot1);
86   EXPECT_FALSE(slot2);
87   EXPECT_FALSE(slot3);
88 
89   slot1.emplace(1);
90   slot2.emplace(2);
91   slot3.emplace(3);
92 
93   EXPECT_TRUE(slot1);
94   EXPECT_TRUE(slot2);
95   EXPECT_TRUE(slot3);
96   EXPECT_EQ(*slot1, 1);
97   EXPECT_EQ(*slot2, 2);
98   EXPECT_EQ(*slot3, 3);
99 
100   slot3.emplace(4);
101   slot2.emplace(5);
102   slot1.emplace(6);
103 
104   EXPECT_EQ(*slot3, 4);
105   EXPECT_EQ(*slot2, 5);
106   EXPECT_EQ(*slot1, 6);
107 }
108 
109 // Verify that changing the value returned by Get() changes the value
110 // in sequence local storage.
TYPED_TEST(SequenceLocalStorageSlotTest,GetReferenceModifiable)111 TYPED_TEST(SequenceLocalStorageSlotTest, GetReferenceModifiable) {
112   using SLSType = typename TypeParam::template Type<bool>;
113   SLSType slot;
114   slot.emplace(false);
115   *slot = true;
116   EXPECT_TRUE(*slot);
117 }
118 
119 // Verify that a move-only type can be stored in sequence local storage.
TYPED_TEST(SequenceLocalStorageSlotTest,EmplaceGetWithMoveOnlyType)120 TYPED_TEST(SequenceLocalStorageSlotTest, EmplaceGetWithMoveOnlyType) {
121   struct MoveOnly {
122     MoveOnly() = default;
123     MoveOnly(const MoveOnly&) = delete;
124     MoveOnly& operator=(const MoveOnly&) = delete;
125     MoveOnly(MoveOnly&&) = default;
126     MoveOnly& operator=(MoveOnly&&) = default;
127     int x = 0x12345678;
128   };
129   using SLSType = typename TypeParam::template Type<MoveOnly>;
130   MoveOnly move_only;
131 
132   SLSType slot;
133   slot.emplace(std::move(move_only));
134 
135   EXPECT_EQ(slot->x, 0x12345678);
136 }
137 
138 // Verify that a Get() without a previous Set() on a slot returns a
139 // default-constructed value.
TYPED_TEST(SequenceLocalStorageSlotTest,GetWithoutSetDefaultConstructs)140 TYPED_TEST(SequenceLocalStorageSlotTest, GetWithoutSetDefaultConstructs) {
141   struct DefaultConstructable {
142     int x = 0x12345678;
143   };
144   using SLSType = typename TypeParam::template Type<DefaultConstructable>;
145 
146   SLSType slot;
147 
148   EXPECT_EQ(slot.GetOrCreateValue().x, 0x12345678);
149 }
150 
151 // Verify that a GetOrCreateValue() without a previous emplace() on a slot with
152 // a POD-type returns a default-constructed value.
153 // Note: this test could be flaky and give a false pass. If it's flaky, the test
154 // might've "passed" because the memory for the slot happened to be zeroed.
TYPED_TEST(SequenceLocalStorageSlotTest,GetWithoutSetDefaultConstructsPOD)155 TYPED_TEST(SequenceLocalStorageSlotTest, GetWithoutSetDefaultConstructsPOD) {
156   using SLSType = typename TypeParam::template Type<void*>;
157   SLSType slot;
158 
159   EXPECT_EQ(slot.GetOrCreateValue(), nullptr);
160 }
161 
162 // Verify that the value of a slot is specific to a SequenceLocalStorageMap
TEST(SequenceLocalStorageSlotMultipleMapTest,EmplaceGetMultipleMapsOneSlot)163 TEST(SequenceLocalStorageSlotMultipleMapTest, EmplaceGetMultipleMapsOneSlot) {
164   SequenceLocalStorageSlot<unsigned int> slot;
165   internal::SequenceLocalStorageMap sequence_local_storage_maps[5];
166 
167   // Set the value of the slot to be the index of the current
168   // SequenceLocalStorageMaps in the vector
169   for (unsigned int i = 0; i < std::size(sequence_local_storage_maps); ++i) {
170     internal::ScopedSetSequenceLocalStorageMapForCurrentThread
171         scoped_sequence_local_storage(&sequence_local_storage_maps[i]);
172 
173     slot.emplace(i);
174   }
175 
176   for (unsigned int i = 0; i < std::size(sequence_local_storage_maps); ++i) {
177     internal::ScopedSetSequenceLocalStorageMapForCurrentThread
178         scoped_sequence_local_storage(&sequence_local_storage_maps[i]);
179 
180     EXPECT_EQ(*slot, i);
181   }
182 }
183 
TEST(SequenceLocalStorageComPtrTest,TestClassesWithNoAddressOfOperatorCanCompile)184 TEST(SequenceLocalStorageComPtrTest,
185      TestClassesWithNoAddressOfOperatorCanCompile) {
186   internal::SequenceLocalStorageMap sequence_local_storage_map;
187   internal::ScopedSetSequenceLocalStorageMapForCurrentThread
188       scoped_sequence_local_storage(&sequence_local_storage_map);
189   // Microsoft::WRL::ComPtr overrides & operator to release the underlying
190   // pointer.
191   // https://learn.microsoft.com/en-us/cpp/cppcx/wrl/comptr-class?view=msvc-170#operator-ampersand
192   // Types stored in SequenceLocalStorage may override `operator&` to have
193   // additional side effects, e.g. Microsoft::WRL::ComPtr. Make sure
194   // SequenceLocalStorage does not invoke/use custom `operator&`s to avoid
195   // triggering those side effects.
196   class TestNoAddressOfOperator {
197    public:
198     TestNoAddressOfOperator() = default;
199     ~TestNoAddressOfOperator() {
200       // Define a non-trivial destructor so that SequenceLocalStorageSlot
201       // will use the external value path.
202     }
203     // See note above class definition for the reason this operator is deleted.
204     TestNoAddressOfOperator* operator&() = delete;
205   };
206   SequenceLocalStorageSlot<TestNoAddressOfOperator> slot;
207   slot.emplace(TestNoAddressOfOperator());
208   EXPECT_NE(slot.GetValuePointer(), nullptr);
209 }
210 
211 }  // namespace base
212