1 // Copyright 2017 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 "third_party/blink/renderer/platform/scheduler/public/worker_scheduler.h"
6 
7 #include <memory>
8 #include "base/bind.h"
9 #include "base/macros.h"
10 #include "base/task/sequence_manager/test/sequence_manager_for_test.h"
11 #include "base/test/simple_test_tick_clock.h"
12 #include "base/test/test_mock_time_task_runner.h"
13 #include "testing/gmock/include/gmock/gmock.h"
14 #include "testing/gtest/include/gtest/gtest.h"
15 #include "third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler.h"
16 #include "third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.h"
17 #include "third_party/blink/renderer/platform/wtf/functional.h"
18 
19 // TODO(crbug.com/960984): Fix memory leaks in tests and re-enable on LSAN.
20 #ifdef LEAK_SANITIZER
21 #define MAYBE_PausableTasks DISABLED_PausableTasks
22 #define MAYBE_NestedPauseHandlesTasks DISABLED_NestedPauseHandlesTasks
23 #else
24 #define MAYBE_PausableTasks PausableTasks
25 #define MAYBE_NestedPauseHandlesTasks NestedPauseHandlesTasks
26 #endif
27 
28 using testing::ElementsAre;
29 using testing::ElementsAreArray;
30 
31 namespace blink {
32 namespace scheduler {
33 // To avoid symbol collisions in jumbo builds.
34 namespace worker_scheduler_unittest {
35 
AppendToVectorTestTask(Vector<String> * vector,String value)36 void AppendToVectorTestTask(Vector<String>* vector, String value) {
37   vector->push_back(value);
38 }
39 
RunChainedTask(scoped_refptr<base::sequence_manager::TaskQueue> task_queue,int count,base::TimeDelta duration,scoped_refptr<base::TestMockTimeTaskRunner> environment,Vector<base::TimeTicks> * tasks)40 void RunChainedTask(scoped_refptr<base::sequence_manager::TaskQueue> task_queue,
41                     int count,
42                     base::TimeDelta duration,
43                     scoped_refptr<base::TestMockTimeTaskRunner> environment,
44                     Vector<base::TimeTicks>* tasks) {
45   tasks->push_back(environment->GetMockTickClock()->NowTicks());
46 
47   environment->AdvanceMockTickClock(duration);
48 
49   if (count == 1)
50     return;
51 
52   // Add a delay of 50ms to ensure that wake-up based throttling does not affect
53   // us.
54   task_queue->task_runner()->PostDelayedTask(
55       FROM_HERE,
56       base::BindOnce(&RunChainedTask, task_queue, count - 1, duration,
57                      environment, base::Unretained(tasks)),
58       base::TimeDelta::FromMilliseconds(50));
59 }
60 
61 class WorkerThreadSchedulerForTest : public WorkerThreadScheduler {
62  public:
63   // |manager| and |proxy| must remain valid for the entire lifetime of this
64   // object.
WorkerThreadSchedulerForTest(ThreadType thread_type,base::sequence_manager::SequenceManager * manager,WorkerSchedulerProxy * proxy)65   WorkerThreadSchedulerForTest(ThreadType thread_type,
66                                base::sequence_manager::SequenceManager* manager,
67                                WorkerSchedulerProxy* proxy)
68       : WorkerThreadScheduler(thread_type, manager, proxy) {}
69 
worker_schedulers()70   const HashSet<WorkerScheduler*>& worker_schedulers() {
71     return GetWorkerSchedulersForTesting();
72   }
73 
74   using WorkerThreadScheduler::CreateTaskQueueThrottler;
75   using WorkerThreadScheduler::SetCPUTimeBudgetPoolForTesting;
76 };
77 
78 class WorkerSchedulerForTest : public WorkerScheduler {
79  public:
WorkerSchedulerForTest(WorkerThreadSchedulerForTest * thread_scheduler)80   explicit WorkerSchedulerForTest(
81       WorkerThreadSchedulerForTest* thread_scheduler)
82       : WorkerScheduler(thread_scheduler, nullptr) {}
83 
84   using WorkerScheduler::ThrottleableTaskQueue;
85   using WorkerScheduler::UnpausableTaskQueue;
86 };
87 
88 class WorkerSchedulerTest : public testing::Test {
89  public:
WorkerSchedulerTest()90   WorkerSchedulerTest()
91       : mock_task_runner_(new base::TestMockTimeTaskRunner()),
92         sequence_manager_(
93             base::sequence_manager::SequenceManagerForTest::Create(
94                 nullptr,
95                 mock_task_runner_,
96                 mock_task_runner_->GetMockTickClock())),
97         scheduler_(new WorkerThreadSchedulerForTest(ThreadType::kTestThread,
98                                                     sequence_manager_.get(),
99                                                     nullptr /* proxy */)) {
100     mock_task_runner_->AdvanceMockTickClock(
101         base::TimeDelta::FromMicroseconds(5000));
102   }
103 
104   ~WorkerSchedulerTest() override = default;
105 
SetUp()106   void SetUp() override {
107     scheduler_->Init();
108     worker_scheduler_ =
109         std::make_unique<WorkerSchedulerForTest>(scheduler_.get());
110   }
111 
TearDown()112   void TearDown() override {
113     if (worker_scheduler_) {
114       worker_scheduler_->Dispose();
115       worker_scheduler_.reset();
116     }
117   }
118 
GetClock()119   const base::TickClock* GetClock() {
120     return mock_task_runner_->GetMockTickClock();
121   }
122 
RunUntilIdle()123   void RunUntilIdle() { mock_task_runner_->FastForwardUntilNoTasksRemain(); }
124 
125   // Helper for posting a task.
PostTestTask(Vector<String> * run_order,const String & task_descriptor,TaskType task_type)126   void PostTestTask(Vector<String>* run_order,
127                     const String& task_descriptor,
128                     TaskType task_type) {
129     worker_scheduler_->GetTaskRunner(task_type)->PostTask(
130         FROM_HERE, WTF::Bind(&AppendToVectorTestTask,
131                              WTF::Unretained(run_order), task_descriptor));
132   }
133 
134  protected:
135   scoped_refptr<base::TestMockTimeTaskRunner> mock_task_runner_;
136   std::unique_ptr<base::sequence_manager::SequenceManagerForTest>
137       sequence_manager_;
138   std::unique_ptr<WorkerThreadSchedulerForTest> scheduler_;
139   std::unique_ptr<WorkerSchedulerForTest> worker_scheduler_;
140 
141   DISALLOW_COPY_AND_ASSIGN(WorkerSchedulerTest);
142 };
143 
TEST_F(WorkerSchedulerTest,TestPostTasks)144 TEST_F(WorkerSchedulerTest, TestPostTasks) {
145   Vector<String> run_order;
146   PostTestTask(&run_order, "T1", TaskType::kInternalTest);
147   PostTestTask(&run_order, "T2", TaskType::kInternalTest);
148   RunUntilIdle();
149   PostTestTask(&run_order, "T3", TaskType::kInternalTest);
150   RunUntilIdle();
151   EXPECT_THAT(run_order, testing::ElementsAre("T1", "T2", "T3"));
152 
153   // Tasks should not run after the scheduler is disposed of.
154   worker_scheduler_->Dispose();
155   run_order.clear();
156   PostTestTask(&run_order, "T4", TaskType::kInternalTest);
157   PostTestTask(&run_order, "T5", TaskType::kInternalTest);
158   RunUntilIdle();
159   EXPECT_TRUE(run_order.IsEmpty());
160 
161   worker_scheduler_.reset();
162 }
163 
TEST_F(WorkerSchedulerTest,RegisterWorkerSchedulers)164 TEST_F(WorkerSchedulerTest, RegisterWorkerSchedulers) {
165   EXPECT_THAT(scheduler_->worker_schedulers(),
166               testing::ElementsAre(worker_scheduler_.get()));
167 
168   std::unique_ptr<WorkerSchedulerForTest> worker_scheduler2 =
169       std::make_unique<WorkerSchedulerForTest>(scheduler_.get());
170 
171   EXPECT_THAT(scheduler_->worker_schedulers(),
172               testing::UnorderedElementsAre(worker_scheduler_.get(),
173                                             worker_scheduler2.get()));
174 
175   worker_scheduler_->Dispose();
176   worker_scheduler_.reset();
177 
178   EXPECT_THAT(scheduler_->worker_schedulers(),
179               testing::ElementsAre(worker_scheduler2.get()));
180 
181   worker_scheduler2->Dispose();
182 
183   EXPECT_THAT(scheduler_->worker_schedulers(), testing::ElementsAre());
184 }
185 
TEST_F(WorkerSchedulerTest,ThrottleWorkerScheduler)186 TEST_F(WorkerSchedulerTest, ThrottleWorkerScheduler) {
187   scheduler_->CreateTaskQueueThrottler();
188 
189   EXPECT_FALSE(scheduler_->task_queue_throttler()->IsThrottled(
190       worker_scheduler_->ThrottleableTaskQueue().get()));
191 
192   scheduler_->OnLifecycleStateChanged(SchedulingLifecycleState::kThrottled);
193   EXPECT_TRUE(scheduler_->task_queue_throttler()->IsThrottled(
194       worker_scheduler_->ThrottleableTaskQueue().get()));
195 
196   scheduler_->OnLifecycleStateChanged(SchedulingLifecycleState::kThrottled);
197   EXPECT_TRUE(scheduler_->task_queue_throttler()->IsThrottled(
198       worker_scheduler_->ThrottleableTaskQueue().get()));
199 
200   // Ensure that two calls with kThrottled do not mess with throttling
201   // refcount.
202   scheduler_->OnLifecycleStateChanged(SchedulingLifecycleState::kNotThrottled);
203   EXPECT_FALSE(scheduler_->task_queue_throttler()->IsThrottled(
204       worker_scheduler_->ThrottleableTaskQueue().get()));
205 }
206 
TEST_F(WorkerSchedulerTest,ThrottleWorkerScheduler_CreateThrottled)207 TEST_F(WorkerSchedulerTest, ThrottleWorkerScheduler_CreateThrottled) {
208   scheduler_->CreateTaskQueueThrottler();
209 
210   scheduler_->OnLifecycleStateChanged(SchedulingLifecycleState::kThrottled);
211 
212   std::unique_ptr<WorkerSchedulerForTest> worker_scheduler2 =
213       std::make_unique<WorkerSchedulerForTest>(scheduler_.get());
214 
215   // Ensure that newly created scheduler is throttled.
216   EXPECT_TRUE(scheduler_->task_queue_throttler()->IsThrottled(
217       worker_scheduler2->ThrottleableTaskQueue().get()));
218 
219   worker_scheduler2->Dispose();
220 }
221 
TEST_F(WorkerSchedulerTest,ThrottleWorkerScheduler_RunThrottledTasks)222 TEST_F(WorkerSchedulerTest, ThrottleWorkerScheduler_RunThrottledTasks) {
223   scheduler_->CreateTaskQueueThrottler();
224   scheduler_->SetCPUTimeBudgetPoolForTesting(nullptr);
225 
226   // Create a new |worker_scheduler| to ensure that it's properly initialised.
227   worker_scheduler_->Dispose();
228   worker_scheduler_ =
229       std::make_unique<WorkerSchedulerForTest>(scheduler_.get());
230 
231   scheduler_->OnLifecycleStateChanged(SchedulingLifecycleState::kThrottled);
232 
233   Vector<base::TimeTicks> tasks;
234 
235   worker_scheduler_->ThrottleableTaskQueue()->task_runner()->PostTask(
236       FROM_HERE, base::BindOnce(&RunChainedTask,
237                                 worker_scheduler_->ThrottleableTaskQueue(), 5,
238                                 base::TimeDelta(), mock_task_runner_,
239                                 base::Unretained(&tasks)));
240 
241   RunUntilIdle();
242 
243   EXPECT_THAT(tasks,
244               ElementsAre(base::TimeTicks() + base::TimeDelta::FromSeconds(1),
245                           base::TimeTicks() + base::TimeDelta::FromSeconds(2),
246                           base::TimeTicks() + base::TimeDelta::FromSeconds(3),
247                           base::TimeTicks() + base::TimeDelta::FromSeconds(4),
248                           base::TimeTicks() + base::TimeDelta::FromSeconds(5)));
249 }
250 
TEST_F(WorkerSchedulerTest,ThrottleWorkerScheduler_RunThrottledTasks_CPUBudget)251 TEST_F(WorkerSchedulerTest,
252        ThrottleWorkerScheduler_RunThrottledTasks_CPUBudget) {
253   scheduler_->CreateTaskQueueThrottler();
254 
255   scheduler_->cpu_time_budget_pool()->SetTimeBudgetRecoveryRate(
256       GetClock()->NowTicks(), 0.01);
257 
258   // Create a new |worker_scheduler| to ensure that it's properly initialised.
259   worker_scheduler_->Dispose();
260   worker_scheduler_ =
261       std::make_unique<WorkerSchedulerForTest>(scheduler_.get());
262 
263   scheduler_->OnLifecycleStateChanged(SchedulingLifecycleState::kThrottled);
264 
265   Vector<base::TimeTicks> tasks;
266 
267   worker_scheduler_->ThrottleableTaskQueue()->task_runner()->PostTask(
268       FROM_HERE, base::BindOnce(&RunChainedTask,
269                                 worker_scheduler_->ThrottleableTaskQueue(), 5,
270                                 base::TimeDelta::FromMilliseconds(100),
271                                 mock_task_runner_, base::Unretained(&tasks)));
272 
273   RunUntilIdle();
274 
275   EXPECT_THAT(
276       tasks, ElementsAre(base::TimeTicks() + base::TimeDelta::FromSeconds(1),
277                          base::TimeTicks() + base::TimeDelta::FromSeconds(11),
278                          base::TimeTicks() + base::TimeDelta::FromSeconds(21),
279                          base::TimeTicks() + base::TimeDelta::FromSeconds(31),
280                          base::TimeTicks() + base::TimeDelta::FromSeconds(41)));
281 }
282 
TEST_F(WorkerSchedulerTest,MAYBE_PausableTasks)283 TEST_F(WorkerSchedulerTest, MAYBE_PausableTasks) {
284   Vector<String> run_order;
285   auto pause_handle = worker_scheduler_->Pause();
286   // Tests interlacing pausable, throttable and unpausable tasks and
287   // ensures that the pausable & throttable tasks don't run when paused.
288   // Throttable
289   PostTestTask(&run_order, "T1", TaskType::kJavascriptTimerDelayedLowNesting);
290   // Pausable
291   PostTestTask(&run_order, "T2", TaskType::kNetworking);
292   // Unpausable
293   PostTestTask(&run_order, "T3", TaskType::kInternalTest);
294   RunUntilIdle();
295   EXPECT_THAT(run_order, testing::ElementsAre("T3"));
296   pause_handle.reset();
297   RunUntilIdle();
298 
299   EXPECT_THAT(run_order, testing::ElementsAre("T3", "T1", "T2"));
300 }
301 
TEST_F(WorkerSchedulerTest,MAYBE_NestedPauseHandlesTasks)302 TEST_F(WorkerSchedulerTest, MAYBE_NestedPauseHandlesTasks) {
303   Vector<String> run_order;
304   auto pause_handle = worker_scheduler_->Pause();
305   {
306     auto pause_handle2 = worker_scheduler_->Pause();
307     PostTestTask(&run_order, "T1", TaskType::kJavascriptTimerDelayedLowNesting);
308     PostTestTask(&run_order, "T2", TaskType::kNetworking);
309   }
310   RunUntilIdle();
311   EXPECT_EQ(0u, run_order.size());
312   pause_handle.reset();
313   RunUntilIdle();
314   EXPECT_THAT(run_order, testing::ElementsAre("T1", "T2"));
315 }
316 
317 }  // namespace worker_scheduler_unittest
318 }  // namespace scheduler
319 }  // namespace blink
320