1 // Copyright 2016 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/worker_thread_set.h"
6
7 #include "base/check_op.h"
8 #include "base/memory/ref_counted.h"
9 #include "base/task/thread_pool/task_source.h"
10 #include "base/task/thread_pool/task_tracker.h"
11 #include "base/task/thread_pool/worker_thread.h"
12 #include "base/task/thread_pool/worker_thread_waitable_event.h"
13 #include "base/test/gtest_util.h"
14 #include "base/threading/platform_thread.h"
15 #include "base/time/time.h"
16 #include "testing/gtest/include/gtest/gtest.h"
17
18 namespace base::internal {
19
20 namespace {
21
22 class MockWorkerThreadDelegate : public WorkerThreadWaitableEvent::Delegate {
23 public:
GetThreadLabel() const24 WorkerThread::ThreadLabel GetThreadLabel() const override {
25 return WorkerThread::ThreadLabel::DEDICATED;
26 }
OnMainEntry(WorkerThread * worker)27 void OnMainEntry(WorkerThread* worker) override {}
GetWork(WorkerThread * worker)28 RegisteredTaskSource GetWork(WorkerThread* worker) override {
29 return nullptr;
30 }
SwapProcessedTask(RegisteredTaskSource task_source,WorkerThread * worker)31 RegisteredTaskSource SwapProcessedTask(RegisteredTaskSource task_source,
32 WorkerThread* worker) override {
33 ADD_FAILURE() << "Unexpected call to SwapProcessedTask()";
34 return nullptr;
35 }
GetSleepTimeout()36 TimeDelta GetSleepTimeout() override { return TimeDelta::Max(); }
37 };
38
39 class ThreadPoolWorkerSetTest : public testing::Test {
40 protected:
SetUp()41 void SetUp() override {
42 worker_a_ = MakeRefCounted<WorkerThreadWaitableEvent>(
43 ThreadType::kDefault, std::make_unique<MockWorkerThreadDelegate>(),
44 task_tracker_.GetTrackedRef(), 0);
45 ASSERT_TRUE(worker_a_);
46 worker_b_ = MakeRefCounted<WorkerThreadWaitableEvent>(
47 ThreadType::kDefault, std::make_unique<MockWorkerThreadDelegate>(),
48 task_tracker_.GetTrackedRef(), 1);
49 ASSERT_TRUE(worker_b_);
50 worker_c_ = MakeRefCounted<WorkerThreadWaitableEvent>(
51 ThreadType::kDefault, std::make_unique<MockWorkerThreadDelegate>(),
52 task_tracker_.GetTrackedRef(), 2);
53 ASSERT_TRUE(worker_c_);
54 }
55
56 private:
57 TaskTracker task_tracker_;
58
59 protected:
60 scoped_refptr<WorkerThreadWaitableEvent> worker_a_;
61 scoped_refptr<WorkerThreadWaitableEvent> worker_b_;
62 scoped_refptr<WorkerThreadWaitableEvent> worker_c_;
63 };
64
65 } // namespace
66
67 // Verify that Insert() and Take() add/remove values in FIFO order.
TEST_F(ThreadPoolWorkerSetTest,InsertTake)68 TEST_F(ThreadPoolWorkerSetTest, InsertTake) {
69 WorkerThreadSet set;
70 EXPECT_EQ(nullptr, set.Take());
71
72 EXPECT_TRUE(set.IsEmpty());
73 EXPECT_EQ(0U, set.Size());
74
75 set.Insert(worker_a_.get());
76 EXPECT_FALSE(set.IsEmpty());
77 EXPECT_EQ(1U, set.Size());
78
79 set.Insert(worker_b_.get());
80 EXPECT_FALSE(set.IsEmpty());
81 EXPECT_EQ(2U, set.Size());
82
83 set.Insert(worker_c_.get());
84 EXPECT_FALSE(set.IsEmpty());
85 EXPECT_EQ(3U, set.Size());
86
87 WorkerThreadWaitableEvent* idle_worker = set.Take();
88 EXPECT_EQ(idle_worker, worker_a_.get());
89 EXPECT_FALSE(set.IsEmpty());
90 EXPECT_EQ(2U, set.Size());
91
92 set.Insert(idle_worker);
93 EXPECT_FALSE(set.IsEmpty());
94 EXPECT_EQ(3U, set.Size());
95
96 EXPECT_EQ(idle_worker, set.Take());
97 EXPECT_FALSE(set.IsEmpty());
98 EXPECT_EQ(2U, set.Size());
99
100 EXPECT_TRUE(set.Take());
101 EXPECT_FALSE(set.IsEmpty());
102 EXPECT_EQ(1U, set.Size());
103
104 EXPECT_TRUE(set.Take());
105 EXPECT_TRUE(set.IsEmpty());
106 EXPECT_EQ(0U, set.Size());
107
108 EXPECT_EQ(nullptr, set.Take());
109 }
110
111 // Verify that Peek() returns the correct values in FIFO order.
TEST_F(ThreadPoolWorkerSetTest,PeekPop)112 TEST_F(ThreadPoolWorkerSetTest, PeekPop) {
113 WorkerThreadSet set;
114 EXPECT_EQ(nullptr, set.Peek());
115
116 EXPECT_TRUE(set.IsEmpty());
117 EXPECT_EQ(0U, set.Size());
118
119 set.Insert(worker_a_.get());
120 EXPECT_EQ(worker_a_.get(), set.Peek());
121 EXPECT_FALSE(set.IsEmpty());
122 EXPECT_EQ(1U, set.Size());
123
124 set.Insert(worker_b_.get());
125 EXPECT_FALSE(set.IsEmpty());
126 EXPECT_EQ(2U, set.Size());
127
128 set.Insert(worker_c_.get());
129 EXPECT_FALSE(set.IsEmpty());
130 EXPECT_EQ(3U, set.Size());
131
132 WorkerThreadWaitableEvent* idle_worker = set.Take();
133 EXPECT_EQ(worker_a_.get(), idle_worker);
134 EXPECT_EQ(worker_b_.get(), set.Peek());
135 EXPECT_FALSE(set.IsEmpty());
136 EXPECT_EQ(2U, set.Size());
137
138 EXPECT_EQ(worker_b_.get(), set.Take());
139 EXPECT_EQ(worker_c_.get(), set.Peek());
140 EXPECT_FALSE(set.IsEmpty());
141 EXPECT_EQ(1U, set.Size());
142
143 EXPECT_EQ(worker_c_.get(), set.Take());
144 EXPECT_TRUE(set.IsEmpty());
145 EXPECT_EQ(0U, set.Size());
146
147 EXPECT_EQ(nullptr, set.Peek());
148 }
149
150 // Verify that Contains() returns true for workers on the set.
TEST_F(ThreadPoolWorkerSetTest,Contains)151 TEST_F(ThreadPoolWorkerSetTest, Contains) {
152 WorkerThreadSet set;
153 EXPECT_FALSE(set.Contains(worker_a_.get()));
154 EXPECT_FALSE(set.Contains(worker_b_.get()));
155 EXPECT_FALSE(set.Contains(worker_c_.get()));
156
157 set.Insert(worker_a_.get());
158 EXPECT_TRUE(set.Contains(worker_a_.get()));
159 EXPECT_FALSE(set.Contains(worker_b_.get()));
160 EXPECT_FALSE(set.Contains(worker_c_.get()));
161
162 set.Insert(worker_b_.get());
163 EXPECT_TRUE(set.Contains(worker_a_.get()));
164 EXPECT_TRUE(set.Contains(worker_b_.get()));
165 EXPECT_FALSE(set.Contains(worker_c_.get()));
166
167 set.Insert(worker_c_.get());
168 EXPECT_TRUE(set.Contains(worker_a_.get()));
169 EXPECT_TRUE(set.Contains(worker_b_.get()));
170 EXPECT_TRUE(set.Contains(worker_c_.get()));
171
172 WorkerThreadWaitableEvent* idle_worker = set.Take();
173 EXPECT_EQ(idle_worker, worker_a_.get());
174 EXPECT_FALSE(set.Contains(worker_a_.get()));
175 EXPECT_TRUE(set.Contains(worker_b_.get()));
176 EXPECT_TRUE(set.Contains(worker_c_.get()));
177
178 set.Take();
179
180 set.Take();
181 EXPECT_FALSE(set.Contains(worker_a_.get()));
182 EXPECT_FALSE(set.Contains(worker_b_.get()));
183 EXPECT_FALSE(set.Contains(worker_c_.get()));
184 }
185
186 // Verify that a value can be removed by Remove().
TEST_F(ThreadPoolWorkerSetTest,Remove)187 TEST_F(ThreadPoolWorkerSetTest, Remove) {
188 WorkerThreadSet set;
189 EXPECT_TRUE(set.IsEmpty());
190 EXPECT_EQ(0U, set.Size());
191
192 set.Insert(worker_a_.get());
193 EXPECT_FALSE(set.IsEmpty());
194 EXPECT_EQ(1U, set.Size());
195
196 set.Insert(worker_b_.get());
197 EXPECT_FALSE(set.IsEmpty());
198 EXPECT_EQ(2U, set.Size());
199
200 set.Insert(worker_c_.get());
201 EXPECT_FALSE(set.IsEmpty());
202 EXPECT_EQ(3U, set.Size());
203
204 set.Remove(worker_b_.get());
205 EXPECT_FALSE(set.IsEmpty());
206 EXPECT_EQ(2U, set.Size());
207
208 EXPECT_EQ(worker_a_.get(), set.Take());
209 EXPECT_FALSE(set.IsEmpty());
210 EXPECT_EQ(1U, set.Size());
211
212 EXPECT_EQ(worker_c_.get(), set.Take());
213 EXPECT_TRUE(set.IsEmpty());
214 EXPECT_EQ(0U, set.Size());
215 }
216
217 // Verify that a value can be pushed again after it has been removed.
TEST_F(ThreadPoolWorkerSetTest,PushAfterRemove)218 TEST_F(ThreadPoolWorkerSetTest, PushAfterRemove) {
219 WorkerThreadSet set;
220 EXPECT_EQ(0U, set.Size());
221
222 set.Insert(worker_a_.get());
223 EXPECT_EQ(1U, set.Size());
224
225 // Need to also push worker B for this test as it's illegal to Remove() the
226 // front of the set.
227 set.Insert(worker_b_.get());
228 EXPECT_EQ(2U, set.Size());
229
230 set.Remove(worker_b_.get());
231 worker_b_->EndUnusedPeriod();
232 EXPECT_EQ(1U, set.Size());
233
234 set.Insert(worker_b_.get());
235 EXPECT_EQ(2U, set.Size());
236 }
237
238 // Verify that Insert() DCHECKs when a value is inserted twice.
TEST_F(ThreadPoolWorkerSetTest,PushTwice)239 TEST_F(ThreadPoolWorkerSetTest, PushTwice) {
240 WorkerThreadSet set;
241 set.Insert(worker_a_.get());
242 EXPECT_DCHECK_DEATH({ set.Insert(worker_a_.get()); });
243 }
244
245 } // namespace base::internal
246