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