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