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