summaryrefslogtreecommitdiff
path: root/base/threading/sequence_local_storage_slot_unittest.cc
blob: 52c606e00cd58a81b4e7b2bbf9b6c0a3953dc4fd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/threading/sequence_local_storage_slot.h"

#include <utility>

#include "base/memory/ptr_util.h"
#include "base/threading/sequence_local_storage_map.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace base {

namespace {

template <class T>
class SequenceLocalStorageSlotTest : public testing::Test {
 public:
  SequenceLocalStorageSlotTest(const SequenceLocalStorageSlotTest&) = delete;
  SequenceLocalStorageSlotTest& operator=(const SequenceLocalStorageSlotTest&) =
      delete;

 protected:
  SequenceLocalStorageSlotTest()
      : scoped_sequence_local_storage_(&sequence_local_storage_) {}

  internal::SequenceLocalStorageMap sequence_local_storage_;
  internal::ScopedSetSequenceLocalStorageMapForCurrentThread
      scoped_sequence_local_storage_;
};

}  // namespace

struct GenericSLS {
  template <class T>
  using Type = GenericSequenceLocalStorageSlot<T>;
};

struct SmallSLS {
  template <class T>
  using Type = GenericSequenceLocalStorageSlot<T>;
};

using StorageTypes = testing::Types<GenericSLS, SmallSLS>;
TYPED_TEST_SUITE(SequenceLocalStorageSlotTest, StorageTypes);

// Verify that a value stored with emplace() can be retrieved with operator*().
TYPED_TEST(SequenceLocalStorageSlotTest, GetEmplace) {
  using SLSType = typename TypeParam::template Type<int>;
  SLSType slot;
  slot.emplace(5);
  EXPECT_EQ(*slot, 5);
}

// Verify that inserting an object in a SequenceLocalStorageSlot creates a copy
// of that object independent of the original one.
TYPED_TEST(SequenceLocalStorageSlotTest, EmplaceObjectIsIndependent) {
  using SLSType = typename TypeParam::template Type<bool>;
  bool should_be_false = false;

  SLSType slot;

  slot.emplace(should_be_false);

  EXPECT_FALSE(*slot);
  *slot = true;
  EXPECT_TRUE(*slot);

  EXPECT_NE(should_be_false, *slot);
}

// Verify that multiple slots work and that calling emplace after overwriting
// a value in a slot yields the new value.
TYPED_TEST(SequenceLocalStorageSlotTest, GetEmplaceMultipleSlots) {
  using SLSType = typename TypeParam::template Type<int>;
  SLSType slot1;
  SLSType slot2;
  SLSType slot3;
  EXPECT_FALSE(slot1);
  EXPECT_FALSE(slot2);
  EXPECT_FALSE(slot3);

  slot1.emplace(1);
  slot2.emplace(2);
  slot3.emplace(3);

  EXPECT_TRUE(slot1);
  EXPECT_TRUE(slot2);
  EXPECT_TRUE(slot3);
  EXPECT_EQ(*slot1, 1);
  EXPECT_EQ(*slot2, 2);
  EXPECT_EQ(*slot3, 3);

  slot3.emplace(4);
  slot2.emplace(5);
  slot1.emplace(6);

  EXPECT_EQ(*slot3, 4);
  EXPECT_EQ(*slot2, 5);
  EXPECT_EQ(*slot1, 6);
}

// Verify that changing the value returned by Get() changes the value
// in sequence local storage.
TYPED_TEST(SequenceLocalStorageSlotTest, GetReferenceModifiable) {
  using SLSType = typename TypeParam::template Type<bool>;
  SLSType slot;
  slot.emplace(false);
  *slot = true;
  EXPECT_TRUE(*slot);
}

