xref: /aosp_15_r20/external/cronet/base/task/thread_pool/tracked_ref_unittest.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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