1 // Copyright 2017 The Dawn Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include <gtest/gtest.h>
16 #include <thread>
17
18 #include "common/RefCounted.h"
19
20 class RCTest : public RefCounted {
21 public:
RCTest()22 RCTest() : RefCounted() {
23 }
24
RCTest(uint64_t payload)25 RCTest(uint64_t payload) : RefCounted(payload) {
26 }
27
RCTest(bool* deleted)28 RCTest(bool* deleted) : mDeleted(deleted) {
29 }
30
31 ~RCTest() override {
32 if (mDeleted != nullptr) {
33 *mDeleted = true;
34 }
35 }
36
GetThis()37 RCTest* GetThis() {
38 return this;
39 }
40
41 private:
42 bool* mDeleted = nullptr;
43 };
44
45 struct RCTestDerived : public RCTest {
46 using RCTest::RCTest;
47 };
48
49 // Test that RCs start with one ref, and removing it destroys the object.
TEST(RefCounted, StartsWithOneRef)50 TEST(RefCounted, StartsWithOneRef) {
51 bool deleted = false;
52 auto test = new RCTest(&deleted);
53
54 test->Release();
55 EXPECT_TRUE(deleted);
56 }
57
58 // Test adding refs keep the RC alive.
TEST(RefCounted, AddingRefKeepsAlive)59 TEST(RefCounted, AddingRefKeepsAlive) {
60 bool deleted = false;
61 auto test = new RCTest(&deleted);
62
63 test->Reference();
64 test->Release();
65 EXPECT_FALSE(deleted);
66
67 test->Release();
68 EXPECT_TRUE(deleted);
69 }
70
71 // Test that Reference and Release atomically change the refcount.
TEST(RefCounted, RaceOnReferenceRelease)72 TEST(RefCounted, RaceOnReferenceRelease) {
73 bool deleted = false;
74 auto* test = new RCTest(&deleted);
75
76 auto referenceManyTimes = [test]() {
77 for (uint32_t i = 0; i < 100000; ++i) {
78 test->Reference();
79 }
80 };
81 std::thread t1(referenceManyTimes);
82 std::thread t2(referenceManyTimes);
83
84 t1.join();
85 t2.join();
86 EXPECT_EQ(test->GetRefCountForTesting(), 200001u);
87
88 auto releaseManyTimes = [test]() {
89 for (uint32_t i = 0; i < 100000; ++i) {
90 test->Release();
91 }
92 };
93
94 std::thread t3(releaseManyTimes);
95 std::thread t4(releaseManyTimes);
96 t3.join();
97 t4.join();
98 EXPECT_EQ(test->GetRefCountForTesting(), 1u);
99
100 test->Release();
101 EXPECT_TRUE(deleted);
102 }
103
104 // Test Ref remove reference when going out of scope
TEST(Ref, EndOfScopeRemovesRef)105 TEST(Ref, EndOfScopeRemovesRef) {
106 bool deleted = false;
107 {
108 Ref<RCTest> test(new RCTest(&deleted));
109 test->Release();
110 }
111 EXPECT_TRUE(deleted);
112 }
113
114 // Test getting pointer out of the Ref
TEST(Ref, Gets)115 TEST(Ref, Gets) {
116 RCTest* original = new RCTest;
117 Ref<RCTest> test(original);
118 test->Release();
119
120 EXPECT_EQ(test.Get(), original);
121 EXPECT_EQ(test->GetThis(), original);
122 }
123
124 // Test Refs default to null
TEST(Ref, DefaultsToNull)125 TEST(Ref, DefaultsToNull) {
126 Ref<RCTest> test;
127
128 EXPECT_EQ(test.Get(), nullptr);
129 // Can't check GetThis() returns nullptr, as it would be undefined behavior.
130 }
131
132 // Test Ref's copy constructor
TEST(Ref, CopyConstructor)133 TEST(Ref, CopyConstructor) {
134 bool deleted = false;
135 RCTest* original = new RCTest(&deleted);
136
137 Ref<RCTest> source(original);
138 EXPECT_EQ(original->GetRefCountForTesting(), 2u);
139
140 Ref<RCTest> destination(source);
141 EXPECT_EQ(original->GetRefCountForTesting(), 3u);
142
143 original->Release();
144 EXPECT_EQ(original->GetRefCountForTesting(), 2u);
145
146 EXPECT_EQ(source.Get(), original);
147 EXPECT_EQ(destination.Get(), original);
148
149 source = nullptr;
150 EXPECT_FALSE(deleted);
151 EXPECT_EQ(original->GetRefCountForTesting(), 1u);
152
153 destination = nullptr;
154 EXPECT_TRUE(deleted);
155 }
156
157 // Test Ref's copy assignment
TEST(Ref, CopyAssignment)158 TEST(Ref, CopyAssignment) {
159 bool deleted = false;
160 RCTest* original = new RCTest(&deleted);
161
162 Ref<RCTest> source(original);
163 original->Release();
164
165 Ref<RCTest> destination;
166 destination = source;
167
168 EXPECT_EQ(source.Get(), original);
169 EXPECT_EQ(destination.Get(), original);
170
171 source = nullptr;
172 // This fails when address sanitizer is turned on
173 EXPECT_FALSE(deleted);
174
175 destination = nullptr;
176 EXPECT_TRUE(deleted);
177 }
178
179 // Test Ref's move constructor
TEST(Ref, MoveConstructor)180 TEST(Ref, MoveConstructor) {
181 bool deleted = false;
182 RCTest* original = new RCTest(&deleted);
183
184 Ref<RCTest> source(original);
185 EXPECT_EQ(original->GetRefCountForTesting(), 2u);
186
187 Ref<RCTest> destination(std::move(source));
188 EXPECT_EQ(original->GetRefCountForTesting(), 2u);
189
190 original->Release();
191 EXPECT_EQ(original->GetRefCountForTesting(), 1u);
192
193 EXPECT_EQ(source.Get(), nullptr);
194 EXPECT_EQ(destination.Get(), original);
195 EXPECT_FALSE(deleted);
196
197 destination = nullptr;
198 EXPECT_TRUE(deleted);
199 }
200
201 // Test Ref's move assignment
TEST(Ref, MoveAssignment)202 TEST(Ref, MoveAssignment) {
203 bool deleted = false;
204 RCTest* original = new RCTest(&deleted);
205
206 Ref<RCTest> source(original);
207 EXPECT_EQ(original->GetRefCountForTesting(), 2u);
208
209 original->Release();
210 EXPECT_EQ(original->GetRefCountForTesting(), 1u);
211
212 Ref<RCTest> destination;
213 destination = std::move(source);
214 EXPECT_EQ(original->GetRefCountForTesting(), 1u);
215
216 EXPECT_EQ(source.Get(), nullptr);
217 EXPECT_EQ(destination.Get(), original);
218 EXPECT_FALSE(deleted);
219
220 destination = nullptr;
221 EXPECT_TRUE(deleted);
222 }
223
224 // Test move assigment where the destination and source
225 // point to the same underlying object.
TEST(Ref, MoveAssignmentSameObject)226 TEST(Ref, MoveAssignmentSameObject) {
227 bool deleted = false;
228 RCTest* original = new RCTest(&deleted);
229
230 Ref<RCTest> source(original);
231 EXPECT_EQ(original->GetRefCountForTesting(), 2u);
232
233 original->Release();
234 EXPECT_EQ(original->GetRefCountForTesting(), 1u);
235
236 Ref<RCTest>& referenceToSource = source;
237 EXPECT_EQ(original->GetRefCountForTesting(), 1u);
238
239 referenceToSource = std::move(source);
240
241 EXPECT_EQ(source.Get(), original);
242 EXPECT_EQ(original->GetRefCountForTesting(), 1u);
243 EXPECT_FALSE(deleted);
244
245 source = nullptr;
246 EXPECT_TRUE(deleted);
247 }
248
249 // Test the payload initial value is set correctly
TEST(Ref, InitialPayloadValue)250 TEST(Ref, InitialPayloadValue) {
251 RCTest* testDefaultConstructor = new RCTest();
252 EXPECT_EQ(testDefaultConstructor->GetRefCountPayload(), 0u);
253 testDefaultConstructor->Release();
254
255 RCTest* testZero = new RCTest(uint64_t(0ull));
256 EXPECT_EQ(testZero->GetRefCountPayload(), 0u);
257 testZero->Release();
258
259 RCTest* testOne = new RCTest(1ull);
260 EXPECT_EQ(testOne->GetRefCountPayload(), 1u);
261 testOne->Release();
262 }
263
264 // Test that the payload survives ref and release operations
TEST(Ref, PayloadUnchangedByRefCounting)265 TEST(Ref, PayloadUnchangedByRefCounting) {
266 RCTest* test = new RCTest(1ull);
267 EXPECT_EQ(test->GetRefCountPayload(), 1u);
268
269 test->Reference();
270 EXPECT_EQ(test->GetRefCountPayload(), 1u);
271 test->Release();
272 EXPECT_EQ(test->GetRefCountPayload(), 1u);
273
274 test->Release();
275 }
276
277 // Test that Detach pulls out the pointer and stops tracking it.
TEST(Ref, Detach)278 TEST(Ref, Detach) {
279 bool deleted = false;
280 RCTest* original = new RCTest(&deleted);
281
282 Ref<RCTest> test(original);
283 original->Release();
284
285 RCTest* detached = test.Detach();
286 EXPECT_EQ(detached, original);
287 EXPECT_EQ(detached->GetRefCountForTesting(), 1u);
288 EXPECT_EQ(test.Get(), nullptr);
289
290 detached->Release();
291 EXPECT_TRUE(deleted);
292 }
293
294 // Test constructor passed a derived pointer
TEST(Ref, DerivedPointerConstructor)295 TEST(Ref, DerivedPointerConstructor) {
296 bool deleted = false;
297 {
298 Ref<RCTest> test(new RCTestDerived(&deleted));
299 test->Release();
300 }
301 EXPECT_TRUE(deleted);
302 }
303
304 // Test copy constructor of derived class
TEST(Ref, DerivedCopyConstructor)305 TEST(Ref, DerivedCopyConstructor) {
306 bool deleted = false;
307 Ref<RCTestDerived> testDerived(new RCTestDerived(&deleted));
308 testDerived->Release();
309
310 {
311 Ref<RCTest> testBase(testDerived);
312 EXPECT_EQ(testBase->GetRefCountForTesting(), 2u);
313 EXPECT_EQ(testDerived->GetRefCountForTesting(), 2u);
314 }
315
316 EXPECT_EQ(testDerived->GetRefCountForTesting(), 1u);
317 }
318
319 // Test Ref constructed with nullptr
TEST(Ref, ConstructedWithNullptr)320 TEST(Ref, ConstructedWithNullptr) {
321 Ref<RCTest> test(nullptr);
322 EXPECT_EQ(test.Get(), nullptr);
323 }
324
325 // Test Ref's copy assignment with derived class
TEST(Ref, CopyAssignmentDerived)326 TEST(Ref, CopyAssignmentDerived) {
327 bool deleted = false;
328
329 RCTestDerived* original = new RCTestDerived(&deleted);
330 Ref<RCTestDerived> source(original);
331 original->Release();
332 EXPECT_EQ(original->GetRefCountForTesting(), 1u);
333
334 Ref<RCTest> destination;
335 destination = source;
336 EXPECT_EQ(original->GetRefCountForTesting(), 2u);
337
338 EXPECT_EQ(source.Get(), original);
339 EXPECT_EQ(destination.Get(), original);
340
341 source = nullptr;
342 EXPECT_EQ(original->GetRefCountForTesting(), 1u);
343 EXPECT_FALSE(deleted);
344
345 destination = nullptr;
346 EXPECT_TRUE(deleted);
347 }
348
349 // Test Ref's move constructor with derived class
TEST(Ref, MoveConstructorDerived)350 TEST(Ref, MoveConstructorDerived) {
351 bool deleted = false;
352 RCTestDerived* original = new RCTestDerived(&deleted);
353
354 Ref<RCTestDerived> source(original);
355 EXPECT_EQ(original->GetRefCountForTesting(), 2u);
356
357 Ref<RCTest> destination(std::move(source));
358 EXPECT_EQ(original->GetRefCountForTesting(), 2u);
359
360 original->Release();
361 EXPECT_EQ(original->GetRefCountForTesting(), 1u);
362
363 EXPECT_EQ(source.Get(), nullptr);
364 EXPECT_EQ(destination.Get(), original);
365 EXPECT_FALSE(deleted);
366
367 destination = nullptr;
368 EXPECT_TRUE(deleted);
369 }
370
371 // Test Ref's move assignment with derived class
TEST(Ref, MoveAssignmentDerived)372 TEST(Ref, MoveAssignmentDerived) {
373 bool deleted = false;
374 RCTestDerived* original = new RCTestDerived(&deleted);
375
376 Ref<RCTestDerived> source(original);
377 EXPECT_EQ(original->GetRefCountForTesting(), 2u);
378
379 original->Release();
380 EXPECT_EQ(original->GetRefCountForTesting(), 1u);
381
382 Ref<RCTest> destination;
383 destination = std::move(source);
384
385 EXPECT_EQ(original->GetRefCountForTesting(), 1u);
386
387 EXPECT_EQ(source.Get(), nullptr);
388 EXPECT_EQ(destination.Get(), original);
389 EXPECT_FALSE(deleted);
390
391 destination = nullptr;
392 EXPECT_TRUE(deleted);
393 }
394
395 // Test Ref's InitializeInto.
TEST(Ref, InitializeInto)396 TEST(Ref, InitializeInto) {
397 bool deleted = false;
398 RCTest* original = new RCTest(&deleted);
399
400 // InitializeInto acquires the ref.
401 Ref<RCTest> ref;
402 *ref.InitializeInto() = original;
403 EXPECT_EQ(original->GetRefCountForTesting(), 1u);
404
405 ref = nullptr;
406 EXPECT_TRUE(deleted);
407 }
408