summaryrefslogtreecommitdiff
path: root/chromium/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.cc')
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.cc400
1 files changed, 400 insertions, 0 deletions
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.cc b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.cc
new file mode 100644
index 00000000000..c105a49f2f9
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.cc
@@ -0,0 +1,400 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.h"
+
+#include "base/logging.h"
+#include "base/metrics/field_trial_params.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/stringprintf.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/scheduler/base/virtual_time_domain.h"
+#include "third_party/blink/renderer/platform/scheduler/child/default_params.h"
+#include "third_party/blink/renderer/platform/scheduler/common/throttling/budget_pool.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h"
+#include "third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h"
+#include "third_party/blink/renderer/platform/scheduler/renderer/auto_advancing_virtual_time_domain.h"
+
+namespace blink {
+namespace scheduler {
+
+namespace {
+
+constexpr double kDefaultBackgroundBudgetAsCPUFraction = .01;
+constexpr double kDefaultMaxBackgroundBudgetLevelInSeconds = 3;
+constexpr double kDefaultInitialBackgroundBudgetInSeconds = 1;
+constexpr double kDefaultMaxBackgroundThrottlingDelayInSeconds = 0;
+
+// Given that we already align timers to 1Hz, do not report throttling if
+// it is under 3s.
+constexpr base::TimeDelta kMinimalBackgroundThrottlingDurationToReport =
+ base::TimeDelta::FromSeconds(3);
+
+// Values coming from the field trial config are interpreted as follows:
+// -1 is "not set". Scheduler should use a reasonable default.
+// 0 corresponds to base::nullopt.
+// Other values are left without changes.
+
+struct BackgroundThrottlingSettings {
+ double budget_recovery_rate;
+ base::Optional<base::TimeDelta> max_budget_level;
+ base::Optional<base::TimeDelta> max_throttling_delay;
+ base::Optional<base::TimeDelta> initial_budget;
+};
+
+double GetDoubleParameterFromMap(
+ const std::map<std::string, std::string>& settings,
+ const std::string& setting_name,
+ double default_value) {
+ const auto& find_it = settings.find(setting_name);
+ if (find_it == settings.end())
+ return default_value;
+ double parsed_value;
+ if (!base::StringToDouble(find_it->second, &parsed_value))
+ return default_value;
+ if (parsed_value == -1)
+ return default_value;
+ return parsed_value;
+}
+
+base::Optional<base::TimeDelta> DoubleToOptionalTime(double value) {
+ if (value == 0)
+ return base::nullopt;
+ return base::TimeDelta::FromSecondsD(value);
+}
+
+BackgroundThrottlingSettings GetBackgroundThrottlingSettings() {
+ std::map<std::string, std::string> background_throttling_settings;
+ base::GetFieldTrialParams("ExpensiveBackgroundTimerThrottling",
+ &background_throttling_settings);
+
+ BackgroundThrottlingSettings settings;
+
+ settings.budget_recovery_rate =
+ GetDoubleParameterFromMap(background_throttling_settings, "cpu_budget",
+ kDefaultBackgroundBudgetAsCPUFraction);
+
+ settings.max_budget_level = DoubleToOptionalTime(
+ GetDoubleParameterFromMap(background_throttling_settings, "max_budget",
+ kDefaultMaxBackgroundBudgetLevelInSeconds));
+
+ settings.max_throttling_delay = DoubleToOptionalTime(
+ GetDoubleParameterFromMap(background_throttling_settings, "max_delay",
+ kDefaultMaxBackgroundThrottlingDelayInSeconds));
+
+ settings.initial_budget = DoubleToOptionalTime(GetDoubleParameterFromMap(
+ background_throttling_settings, "initial_budget",
+ kDefaultInitialBackgroundBudgetInSeconds));
+
+ return settings;
+}
+
+} // namespace
+
+PageSchedulerImpl::PageSchedulerImpl(
+ PageScheduler::Delegate* delegate,
+ MainThreadSchedulerImpl* main_thread_scheduler,
+ bool disable_background_timer_throttling)
+ : main_thread_scheduler_(main_thread_scheduler),
+ page_visibility_(kDefaultPageVisibility),
+ disable_background_timer_throttling_(disable_background_timer_throttling),
+ is_audio_playing_(false),
+ is_frozen_(false),
+ reported_background_throttling_since_navigation_(false),
+ has_active_connection_(false),
+ nested_runloop_(false),
+ is_main_frame_local_(false),
+ background_time_budget_pool_(nullptr),
+ delegate_(delegate),
+ weak_factory_(this) {
+ main_thread_scheduler->AddPageScheduler(this);
+}
+
+PageSchedulerImpl::~PageSchedulerImpl() {
+ // TODO(alexclarke): Find out why we can't rely on the web view outliving the
+ // frame.
+ for (FrameSchedulerImpl* frame_scheduler : frame_schedulers_) {
+ frame_scheduler->DetachFromPageScheduler();
+ }
+ main_thread_scheduler_->RemovePageScheduler(this);
+
+ if (background_time_budget_pool_)
+ background_time_budget_pool_->Close();
+}
+
+void PageSchedulerImpl::SetPageVisible(bool page_visible) {
+ PageVisibilityState page_visibility = page_visible
+ ? PageVisibilityState::kVisible
+ : PageVisibilityState::kHidden;
+
+ if (disable_background_timer_throttling_ ||
+ page_visibility_ == page_visibility)
+ return;
+
+ page_visibility_ = page_visibility;
+
+ UpdateBackgroundThrottlingState();
+
+ // Visible pages should not be frozen.
+ if (page_visibility_ == PageVisibilityState::kVisible && is_frozen_)
+ SetPageFrozen(false);
+}
+
+void PageSchedulerImpl::SetPageFrozen(bool frozen) {
+ if (is_frozen_ == frozen)
+ return;
+ is_frozen_ = frozen;
+ for (FrameSchedulerImpl* frame_scheduler : frame_schedulers_)
+ frame_scheduler->SetPageFrozen(frozen);
+ if (delegate_)
+ delegate_->SetPageFrozen(frozen);
+}
+
+void PageSchedulerImpl::SetKeepActive(bool keep_active) {
+ for (FrameSchedulerImpl* frame_scheduler : frame_schedulers_)
+ frame_scheduler->SetKeepActive(keep_active);
+}
+
+bool PageSchedulerImpl::IsMainFrameLocal() const {
+ return is_main_frame_local_;
+}
+
+void PageSchedulerImpl::SetIsMainFrameLocal(bool is_local) {
+ is_main_frame_local_ = is_local;
+}
+
+std::unique_ptr<FrameSchedulerImpl> PageSchedulerImpl::CreateFrameSchedulerImpl(
+ base::trace_event::BlameContext* blame_context,
+ FrameScheduler::FrameType frame_type) {
+ MaybeInitializeBackgroundCPUTimeBudgetPool();
+ std::unique_ptr<FrameSchedulerImpl> frame_scheduler(new FrameSchedulerImpl(
+ main_thread_scheduler_, this, blame_context, frame_type));
+ frame_scheduler->SetPageVisibility(page_visibility_);
+ frame_schedulers_.insert(frame_scheduler.get());
+ return frame_scheduler;
+}
+
+std::unique_ptr<blink::FrameScheduler> PageSchedulerImpl::CreateFrameScheduler(
+ blink::BlameContext* blame_context,
+ FrameScheduler::FrameType frame_type) {
+ return CreateFrameSchedulerImpl(blame_context, frame_type);
+}
+
+void PageSchedulerImpl::Unregister(FrameSchedulerImpl* frame_scheduler) {
+ DCHECK(frame_schedulers_.find(frame_scheduler) != frame_schedulers_.end());
+ frame_schedulers_.erase(frame_scheduler);
+}
+
+void PageSchedulerImpl::OnNavigation() {
+ reported_background_throttling_since_navigation_ = false;
+}
+
+void PageSchedulerImpl::ReportIntervention(const std::string& message) {
+ delegate_->ReportIntervention(String::FromUTF8(message.c_str()));
+}
+
+base::TimeTicks PageSchedulerImpl::EnableVirtualTime() {
+ return main_thread_scheduler_->EnableVirtualTime(
+ MainThreadSchedulerImpl::BaseTimeOverridePolicy::DO_NOT_OVERRIDE);
+}
+
+void PageSchedulerImpl::DisableVirtualTimeForTesting() {
+ main_thread_scheduler_->DisableVirtualTimeForTesting();
+}
+
+void PageSchedulerImpl::SetVirtualTimePolicy(VirtualTimePolicy policy) {
+ main_thread_scheduler_->SetVirtualTimePolicy(policy);
+}
+
+void PageSchedulerImpl::SetInitialVirtualTimeOffset(base::TimeDelta offset) {
+ main_thread_scheduler_->SetInitialVirtualTimeOffset(offset);
+}
+
+bool PageSchedulerImpl::VirtualTimeAllowedToAdvance() const {
+ return main_thread_scheduler_->VirtualTimeAllowedToAdvance();
+}
+
+void PageSchedulerImpl::GrantVirtualTimeBudget(
+ base::TimeDelta budget,
+ base::OnceClosure budget_exhausted_callback) {
+ main_thread_scheduler_->VirtualTimeControlTaskQueue()->PostDelayedTask(
+ FROM_HERE, std::move(budget_exhausted_callback), budget);
+ // This can shift time forwards if there's a pending MaybeAdvanceVirtualTime,
+ // so it's important this is called second.
+ main_thread_scheduler_->GetVirtualTimeDomain()->SetVirtualTimeFence(
+ main_thread_scheduler_->GetVirtualTimeDomain()->Now() + budget);
+}
+
+void PageSchedulerImpl::AddVirtualTimeObserver(VirtualTimeObserver* observer) {
+ main_thread_scheduler_->AddVirtualTimeObserver(observer);
+}
+
+void PageSchedulerImpl::RemoveVirtualTimeObserver(
+ VirtualTimeObserver* observer) {
+ main_thread_scheduler_->RemoveVirtualTimeObserver(observer);
+}
+
+void PageSchedulerImpl::AudioStateChanged(bool is_audio_playing) {
+ is_audio_playing_ = is_audio_playing;
+ main_thread_scheduler_->OnAudioStateChanged();
+}
+
+bool PageSchedulerImpl::IsExemptFromBudgetBasedThrottling() const {
+ return has_active_connection_;
+}
+
+bool PageSchedulerImpl::HasActiveConnectionForTest() const {
+ return has_active_connection_;
+}
+
+void PageSchedulerImpl::RequestBeginMainFrameNotExpected(bool new_state) {
+ delegate_->RequestBeginMainFrameNotExpected(new_state);
+}
+
+bool PageSchedulerImpl::IsPlayingAudio() const {
+ return is_audio_playing_;
+}
+
+bool PageSchedulerImpl::IsFrozen() const {
+ return is_frozen_;
+}
+
+void PageSchedulerImpl::OnConnectionUpdated() {
+ bool has_active_connection = false;
+ for (FrameSchedulerImpl* frame_scheduler : frame_schedulers_) {
+ has_active_connection |= frame_scheduler->has_active_connection();
+ }
+
+ if (has_active_connection_ != has_active_connection) {
+ has_active_connection_ = has_active_connection;
+ UpdateBackgroundThrottlingState();
+ }
+}
+
+void PageSchedulerImpl::OnTraceLogEnabled() {
+ tracing_controller_.OnTraceLogEnabled();
+ for (FrameSchedulerImpl* frame_scheduler : frame_schedulers_) {
+ frame_scheduler->OnTraceLogEnabled();
+ }
+}
+
+void PageSchedulerImpl::AsValueInto(
+ base::trace_event::TracedValue* state) const {
+ state->SetBoolean("page_visible",
+ page_visibility_ == PageVisibilityState::kVisible);
+ state->SetBoolean("disable_background_timer_throttling",
+ disable_background_timer_throttling_);
+ state->SetBoolean("is_audio_playing", is_audio_playing_);
+ state->SetBoolean("is_frozen", is_frozen_);
+ state->SetBoolean("reported_background_throttling_since_navigation",
+ reported_background_throttling_since_navigation_);
+
+ state->BeginDictionary("frame_schedulers");
+ for (FrameSchedulerImpl* frame_scheduler : frame_schedulers_) {
+ state->BeginDictionaryWithCopiedName(PointerToString(frame_scheduler));
+ frame_scheduler->AsValueInto(state);
+ state->EndDictionary();
+ }
+ state->EndDictionary();
+}
+
+CPUTimeBudgetPool* PageSchedulerImpl::BackgroundCPUTimeBudgetPool() {
+ MaybeInitializeBackgroundCPUTimeBudgetPool();
+ return background_time_budget_pool_;
+}
+
+void PageSchedulerImpl::MaybeInitializeBackgroundCPUTimeBudgetPool() {
+ if (background_time_budget_pool_)
+ return;
+
+ if (!RuntimeEnabledFeatures::ExpensiveBackgroundTimerThrottlingEnabled())
+ return;
+
+ background_time_budget_pool_ =
+ main_thread_scheduler_->task_queue_throttler()->CreateCPUTimeBudgetPool(
+ "background");
+ LazyNow lazy_now(main_thread_scheduler_->tick_clock());
+
+ BackgroundThrottlingSettings settings = GetBackgroundThrottlingSettings();
+
+ background_time_budget_pool_->SetMaxBudgetLevel(lazy_now.Now(),
+ settings.max_budget_level);
+ background_time_budget_pool_->SetMaxThrottlingDelay(
+ lazy_now.Now(), settings.max_throttling_delay);
+
+ UpdateBackgroundThrottlingState();
+
+ background_time_budget_pool_->SetTimeBudgetRecoveryRate(
+ lazy_now.Now(), settings.budget_recovery_rate);
+
+ if (settings.initial_budget) {
+ background_time_budget_pool_->GrantAdditionalBudget(
+ lazy_now.Now(), settings.initial_budget.value());
+ }
+}
+
+void PageSchedulerImpl::OnThrottlingReported(
+ base::TimeDelta throttling_duration) {
+ if (throttling_duration < kMinimalBackgroundThrottlingDurationToReport)
+ return;
+
+ if (reported_background_throttling_since_navigation_)
+ return;
+ reported_background_throttling_since_navigation_ = true;
+
+ std::string message = base::StringPrintf(
+ "Timer tasks have taken too much time while the page was in the "
+ "background. "
+ "As a result, they have been deferred for %.3f seconds. "
+ "See https://www.chromestatus.com/feature/6172836527865856 "
+ "for more details",
+ throttling_duration.InSecondsF());
+
+ delegate_->ReportIntervention(String::FromUTF8(message.c_str()));
+}
+
+void PageSchedulerImpl::UpdateBackgroundThrottlingState() {
+ for (FrameSchedulerImpl* frame_scheduler : frame_schedulers_)
+ frame_scheduler->SetPageVisibility(page_visibility_);
+ UpdateBackgroundBudgetPoolThrottlingState();
+}
+
+void PageSchedulerImpl::UpdateBackgroundBudgetPoolThrottlingState() {
+ if (!background_time_budget_pool_)
+ return;
+
+ LazyNow lazy_now(main_thread_scheduler_->tick_clock());
+ if (page_visibility_ == PageVisibilityState::kVisible ||
+ has_active_connection_) {
+ background_time_budget_pool_->DisableThrottling(&lazy_now);
+ } else {
+ background_time_budget_pool_->EnableThrottling(&lazy_now);
+ }
+}
+
+size_t PageSchedulerImpl::FrameCount() const {
+ return frame_schedulers_.size();
+}
+
+void PageSchedulerImpl::SetMaxVirtualTimeTaskStarvationCount(
+ int max_task_starvation_count) {
+ main_thread_scheduler_->SetMaxVirtualTimeTaskStarvationCount(
+ max_task_starvation_count);
+}
+
+ukm::UkmRecorder* PageSchedulerImpl::GetUkmRecorder() {
+ if (!delegate_)
+ return nullptr;
+ return delegate_->GetUkmRecorder();
+}
+
+int64_t PageSchedulerImpl::GetUkmSourceId() {
+ if (!delegate_)
+ return 0;
+ return delegate_->GetUkmSourceId();
+}
+
+} // namespace scheduler
+} // namespace blink