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 "third_party/blink/public/common/features.h"
8 #include "third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler.h"
9 #include "third_party/blink/renderer/platform/scheduler/common/throttling/wake_up_budget_pool.h"
10 #include "third_party/blink/renderer/platform/scheduler/worker/worker_scheduler_proxy.h"
11 #include "third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.h"
12
13 namespace blink {
14 namespace scheduler {
15
PauseHandle(base::WeakPtr<WorkerScheduler> scheduler)16 WorkerScheduler::PauseHandle::PauseHandle(
17 base::WeakPtr<WorkerScheduler> scheduler)
18 : scheduler_(scheduler) {
19 scheduler_->PauseImpl();
20 }
21
~PauseHandle()22 WorkerScheduler::PauseHandle::~PauseHandle() {
23 if (scheduler_)
24 scheduler_->ResumeImpl();
25 }
26
WorkerScheduler(WorkerThreadScheduler * worker_thread_scheduler,WorkerSchedulerProxy * proxy)27 WorkerScheduler::WorkerScheduler(WorkerThreadScheduler* worker_thread_scheduler,
28 WorkerSchedulerProxy* proxy)
29 : throttleable_task_queue_(
30 worker_thread_scheduler->CreateTaskQueue("worker_throttleable_tq")),
31 pausable_task_queue_(
32 worker_thread_scheduler->CreateTaskQueue("worker_pausable_tq")),
33 unpausable_task_queue_(
34 worker_thread_scheduler->CreateTaskQueue("worker_unpausable_tq")),
35 thread_scheduler_(worker_thread_scheduler) {
36 task_runners_.emplace(throttleable_task_queue_,
37 throttleable_task_queue_->CreateQueueEnabledVoter());
38 task_runners_.emplace(pausable_task_queue_,
39 pausable_task_queue_->CreateQueueEnabledVoter());
40 task_runners_.emplace(unpausable_task_queue_, nullptr);
41
42 thread_scheduler_->RegisterWorkerScheduler(this);
43
44 SetUpThrottling();
45
46 // |proxy| can be nullptr in unit tests.
47 if (proxy)
48 proxy->OnWorkerSchedulerCreated(GetWeakPtr());
49 }
50
GetWeakPtr()51 base::WeakPtr<WorkerScheduler> WorkerScheduler::GetWeakPtr() {
52 return weak_factory_.GetWeakPtr();
53 }
54
~WorkerScheduler()55 WorkerScheduler::~WorkerScheduler() {
56 DCHECK(is_disposed_);
57 DCHECK_EQ(0u, paused_count_);
58 }
59
Pause()60 std::unique_ptr<WorkerScheduler::PauseHandle> WorkerScheduler::Pause() {
61 thread_scheduler_->helper()->CheckOnValidThread();
62 if (is_disposed_)
63 return nullptr;
64 return std::make_unique<PauseHandle>(GetWeakPtr());
65 }
66
PauseImpl()67 void WorkerScheduler::PauseImpl() {
68 thread_scheduler_->helper()->CheckOnValidThread();
69 paused_count_++;
70 if (paused_count_ == 1) {
71 for (const auto& pair : task_runners_) {
72 if (pair.second) {
73 pair.second->SetVoteToEnable(false);
74 }
75 }
76 }
77 }
78
ResumeImpl()79 void WorkerScheduler::ResumeImpl() {
80 thread_scheduler_->helper()->CheckOnValidThread();
81 paused_count_--;
82 if (paused_count_ == 0 && !is_disposed_) {
83 for (const auto& pair : task_runners_) {
84 if (pair.second) {
85 pair.second->SetVoteToEnable(true);
86 }
87 }
88 }
89 }
90
SetUpThrottling()91 void WorkerScheduler::SetUpThrottling() {
92 if (!thread_scheduler_->task_queue_throttler())
93 return;
94 base::TimeTicks now = thread_scheduler_->GetTickClock()->NowTicks();
95
96 WakeUpBudgetPool* wake_up_budget_pool =
97 thread_scheduler_->wake_up_budget_pool();
98 CPUTimeBudgetPool* cpu_time_budget_pool =
99 thread_scheduler_->cpu_time_budget_pool();
100
101 DCHECK(wake_up_budget_pool || cpu_time_budget_pool)
102 << "At least one budget pool should be present";
103
104 if (wake_up_budget_pool) {
105 wake_up_budget_pool->AddQueue(now, throttleable_task_queue_.get());
106 }
107 if (cpu_time_budget_pool) {
108 cpu_time_budget_pool->AddQueue(now, throttleable_task_queue_.get());
109 }
110 }
111
CalculateLifecycleState(ObserverType) const112 SchedulingLifecycleState WorkerScheduler::CalculateLifecycleState(
113 ObserverType) const {
114 return thread_scheduler_->lifecycle_state();
115 }
116
Dispose()117 void WorkerScheduler::Dispose() {
118 if (TaskQueueThrottler* throttler =
119 thread_scheduler_->task_queue_throttler()) {
120 throttler->ShutdownTaskQueue(throttleable_task_queue_.get());
121 }
122
123 thread_scheduler_->UnregisterWorkerScheduler(this);
124
125 for (const auto& pair : task_runners_) {
126 pair.first->ShutdownTaskQueue();
127 }
128
129 task_runners_.clear();
130
131 is_disposed_ = true;
132 }
133
GetTaskRunner(TaskType type) const134 scoped_refptr<base::SingleThreadTaskRunner> WorkerScheduler::GetTaskRunner(
135 TaskType type) const {
136 switch (type) {
137 case TaskType::kJavascriptTimerImmediate:
138 case TaskType::kJavascriptTimerDelayedLowNesting:
139 case TaskType::kJavascriptTimerDelayedHighNesting:
140 case TaskType::kPostedMessage:
141 case TaskType::kWorkerAnimation:
142 return throttleable_task_queue_->CreateTaskRunner(type);
143 case TaskType::kDOMManipulation:
144 case TaskType::kUserInteraction:
145 case TaskType::kNetworking:
146 case TaskType::kNetworkingWithURLLoaderAnnotation:
147 case TaskType::kNetworkingControl:
148 case TaskType::kHistoryTraversal:
149 case TaskType::kEmbed:
150 case TaskType::kMediaElementEvent:
151 case TaskType::kCanvasBlobSerialization:
152 case TaskType::kMicrotask:
153 case TaskType::kRemoteEvent:
154 case TaskType::kWebSocket:
155 case TaskType::kUnshippedPortMessage:
156 case TaskType::kFileReading:
157 case TaskType::kDatabaseAccess:
158 case TaskType::kPresentation:
159 case TaskType::kSensor:
160 case TaskType::kPerformanceTimeline:
161 case TaskType::kWebGL:
162 case TaskType::kIdleTask:
163 case TaskType::kMiscPlatformAPI:
164 case TaskType::kFontLoading:
165 case TaskType::kApplicationLifeCycle:
166 case TaskType::kBackgroundFetch:
167 case TaskType::kPermission:
168 case TaskType::kInternalDefault:
169 case TaskType::kInternalLoading:
170 case TaskType::kInternalWebCrypto:
171 case TaskType::kInternalMedia:
172 case TaskType::kInternalMediaRealTime:
173 case TaskType::kInternalUserInteraction:
174 case TaskType::kInternalIntersectionObserver:
175 case TaskType::kInternalNavigationAssociated:
176 case TaskType::kInternalContinueScriptLoading:
177 // UnthrottledTaskRunner is generally discouraged in future.
178 // TODO(nhiroki): Identify which tasks can be throttled / suspendable and
179 // move them into other task runners. See also comments in
180 // Get(LocalFrame). (https://crbug.com/670534)
181 return pausable_task_queue_->CreateTaskRunner(type);
182 case TaskType::kDeprecatedNone:
183 case TaskType::kInternalInspector:
184 case TaskType::kInternalTest:
185 case TaskType::kInternalNavigationAssociatedUnfreezable:
186 // kWebLocks can be frozen if for entire page, but not for individual
187 // frames. See https://crrev.com/c/1687716
188 case TaskType::kWebLocks:
189 // UnthrottledTaskRunner is generally discouraged in future.
190 // TODO(nhiroki): Identify which tasks can be throttled / suspendable and
191 // move them into other task runners. See also comments in
192 // Get(LocalFrame). (https://crbug.com/670534)
193 return unpausable_task_queue_->CreateTaskRunner(type);
194 case TaskType::kNetworkingUnfreezable:
195 return base::FeatureList::IsEnabled(features::kLoadingTasksUnfreezable)
196 ? unpausable_task_queue_->CreateTaskRunner(type)
197 : pausable_task_queue_->CreateTaskRunner(type);
198 case TaskType::kMainThreadTaskQueueV8:
199 case TaskType::kMainThreadTaskQueueCompositor:
200 case TaskType::kMainThreadTaskQueueDefault:
201 case TaskType::kMainThreadTaskQueueInput:
202 case TaskType::kMainThreadTaskQueueIdle:
203 case TaskType::kMainThreadTaskQueueControl:
204 case TaskType::kMainThreadTaskQueueMemoryPurge:
205 case TaskType::kMainThreadTaskQueueNonWaking:
206 case TaskType::kCompositorThreadTaskQueueDefault:
207 case TaskType::kCompositorThreadTaskQueueInput:
208 case TaskType::kWorkerThreadTaskQueueDefault:
209 case TaskType::kWorkerThreadTaskQueueV8:
210 case TaskType::kWorkerThreadTaskQueueCompositor:
211 case TaskType::kInternalTranslation:
212 case TaskType::kServiceWorkerClientMessage:
213 case TaskType::kInternalContentCapture:
214 case TaskType::kExperimentalWebScheduling:
215 case TaskType::kInternalFrameLifecycleControl:
216 case TaskType::kInternalFindInPage:
217 case TaskType::kInternalHighPriorityLocalFrame:
218 case TaskType::kMainThreadTaskQueueIPCTracking:
219 case TaskType::kCount:
220 NOTREACHED();
221 break;
222 }
223 NOTREACHED();
224 return nullptr;
225 }
226
OnLifecycleStateChanged(SchedulingLifecycleState lifecycle_state)227 void WorkerScheduler::OnLifecycleStateChanged(
228 SchedulingLifecycleState lifecycle_state) {
229 if (lifecycle_state_ == lifecycle_state)
230 return;
231 lifecycle_state_ = lifecycle_state;
232 thread_scheduler_->OnLifecycleStateChanged(lifecycle_state);
233
234 if (TaskQueueThrottler* throttler =
235 thread_scheduler_->task_queue_throttler()) {
236 if (lifecycle_state_ == SchedulingLifecycleState::kThrottled) {
237 throttler->IncreaseThrottleRefCount(throttleable_task_queue_.get());
238 } else {
239 throttler->DecreaseThrottleRefCount(throttleable_task_queue_.get());
240 }
241 }
242 NotifyLifecycleObservers();
243 }
244
UnpausableTaskQueue()245 scoped_refptr<NonMainThreadTaskQueue> WorkerScheduler::UnpausableTaskQueue() {
246 return unpausable_task_queue_.get();
247 }
248
PausableTaskQueue()249 scoped_refptr<NonMainThreadTaskQueue> WorkerScheduler::PausableTaskQueue() {
250 return pausable_task_queue_.get();
251 }
252
ThrottleableTaskQueue()253 scoped_refptr<NonMainThreadTaskQueue> WorkerScheduler::ThrottleableTaskQueue() {
254 return throttleable_task_queue_.get();
255 }
256
OnStartedUsingFeature(SchedulingPolicy::Feature feature,const SchedulingPolicy & policy)257 void WorkerScheduler::OnStartedUsingFeature(SchedulingPolicy::Feature feature,
258 const SchedulingPolicy& policy) {}
259
OnStoppedUsingFeature(SchedulingPolicy::Feature feature,const SchedulingPolicy & policy)260 void WorkerScheduler::OnStoppedUsingFeature(SchedulingPolicy::Feature feature,
261 const SchedulingPolicy& policy) {}
262
263 } // namespace scheduler
264 } // namespace blink
265