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