// Verify that a move-only type can be stored in sequence local storage.
TYPED_TEST(SequenceLocalStorageSlotTest, EmplaceGetWithMoveOnlyType) {
  struct MoveOnly {
    MoveOnly() = default;
    MoveOnly(const MoveOnly&) = delete;
    MoveOnly& operator=(const MoveOnly&) = delete;
    MoveOnly(MoveOnly&&) = default;
    MoveOnly& operator=(MoveOnly&&) = default;
    int x = 0x12345678;
  };
  using SLSType = typename TypeParam::template Type<MoveOnly>;
  MoveOnly move_only;

  SLSType slot;
  slot.emplace(std::move(move_only));

  EXPECT_EQ(slot->x, 0x12345678);
}

// Verify that a Get() without a previous Set() on a slot returns a
// default-constructed value.
TYPED_TEST(SequenceLocalStorageSlotTest, GetWithoutSetDefaultConstructs) {
  struct DefaultConstructable {
    int x = 0x12345678;
  };
  using SLSType = typename TypeParam::template Type<DefaultConstructable>;

  SLSType slot;

  EXPECT_EQ(slot.GetOrCreateValue().x, 0x12345678);
}

// Verify that a GetOrCreateValue() without a previous emplace() on a slot with
// a POD-type returns a default-constructed value.
// Note: this test could be flaky and give a false pass. If it's flaky, the test
// might've "passed" because the memory for the slot happened to be zeroed.
TYPED_TEST(SequenceLocalStorageSlotTest, GetWithoutSetDefaultConstructsPOD) {
  using SLSType = typename TypeParam::template Type<void*>;
  SLSType slot;

  EXPECT_EQ(slot.GetOrCreateValue(), nullptr);
}

// Verify that the value of a slot is specific to a SequenceLocalStorageMap
TEST(SequenceLocalStorageSlotMultipleMapTest, EmplaceGetMultipleMapsOneSlot) {
  SequenceLocalStorageSlot<unsigned int> slot;
  internal::SequenceLocalStorageMap sequence_local_storage_maps[5];

  // Set the value of the slot to be the index of the current
  // SequenceLocalStorageMaps in the vector
  for (unsigned int i = 0; i < std::size(sequence_local_storage_maps); ++i) {
    internal::ScopedSetSequenceLocalStorageMapForCurrentThread
        scoped_sequence_local_storage(&sequence_local_storage_maps[i]);

    slot.emplace(i);
  }

  for (unsigned int i = 0; i < std::size(sequence_local_storage_maps); ++i) {
    internal::ScopedSetSequenceLocalStorageMapForCurrentThread
        scoped_sequence_local_storage(&sequence_local_storage_maps[i]);

    EXPECT_EQ(*slot, i);
  }
}

TEST(SequenceLocalStorageComPtrTest,
     TestClassesWithNoAddressOfOperatorCanCompile) {
  internal::SequenceLocalStorageMap sequence_local_storage_map;
  internal::ScopedSetSequenceLocalStorageMapForCurrentThread
      scoped_sequence_local_storage(&sequence_local_storage_map);
  // Microsoft::WRL::ComPtr overrides & operator to release the underlying
  // pointer.
  // https://learn.microsoft.com/en-us/cpp/cppcx/wrl/comptr-class?view=msvc-170#operator-ampersand
  // Types stored in SequenceLocalStorage may override `operator&` to have
  // additional side effects, e.g. Microsoft::WRL::ComPtr. Make sure
  // SequenceLocalStorage does not invoke/use custom `operator&`s to avoid
  // triggering those side effects.
  class TestNoAddressOfOperator {
   public:
    TestNoAddressOfOperator() = default;
    ~TestNoAddressOfOperator() {
      // Define a non-trivial destructor so that SequenceLocalStorageSlot
      // will use the external value path.
    }
    // See note above class definition for the reason this operator is deleted.
    TestNoAddressOfOperator* operator&() = delete;
  };
  SequenceLocalStorageSlot<TestNoAddressOfOperator> slot;
  slot.emplace(TestNoAddressOfOperator());
  EXPECT_NE(slot.GetValuePointer(), nullptr);
}

}  // namespace base