summaryrefslogtreecommitdiff
path: root/base/threading/sequence_local_storage_map_unittest.cc
blob: 1b507cabdebe31646f2913a2f8137e65e7dfeeb3 (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
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
// 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_map.h"

#include <memory>
#include <utility>

#include "base/compiler_specific.h"
#include "base/memory/raw_ptr.h"
#include "partition_alloc/partition_alloc_buildflags.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace base {
namespace internal {

namespace {

constexpr int kSlotId = 1;

class TRIVIAL_ABI SetOnDestroy {
 public:
  SetOnDestroy(bool* was_destroyed_ptr)
      : was_destroyed_ptr_(was_destroyed_ptr) {
    DCHECK(was_destroyed_ptr_);
    DCHECK(!(*was_destroyed_ptr_));
  }

  SetOnDestroy(const SetOnDestroy&) = delete;
  SetOnDestroy& operator=(const SetOnDestroy&) = delete;

  SetOnDestroy(SetOnDestroy&& other) {
    using std::swap;
    swap(was_destroyed_ptr_, other.was_destroyed_ptr_);
  }
  SetOnDestroy& operator=(SetOnDestroy&& other) {
    using std::swap;
    swap(was_destroyed_ptr_, other.was_destroyed_ptr_);
    return *this;
  }

  ~SetOnDestroy() {
    if (!was_destroyed_ptr_) {
      return;
    }
    DCHECK(!(*was_destroyed_ptr_));
    *was_destroyed_ptr_ = true;
  }

 private:
#if BUILDFLAG(ENABLE_BACKUP_REF_PTR_INSTANCE_TRACER)
  // In instance tracer mode, raw_ptr is larger than a void*, but values stored
  // inline in a SequenceLocalStorageMap must be at most sizeof(void*).
  RAW_PTR_EXCLUSION bool* was_destroyed_ptr_;
#else
  raw_ptr<bool> was_destroyed_ptr_;
#endif
};

template <typename T, typename... Args>
SequenceLocalStorageMap::ValueDestructorPair CreateExternalValueDestructorPair(
    Args... args) {
  internal::SequenceLocalStorageMap::ExternalValue value;
  value.emplace(new T(args...));
  auto* destructor =
      SequenceLocalStorageMap::MakeExternalDestructor<T,
                                                      std::default_delete<T>>();

  SequenceLocalStorageMap::ValueDestructorPair value_destructor_pair{
      std::move(value), destructor};

  return value_destructor_pair;
}

template <typename T, typename... Args>
SequenceLocalStorageMap::ValueDestructorPair CreateInlineValueDestructorPair(
    Args... args) {
  internal::SequenceLocalStorageMap::InlineValue value;
  value.emplace<T>(args...);
  auto* destructor = SequenceLocalStorageMap::MakeInlineDestructor<T>();

  SequenceLocalStorageMap::ValueDestructorPair value_destructor_pair{
      std::move(value), destructor};

  return value_destructor_pair;
}

}  // namespace

// Verify that setting a value in the SequenceLocalStorageMap, then getting
// it will yield the same value.
TEST(SequenceLocalStorageMapTest, SetGetExternal) {
  SequenceLocalStorageMap sequence_local_storage_map;
  ScopedSetSequenceLocalStorageMapForCurrentThread
      scoped_sequence_local_storage_map(&sequence_local_storage_map);

  SequenceLocalStorageMap::ValueDestructorPair value_destructor_pair =
      CreateExternalValueDestructorPair<int>(5);

  sequence_local_storage_map.Set(kSlotId, std::move(value_destructor_pair));

  EXPECT_EQ(
      sequence_local_storage_map.Get(kSlotId)->external_value.value_as<int>(),
      5);
}

TEST(SequenceLocalStorageMapTest, SetGetInline) {
  SequenceLocalStorageMap sequence_local_storage_map;
  ScopedSetSequenceLocalStorageMapForCurrentThread
      scoped_sequence_local_storage_map(&sequence_local_storage_map);

  SequenceLocalStorageMap::ValueDestructorPair value_destructor_pair =
      CreateInlineValueDestructorPair<int>(5);

  sequence_local_storage_map.Set(kSlotId, std::move(value_destructor_pair));

  EXPECT_EQ(
      sequence_local_storage_map.Get(kSlotId)->inline_value.value_as<int>(), 5);
}

// Verify that the destructor is called on a value stored in the
// SequenceLocalStorageMap when SequenceLocalStorageMap is destroyed.
TEST(SequenceLocalStorageMapTest, DestructorExternal) {
  bool set_on_destruction = false;

  {
    SequenceLocalStorageMap sequence_local_storage_map;
    ScopedSetSequenceLocalStorageMapForCurrentThread
        scoped_sequence_local_storage_map(&sequence_local_storage_map);

    SequenceLocalStorageMap::ValueDestructorPair value_destructor_pair =
        CreateExternalValueDestructorPair<SetOnDestroy>(&set_on_destruction);

    sequence_local_storage_map.Set(kSlotId, std::move(value_destructor_pair));
  }

  EXPECT_TRUE(set_on_destruction);
}

// Verify that overwriting a value already in the SequenceLocalStorageMap
// calls value's destructor.
TEST(SequenceLocalStorageMapTest, DestructorCalledOnSetOverwriteExternal) {
  bool set_on_destruction = false;
  bool set_on_destruction2 = false;
  {
    SequenceLocalStorageMap sequence_local_storage_map;
    ScopedSetSequenceLocalStorageMapForCurrentThread
        scoped_sequence_local_storage_map(&sequence_local_storage_map);

    SequenceLocalStorageMap::ValueDestructorPair value_destructor_pair =
        CreateExternalValueDestructorPair<SetOnDestroy>(&set_on_destruction);
    SequenceLocalStorageMap::ValueDestructorPair value_destructor_pair2 =
        CreateExternalValueDestructorPair<SetOnDestroy>(&set_on_destruction2);

    sequence_local_storage_map.Set(kSlotId, std::move(value_destructor_pair));

    ASSERT_FALSE(set_on_destruction);

    // Overwrites the old value in the slot.
    sequence_local_storage_map.Set(kSlotId, std::move(value_destructor_pair2));

    // Destructor should've been called for the old value in the slot, and not
    // yet called for the new value.
    EXPECT_TRUE(set_on_destruction);
    EXPECT_FALSE(set_on_destruction2);
  }
  EXPECT_TRUE(set_on_destruction2);
}

TEST(SequenceLocalStorageMapTest, DestructorInline) {
  if constexpr (!absl::is_trivially_relocatable<SetOnDestroy>()) {
    // Test disabled because there is no reliable way to detect SetOnDestroy
    // is trivially relocatble.
    // See https://github.com/llvm/llvm-project/issues/69394
    GTEST_SKIP();
  } else {
    bool set_on_destruction = false;

    {
      SequenceLocalStorageMap sequence_local_storage_map;
      ScopedSetSequenceLocalStorageMapForCurrentThread
          scoped_sequence_local_storage_map(&sequence_local_storage_map);

      SequenceLocalStorageMap::ValueDestructorPair value_destructor_pair =
          CreateInlineValueDestructorPair<SetOnDestroy>(&set_on_destruction);

      sequence_local_storage_map.Set(kSlotId, std::move(value_destructor_pair));
    }

    EXPECT_TRUE(set_on_destruction);
  }
}

TEST(SequenceLocalStorageMapTest, DestructorCalledOnSetOverwriteInline) {
  if constexpr (!absl::is_trivially_relocatable<SetOnDestroy>()) {
    // Test disabled because there is no reliable way to detect SetOnDestroy
    // is trivially relocatble.
    // See https://github.com/llvm/llvm-project/issues/69394
    GTEST_SKIP();
  } else {
    bool set_on_destruction = false;
    bool set_on_destruction2 = false;
    {
      SequenceLocalStorageMap sequence_local_storage_map;
      ScopedSetSequenceLocalStorageMapForCurrentThread
          scoped_sequence_local_storage_map(&sequence_local_storage_map);

      SequenceLocalStorageMap::ValueDestructorPair value_destructor_pair =
          CreateInlineValueDestructorPair<SetOnDestroy>(&set_on_destruction);
      SequenceLocalStorageMap::ValueDestructorPair value_destructor_pair2 =
          CreateInlineValueDestructorPair<SetOnDestroy>(&set_on_destruction2);

      sequence_local_storage_map.Set(kSlotId, std::move(value_destructor_pair));

      ASSERT_FALSE(set_on_destruction);

      // Overwrites the old value in the slot.
      sequence_local_storage_map.Set(kSlotId,
                                     std::move(value_destructor_pair2));

      // Destructor should've been called for the old value in the slot, and not
      // yet called for the new value.
      EXPECT_TRUE(set_on_destruction);
      EXPECT_FALSE(set_on_destruction2);
    }
    EXPECT_TRUE(set_on_destruction2);
  }
}

}  // namespace internal
}  // namespace base