blob: b793c07b6b2ed61b43a6170700adef3dd63130a8 [file] [log] [blame]
Gabriel Charettec88dae7b2018-04-19 19:02:161// 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
17namespace base {
18namespace internal {
19
20namespace {
21
22class ObjectWithTrackedRefs {
23 public:
24 ObjectWithTrackedRefs() : tracked_ref_factory_(this) {}
25 ~ObjectWithTrackedRefs() { under_destruction_.Set(); }
26
27 TrackedRef<ObjectWithTrackedRefs> GetTrackedRef() {
28 return tracked_ref_factory_.GetTrackedRef();
29 }
30
31 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.
47TEST(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
79TEST(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.
111TEST(TrackedRefTest, NoTrackedRefs) {
112 ObjectWithTrackedRefs obj;
113}
114
115namespace {
116void 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.
121TEST(TrackedRefTest, NoPendingTrackedRefs) {
122 ObjectWithTrackedRefs obj;
123 ConsumesTrackedRef(obj.GetTrackedRef());
124}
125
126TEST(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