1 // Copyright 2022 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/memory/weak_auto_reset.h"
6
7 #include <memory>
8
9 #include "base/memory/weak_ptr.h"
10 #include "testing/gtest/include/gtest/gtest.h"
11
12 namespace base {
13
14 namespace {
15
16 template <class T>
17 class HasWeakFactory {
18 public:
19 HasWeakFactory() = default;
20 ~HasWeakFactory() = default;
21
22 // Returns a WeakAutoReset that temporarily sets value_ to `value`.
SetValueScoped(T value)23 auto SetValueScoped(T value) {
24 return WeakAutoReset(factory_.GetWeakPtr(), &HasWeakFactory::value_,
25 std::move(value));
26 }
27
set_value(T value)28 void set_value(T value) { value_ = std::move(value); }
value() const29 const T& value() const { return value_; }
30
GetWeakPtr()31 WeakPtr<HasWeakFactory> GetWeakPtr() { return factory_.GetWeakPtr(); }
32
33 private:
34 T value_ = T();
35 WeakPtrFactory<HasWeakFactory> factory_{this};
36 };
37
38 } // namespace
39
TEST(WeakAutoResetTest,DefaultConstructor)40 TEST(WeakAutoResetTest, DefaultConstructor) {
41 WeakAutoReset<HasWeakFactory<int>, int> empty;
42 }
43
TEST(WeakAutoResetTest,SingleAutoReset)44 TEST(WeakAutoResetTest, SingleAutoReset) {
45 HasWeakFactory<int> hwf;
46 {
47 WeakAutoReset reset = hwf.SetValueScoped(1);
48 EXPECT_EQ(1, hwf.value());
49 }
50 EXPECT_EQ(0, hwf.value());
51 }
52
TEST(WeakAutoResetTest,SingleAutoResetObjectDestroyed)53 TEST(WeakAutoResetTest, SingleAutoResetObjectDestroyed) {
54 auto hwf = std::make_unique<HasWeakFactory<int>>();
55 WeakAutoReset reset = hwf->SetValueScoped(1);
56 EXPECT_EQ(1, hwf->value());
57 hwf.reset();
58 // ASAN will crash here if we don't correctly detect that hwf has gone away.
59 }
60
TEST(WeakAutoResetTest,MultipleNested)61 TEST(WeakAutoResetTest, MultipleNested) {
62 HasWeakFactory<int> hwf;
63 {
64 WeakAutoReset reset = hwf.SetValueScoped(1);
65 EXPECT_EQ(1, hwf.value());
66 {
67 WeakAutoReset reset2 = hwf.SetValueScoped(2);
68 EXPECT_EQ(2, hwf.value());
69 }
70 EXPECT_EQ(1, hwf.value());
71 }
72 EXPECT_EQ(0, hwf.value());
73 }
74
TEST(WeakAutoResetTest,MultipleNestedObjectDestroyed)75 TEST(WeakAutoResetTest, MultipleNestedObjectDestroyed) {
76 auto hwf = std::make_unique<HasWeakFactory<int>>();
77 WeakAutoReset reset = hwf->SetValueScoped(1);
78 EXPECT_EQ(1, hwf->value());
79 WeakAutoReset reset2 = hwf->SetValueScoped(2);
80 EXPECT_EQ(2, hwf->value());
81 hwf.reset();
82 // ASAN will crash here if we don't correctly detect that hwf has gone away.
83 }
84
TEST(WeakAutoResetTest,MoveAssignmentTransfersOwnership)85 TEST(WeakAutoResetTest, MoveAssignmentTransfersOwnership) {
86 HasWeakFactory<int> hwf;
87 // Create an auto-reset outside of a scope.
88 WeakAutoReset reset = hwf.SetValueScoped(1);
89 {
90 WeakAutoReset<HasWeakFactory<int>, int> reset2;
91 EXPECT_EQ(1, hwf.value());
92 // Move the auto-reset to an instance inside the scope. This should not
93 // cause the value to reset.
94 reset2 = std::move(reset);
95 EXPECT_EQ(1, hwf.value());
96 }
97 // Because the active auto-reset went away with the scope, the original value
98 // should be restored.
99 EXPECT_EQ(0, hwf.value());
100 }
101
TEST(WeakAutoResetTest,MoveAssignmentResetsOldValue)102 TEST(WeakAutoResetTest, MoveAssignmentResetsOldValue) {
103 HasWeakFactory<int> hwf1;
104 HasWeakFactory<int> hwf2;
105 WeakAutoReset reset = hwf1.SetValueScoped(1);
106 WeakAutoReset reset2 = hwf2.SetValueScoped(2);
107 EXPECT_EQ(1, hwf1.value());
108 EXPECT_EQ(2, hwf2.value());
109
110 // Overwriting the first with the second should reset the first value, but not
111 // the second.
112 reset = std::move(reset2);
113 EXPECT_EQ(0, hwf1.value());
114 EXPECT_EQ(2, hwf2.value());
115
116 // Overwriting the moved value with a default value should have no effect.
117 reset2 = WeakAutoReset<HasWeakFactory<int>, int>();
118
119 // Overwriting the live auto-reset with a default value should reset the other
120 // value.
121 reset = WeakAutoReset<HasWeakFactory<int>, int>();
122 EXPECT_EQ(0, hwf1.value());
123 EXPECT_EQ(0, hwf2.value());
124 }
125
TEST(WeakAutoResetTest,MoveAssignmentToSelfIsNoOp)126 TEST(WeakAutoResetTest, MoveAssignmentToSelfIsNoOp) {
127 HasWeakFactory<int> hwf;
128 {
129 WeakAutoReset reset = hwf.SetValueScoped(1);
130 EXPECT_EQ(1, hwf.value());
131
132 // Move the auto-reset to itself. This should have no effect. We'll need to
133 // create an intermediate so that we don't get a compile error.
134 auto* const reset_ref = &reset;
135 reset = std::move(*reset_ref);
136 EXPECT_EQ(1, hwf.value());
137 }
138 // The auto-reset goes out of scope, resetting the value.
139 EXPECT_EQ(0, hwf.value());
140 }
141
TEST(WeakAutoResetTest,DeleteTargetObjectAfterMoveIsSafe)142 TEST(WeakAutoResetTest, DeleteTargetObjectAfterMoveIsSafe) {
143 auto hwf = std::make_unique<HasWeakFactory<int>>();
144 WeakAutoReset reset = hwf->SetValueScoped(1);
145 WeakAutoReset reset2 = std::move(reset);
146 hwf.reset();
147 // ASAN will crash here if we don't correctly detect that hwf has gone away.
148 }
149
150 using HasWeakFactoryPointer = std::unique_ptr<HasWeakFactory<int>>;
151
TEST(WeakAutoResetTest,TestSafelyMovesValue)152 TEST(WeakAutoResetTest, TestSafelyMovesValue) {
153 // We'll use an object that owns another object while keeping a weak reference
154 // to the inner object to determine its lifetime.
155 auto inner = std::make_unique<HasWeakFactory<int>>();
156 auto weak_ptr = inner->GetWeakPtr();
157 auto outer = std::make_unique<HasWeakFactory<HasWeakFactoryPointer>>();
158 outer->set_value(std::move(inner));
159 ASSERT_TRUE(weak_ptr);
160
161 {
162 // Transfer ownership of the inner object to the auto-reset.
163 WeakAutoReset reset = outer->SetValueScoped(HasWeakFactoryPointer());
164 EXPECT_TRUE(weak_ptr);
165 EXPECT_FALSE(outer->value());
166 }
167
168 // Transfer ownership back to the outer object.
169 EXPECT_TRUE(weak_ptr);
170 EXPECT_TRUE(outer->value());
171
172 // Destroying the outer object destroys the inner object.
173 outer.reset();
174 EXPECT_FALSE(weak_ptr);
175 }
176
TEST(WeakAutoResetTest,TestSafelyMovesValueAndThenDestroysIt)177 TEST(WeakAutoResetTest, TestSafelyMovesValueAndThenDestroysIt) {
178 // We'll use an object that owns another object while keeping a weak reference
179 // to the inner object to determine its lifetime.
180 auto inner = std::make_unique<HasWeakFactory<int>>();
181 auto weak_ptr = inner->GetWeakPtr();
182 auto outer = std::make_unique<HasWeakFactory<HasWeakFactoryPointer>>();
183 outer->set_value(std::move(inner));
184 ASSERT_TRUE(weak_ptr);
185
186 {
187 // Transfer ownership of the inner object to the auto-reset.
188 WeakAutoReset reset = outer->SetValueScoped(HasWeakFactoryPointer());
189 EXPECT_TRUE(weak_ptr);
190 EXPECT_FALSE(outer->value());
191
192 // Destroy the outer object. The auto-reset still owns the old inner object.
193 outer.reset();
194 EXPECT_TRUE(weak_ptr);
195 }
196
197 // Onwership can't be transferred back so the inner object is destroyed.
198 EXPECT_FALSE(weak_ptr);
199 }
200
TEST(WeakAutoResetTest,TestMoveConstructorMovesOldValue)201 TEST(WeakAutoResetTest, TestMoveConstructorMovesOldValue) {
202 // We'll use an object that owns another object while keeping a weak reference
203 // to the inner object to determine its lifetime.
204 auto inner = std::make_unique<HasWeakFactory<int>>();
205 auto weak_ptr = inner->GetWeakPtr();
206 auto outer = std::make_unique<HasWeakFactory<HasWeakFactoryPointer>>();
207 outer->set_value(std::move(inner));
208 ASSERT_TRUE(weak_ptr);
209
210 {
211 // Transfer ownership of the inner object to the auto-reset.
212 WeakAutoReset reset = outer->SetValueScoped(HasWeakFactoryPointer());
213 EXPECT_TRUE(weak_ptr);
214 EXPECT_FALSE(outer->value());
215
216 {
217 // Move ownership of the old object to a new auto-reset.
218 WeakAutoReset reset2(std::move(reset));
219 EXPECT_TRUE(weak_ptr);
220 EXPECT_FALSE(outer->value());
221 }
222
223 // Destroying the second auto-reset transfers ownership back to the outer
224 // object.
225 EXPECT_TRUE(weak_ptr);
226 EXPECT_TRUE(outer->value());
227 }
228 }
229
TEST(WeakAutoResetTest,TestMoveAssignmentMovesOldValue)230 TEST(WeakAutoResetTest, TestMoveAssignmentMovesOldValue) {
231 // We'll use an object that owns another object while keeping a weak reference
232 // to the inner object to determine its lifetime.
233 auto inner = std::make_unique<HasWeakFactory<int>>();
234 auto weak_ptr = inner->GetWeakPtr();
235 auto outer = std::make_unique<HasWeakFactory<HasWeakFactoryPointer>>();
236 outer->set_value(std::move(inner));
237 ASSERT_TRUE(weak_ptr);
238
239 {
240 // Create an auto-reset that will receive ownership later.
241 WeakAutoReset<HasWeakFactory<HasWeakFactoryPointer>, HasWeakFactoryPointer>
242 reset;
243
244 {
245 // Move ownership of the inner object to an auto-reset.
246 WeakAutoReset reset2 = outer->SetValueScoped(HasWeakFactoryPointer());
247 EXPECT_TRUE(weak_ptr);
248 EXPECT_FALSE(outer->value());
249
250 // Transfer ownership to the other auto-reset.
251 reset = std::move(reset2);
252 EXPECT_TRUE(weak_ptr);
253 EXPECT_FALSE(outer->value());
254 }
255
256 // The auto-reset that initially received the value is gone, but the one
257 // actually holding the value is still in scope.
258 EXPECT_TRUE(weak_ptr);
259 EXPECT_FALSE(outer->value());
260 }
261
262 // Now both have gone out of scope, so the inner object should be returned to
263 // the outer one.
264 EXPECT_TRUE(weak_ptr);
265 EXPECT_TRUE(outer->value());
266 }
267
TEST(WeakAutoResetTest,TestOldAndNewValuesAreSwapped)268 TEST(WeakAutoResetTest, TestOldAndNewValuesAreSwapped) {
269 // We'll use an object that owns another object while keeping a weak reference
270 // to the inner object to determine its lifetime.
271 auto inner = std::make_unique<HasWeakFactory<int>>();
272 auto weak_ptr = inner->GetWeakPtr();
273 auto outer = std::make_unique<HasWeakFactory<HasWeakFactoryPointer>>();
274 outer->set_value(std::move(inner));
275 ASSERT_TRUE(weak_ptr);
276
277 // Create a second inner object that we'll swap with the first.
278 auto replacement = std::make_unique<HasWeakFactory<int>>();
279 auto weak_ptr2 = replacement->GetWeakPtr();
280
281 {
282 // Swap the values.
283 WeakAutoReset reset = outer->SetValueScoped(std::move(replacement));
284 EXPECT_TRUE(weak_ptr);
285 EXPECT_TRUE(weak_ptr2);
286 EXPECT_EQ(weak_ptr2.get(), outer->value().get());
287 }
288
289 // Unswap the values. The replacement is discarded.
290 EXPECT_TRUE(weak_ptr);
291 EXPECT_FALSE(weak_ptr2);
292 EXPECT_EQ(weak_ptr.get(), outer->value().get());
293 }
294
295 } // namespace base
296