1 // Copyright 2018 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/task/thread_pool/tracked_ref.h"
6
7 #include <memory>
8 #include <utility>
9 #include <vector>
10
11 #include "base/functional/bind.h"
12 #include "base/synchronization/atomic_flag.h"
13 #include "base/test/test_timeouts.h"
14 #include "base/threading/thread.h"
15 #include "base/time/time.h"
16 #include "testing/gtest/include/gtest/gtest.h"
17
18 namespace base {
19 namespace internal {
20
21 namespace {
22
23 class ObjectWithTrackedRefs {
24 public:
ObjectWithTrackedRefs()25 ObjectWithTrackedRefs() : tracked_ref_factory_(this) {}
26 ObjectWithTrackedRefs(const ObjectWithTrackedRefs&) = delete;
27 ObjectWithTrackedRefs& operator=(const ObjectWithTrackedRefs&) = delete;
~ObjectWithTrackedRefs()28 ~ObjectWithTrackedRefs() { under_destruction_.Set(); }
29
GetTrackedRef()30 TrackedRef<ObjectWithTrackedRefs> GetTrackedRef() {
31 return tracked_ref_factory_.GetTrackedRef();
32 }
33
under_destruction() const34 bool under_destruction() const { return under_destruction_.IsSet(); }
35
36 private:
37 // True once ~ObjectWithTrackedRefs() has been initiated.
38 AtomicFlag under_destruction_;
39
40 TrackedRefFactory<ObjectWithTrackedRefs> tracked_ref_factory_;
41 };
42
43 } // namespace
44
45 // Test that an object with a TrackedRefFactory can be destroyed by a single
46 // owner but that its destruction will be blocked on the TrackedRefs being
47 // released.
TEST(TrackedRefTest,TrackedRefObjectDeletion)48 TEST(TrackedRefTest, TrackedRefObjectDeletion) {
49 Thread thread("TrackedRefTestThread");
50 thread.Start();
51
52 std::unique_ptr<ObjectWithTrackedRefs> obj =
53 std::make_unique<ObjectWithTrackedRefs>();
54
55 TimeTicks begin = TimeTicks::Now();
56
57 thread.task_runner()->PostDelayedTask(
58 FROM_HERE,
59 BindOnce(
60 [](TrackedRef<ObjectWithTrackedRefs> obj) {
61 // By the time this kicks in, the object should already be under
62 // destruction, but blocked on this TrackedRef being released. This
63 // is technically racy (main thread has to run |obj.reset()| and
64 // this thread has to observe the side-effects before this delayed
65 // task fires). If this ever flakes this expectation could be turned
66 // into a while(!obj->under_destruction()); but until that's proven
67 // flaky in practice, this expectation is more readable and
68 // diagnosable then a hang.
69 EXPECT_TRUE(obj->under_destruction());
70 },
71 obj->GetTrackedRef()),
72 TestTimeouts::tiny_timeout());
73
74 // This should kick off destruction but block until the above task resolves
75 // and releases the TrackedRef.
76 obj.reset();
77 EXPECT_GE(TimeTicks::Now() - begin, TestTimeouts::tiny_timeout());
78 }
79
TEST(TrackedRefTest,ManyThreadsRacing)80 TEST(TrackedRefTest, ManyThreadsRacing) {
81 constexpr int kNumThreads = 16;
82 std::vector<std::unique_ptr<Thread>> threads;
83 for (int i = 0; i < kNumThreads; ++i) {
84 threads.push_back(std::make_unique<Thread>("TrackedRefTestThread"));
85 threads.back()->StartAndWaitForTesting();
86 }
87
88 std::unique_ptr<ObjectWithTrackedRefs> obj =
89 std::make_unique<ObjectWithTrackedRefs>();
90
91 // Send a TrackedRef to each thread.
92 for (auto& thread : threads) {
93 thread->task_runner()->PostTask(
94 FROM_HERE, BindOnce(
95 [](TrackedRef<ObjectWithTrackedRefs> obj) {
96 // Confirm it's still safe to
97 // dereference |obj| (and, bonus, that
98 // playing with TrackedRefs some more
99 // isn't problematic).
100 EXPECT_TRUE(obj->GetTrackedRef());
101 },
102 obj->GetTrackedRef()));
103 }
104
105 // Initiate destruction racily with the above tasks' execution (they will
106 // crash if TrackedRefs aren't WAI).
107 obj.reset();
108 }
109
110 // Test that instantiating and deleting a TrackedRefFactory without ever taking
111 // a TrackedRef on it is fine.
TEST(TrackedRefTest,NoTrackedRefs)112 TEST(TrackedRefTest, NoTrackedRefs) {
113 ObjectWithTrackedRefs obj;
114 }
115
116 namespace {
ConsumesTrackedRef(TrackedRef<ObjectWithTrackedRefs> obj)117 void ConsumesTrackedRef(TrackedRef<ObjectWithTrackedRefs> obj) {}
118 } // namespace
119
120 // Test that destroying a TrackedRefFactory which had TrackedRefs in the past
121 // that are already gone is WAI.
TEST(TrackedRefTest,NoPendingTrackedRefs)122 TEST(TrackedRefTest, NoPendingTrackedRefs) {
123 ObjectWithTrackedRefs obj;
124 ConsumesTrackedRef(obj.GetTrackedRef());
125 }
126
TEST(TrackedRefTest,CopyAndMoveSemantics)127 TEST(TrackedRefTest, CopyAndMoveSemantics) {
128 struct Foo {
129 Foo() : factory(this) {}
130 TrackedRefFactory<Foo> factory;
131 };
132 Foo foo;
133
134 EXPECT_EQ(1, foo.factory.live_tracked_refs_.SubtleRefCountForDebug());
135
136 {
137 TrackedRef<Foo> plain = foo.factory.GetTrackedRef();
138 EXPECT_EQ(2, foo.factory.live_tracked_refs_.SubtleRefCountForDebug());
139
140 TrackedRef<Foo> copy_constructed(plain);
141 EXPECT_EQ(3, foo.factory.live_tracked_refs_.SubtleRefCountForDebug());
142
143 TrackedRef<Foo> moved_constructed(std::move(copy_constructed));
144 EXPECT_EQ(3, foo.factory.live_tracked_refs_.SubtleRefCountForDebug());
145 }
146
147 EXPECT_EQ(1, foo.factory.live_tracked_refs_.SubtleRefCountForDebug());
148 }
149
150 } // namespace internal
151 } // namespace base
152