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