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