blob: b43db9dcf07d395fb07d6f488ebb91fa6e8c9d0a [file] [log] [blame]
// Copyright 2017 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 "platform/loader/fetch/ResourceLoadScheduler.h"
#include "base/metrics/field_trial_params.h"
#include "base/metrics/histogram.h"
#include "base/strings/string_number_conversions.h"
#include "platform/Histogram.h"
#include "platform/loader/fetch/FetchContext.h"
#include "platform/runtime_enabled_features.h"
#include "platform/scheduler/renderer/frame_status.h"
#include "platform/scheduler/util/aggregated_metric_reporter.h"
#include "public/platform/Platform.h"
namespace blink {
namespace {
// Field trial name.
const char kResourceLoadSchedulerTrial[] = "ResourceLoadScheduler";
// Field trial parameter names.
// Note: bg_limit is supported on m61+, but bg_sub_limit is only on m63+.
// If bg_sub_limit param is not found, we should use bg_limit to make the
// study result statistically correct.
const char kOutstandingLimitForBackgroundMainFrameName[] = "bg_limit";
const char kOutstandingLimitForBackgroundSubFrameName[] = "bg_sub_limit";
// Field trial default parameters.
constexpr size_t kOutstandingLimitForBackgroundFrameDefault = 16u;
// Maximum request count that request count metrics assume.
constexpr base::HistogramBase::Sample kMaximumReportSize10K = 10000;
// Maximum traffic bytes that traffic metrics assume.
constexpr base::HistogramBase::Sample kMaximumReportSize1G =
1 * 1000 * 1000 * 1000;
// Bucket count for metrics.
constexpr int32_t kReportBucketCount = 25;
constexpr char kRendererSideResourceScheduler[] =
"RendererSideResourceScheduler";
// These values are copied from resource_scheduler.cc, but the meaning is a bit
// different because ResourceScheduler counts the running delayable requests
// while ResourceLoadScheduler counts all the running requests.
constexpr size_t kTightLimitForRendererSideResourceScheduler = 1u;
constexpr size_t kLimitForRendererSideResourceScheduler = 10u;
constexpr char kTightLimitForRendererSideResourceSchedulerName[] =
"tight_limit";
constexpr char kLimitForRendererSideResourceSchedulerName[] = "limit";
// Represents a resource load circumstance, e.g. from main frame vs sub-frames,
// or on throttled state vs on not-throttled state.
// Used to report histograms. Do not reorder or insert new items.
enum class ReportCircumstance {
kMainframeThrottled,
kMainframeNotThrottled,
kSubframeThrottled,
kSubframeNotThrottled,
// Append new items here.
kNumOfCircumstances,
};
base::HistogramBase::Sample ToSample(ReportCircumstance circumstance) {
return static_cast<base::HistogramBase::Sample>(circumstance);
}
uint32_t GetFieldTrialUint32Param(const char* trial_name,
const char* parameter_name,
uint32_t default_param) {
std::map<std::string, std::string> trial_params;
bool result = base::GetFieldTrialParams(trial_name, &trial_params);
if (!result)
return default_param;
const auto& found = trial_params.find(parameter_name);
if (found == trial_params.end())
return default_param;
uint32_t param;
if (!base::StringToUint(found->second, &param))
return default_param;
return param;
}
size_t GetOutstandingThrottledLimit(FetchContext* context) {
DCHECK(context);
if (!RuntimeEnabledFeatures::ResourceLoadSchedulerEnabled())
return ResourceLoadScheduler::kOutstandingUnlimited;
uint32_t main_frame_limit = GetFieldTrialUint32Param(
kResourceLoadSchedulerTrial, kOutstandingLimitForBackgroundMainFrameName,
kOutstandingLimitForBackgroundFrameDefault);
if (context->IsMainFrame())
return main_frame_limit;
// We do not have a fixed default limit for sub-frames, but use the limit for
// the main frame so that it works as how previous versions that haven't
// consider sub-frames' specific limit work.
return GetFieldTrialUint32Param(kResourceLoadSchedulerTrial,
kOutstandingLimitForBackgroundSubFrameName,
main_frame_limit);
}
int TakeWholeKilobytes(int64_t& bytes) {
int kilobytes = bytes / 1024;
bytes %= 1024;
return kilobytes;
}
} // namespace
// A class to gather throttling and traffic information to report histograms.
class ResourceLoadScheduler::TrafficMonitor {
public:
explicit TrafficMonitor(FetchContext*);
~TrafficMonitor();
// Notified when the ThrottlingState is changed.
void OnThrottlingStateChanged(WebFrameScheduler::ThrottlingState);
// Reports resource request completion.
void Report(const ResourceLoadScheduler::TrafficReportHints&);
// Reports per-frame reports.
void ReportAll();
private:
const bool is_main_frame_;
const WeakPersistent<FetchContext> context_; // NOT OWNED
WebFrameScheduler::ThrottlingState current_state_ =
WebFrameScheduler::ThrottlingState::kStopped;
size_t total_throttled_request_count_ = 0;
size_t total_throttled_traffic_bytes_ = 0;
size_t total_throttled_decoded_bytes_ = 0;
size_t total_not_throttled_request_count_ = 0;
size_t total_not_throttled_traffic_bytes_ = 0;
size_t total_not_throttled_decoded_bytes_ = 0;
size_t throttling_state_change_count_ = 0;
bool report_all_is_called_ = false;
scheduler::AggregatedMetricReporter<scheduler::FrameStatus, int64_t>
traffic_kilobytes_per_frame_status_;
scheduler::AggregatedMetricReporter<scheduler::FrameStatus, int64_t>
decoded_kilobytes_per_frame_status_;
};
ResourceLoadScheduler::TrafficMonitor::TrafficMonitor(FetchContext* context)
: is_main_frame_(context->IsMainFrame()),
context_(context),
traffic_kilobytes_per_frame_status_(
"Blink.ResourceLoadScheduler.TrafficBytes.KBPerFrameStatus",
&TakeWholeKilobytes),
decoded_kilobytes_per_frame_status_(
"Blink.ResourceLoadScheduler.DecodedBytes.KBPerFrameStatus",
&TakeWholeKilobytes) {
DCHECK(context_);
}
ResourceLoadScheduler::TrafficMonitor::~TrafficMonitor() {
ReportAll();
}
void ResourceLoadScheduler::TrafficMonitor::OnThrottlingStateChanged(
WebFrameScheduler::ThrottlingState state) {
current_state_ = state;
throttling_state_change_count_++;
}
void ResourceLoadScheduler::TrafficMonitor::Report(
const ResourceLoadScheduler::TrafficReportHints& hints) {
// Currently we only care about stats from frames.
if (!IsMainThread())
return;
if (!hints.IsValid())
return;
DEFINE_STATIC_LOCAL(EnumerationHistogram, request_count_by_circumstance,
("Blink.ResourceLoadScheduler.RequestCount",
ToSample(ReportCircumstance::kNumOfCircumstances)));
DEFINE_STATIC_LOCAL(
CustomCountHistogram, main_frame_throttled_traffic_bytes,
("Blink.ResourceLoadScheduler.TrafficBytes.MainframeThrottled", 0,
kMaximumReportSize1G, kReportBucketCount));
DEFINE_STATIC_LOCAL(
CustomCountHistogram, main_frame_not_throttled_traffic_bytes,
("Blink.ResourceLoadScheduler.TrafficBytes.MainframeNotThrottled", 0,
kMaximumReportSize1G, kReportBucketCount));
DEFINE_STATIC_LOCAL(
CustomCountHistogram, sub_frame_throttled_traffic_bytes,
("Blink.ResourceLoadScheduler.TrafficBytes.SubframeThrottled", 0,
kMaximumReportSize1G, kReportBucketCount));
DEFINE_STATIC_LOCAL(
CustomCountHistogram, sub_frame_not_throttled_traffic_bytes,
("Blink.ResourceLoadScheduler.TrafficBytes.SubframeNotThrottled", 0,
kMaximumReportSize1G, kReportBucketCount));
DEFINE_STATIC_LOCAL(
CustomCountHistogram, main_frame_throttled_decoded_bytes,
("Blink.ResourceLoadScheduler.DecodedBytes.MainframeThrottled", 0,
kMaximumReportSize1G, kReportBucketCount));
DEFINE_STATIC_LOCAL(
CustomCountHistogram, main_frame_not_throttled_decoded_bytes,
("Blink.ResourceLoadScheduler.DecodedBytes.MainframeNotThrottled", 0,
kMaximumReportSize1G, kReportBucketCount));
DEFINE_STATIC_LOCAL(
CustomCountHistogram, sub_frame_throttled_decoded_bytes,
("Blink.ResourceLoadScheduler.DecodedBytes.SubframeThrottled", 0,
kMaximumReportSize1G, kReportBucketCount));
DEFINE_STATIC_LOCAL(
CustomCountHistogram, sub_frame_not_throttled_decoded_bytes,
("Blink.ResourceLoadScheduler.DecodedBytes.SubframeNotThrottled", 0,
kMaximumReportSize1G, kReportBucketCount));
switch (current_state_) {
case WebFrameScheduler::ThrottlingState::kThrottled:
if (is_main_frame_) {
request_count_by_circumstance.Count(
ToSample(ReportCircumstance::kMainframeThrottled));
main_frame_throttled_traffic_bytes.Count(hints.encoded_data_length());
main_frame_throttled_decoded_bytes.Count(hints.decoded_body_length());
} else {
request_count_by_circumstance.Count(
ToSample(ReportCircumstance::kSubframeThrottled));
sub_frame_throttled_traffic_bytes.Count(hints.encoded_data_length());
sub_frame_throttled_decoded_bytes.Count(hints.decoded_body_length());
}
total_throttled_request_count_++;
total_throttled_traffic_bytes_ += hints.encoded_data_length();
total_throttled_decoded_bytes_ += hints.decoded_body_length();
break;
case WebFrameScheduler::ThrottlingState::kNotThrottled:
if (is_main_frame_) {
request_count_by_circumstance.Count(
ToSample(ReportCircumstance::kMainframeNotThrottled));
main_frame_not_throttled_traffic_bytes.Count(
hints.encoded_data_length());
main_frame_not_throttled_decoded_bytes.Count(
hints.decoded_body_length());
} else {
request_count_by_circumstance.Count(
ToSample(ReportCircumstance::kSubframeNotThrottled));
sub_frame_not_throttled_traffic_bytes.Count(
hints.encoded_data_length());
sub_frame_not_throttled_decoded_bytes.Count(
hints.decoded_body_length());
}
total_not_throttled_request_count_++;
total_not_throttled_traffic_bytes_ += hints.encoded_data_length();
total_not_throttled_decoded_bytes_ += hints.decoded_body_length();
break;
case WebFrameScheduler::ThrottlingState::kStopped:
break;
}
// Report kilobytes instead of bytes to avoid overflows.
size_t encoded_kilobytes = hints.encoded_data_length() / 1024;
size_t decoded_kilobytes = hints.decoded_body_length() / 1024;
if (encoded_kilobytes) {
traffic_kilobytes_per_frame_status_.RecordTask(
scheduler::GetFrameStatus(context_->GetFrameScheduler()),
encoded_kilobytes);
}
if (decoded_kilobytes) {
decoded_kilobytes_per_frame_status_.RecordTask(
scheduler::GetFrameStatus(context_->GetFrameScheduler()),
decoded_kilobytes);
}
}
void ResourceLoadScheduler::TrafficMonitor::ReportAll() {
// Currently we only care about stats from frames.
if (!IsMainThread())
return;
// Blink has several cases to create DocumentLoader not for an actual page
// load use. I.e., per a XMLHttpRequest in "document" type response.
// We just ignore such uninteresting cases in following metrics.
if (!total_throttled_request_count_ && !total_not_throttled_request_count_)
return;
if (report_all_is_called_)
return;
report_all_is_called_ = true;
DEFINE_STATIC_LOCAL(
CustomCountHistogram, main_frame_total_throttled_request_count,
("Blink.ResourceLoadScheduler.TotalRequestCount.MainframeThrottled", 0,
kMaximumReportSize10K, kReportBucketCount));
DEFINE_STATIC_LOCAL(
CustomCountHistogram, main_frame_total_not_throttled_request_count,
("Blink.ResourceLoadScheduler.TotalRequestCount.MainframeNotThrottled", 0,
kMaximumReportSize10K, kReportBucketCount));
DEFINE_STATIC_LOCAL(
CustomCountHistogram, sub_frame_total_throttled_request_count,
("Blink.ResourceLoadScheduler.TotalRequestCount.SubframeThrottled", 0,
kMaximumReportSize10K, kReportBucketCount));
DEFINE_STATIC_LOCAL(
CustomCountHistogram, sub_frame_total_not_throttled_request_count,
("Blink.ResourceLoadScheduler.TotalRequestCount.SubframeNotThrottled", 0,
kMaximumReportSize10K, kReportBucketCount));
DEFINE_STATIC_LOCAL(
CustomCountHistogram, main_frame_total_throttled_traffic_bytes,
("Blink.ResourceLoadScheduler.TotalTrafficBytes.MainframeThrottled", 0,
kMaximumReportSize1G, kReportBucketCount));
DEFINE_STATIC_LOCAL(
CustomCountHistogram, main_frame_total_not_throttled_traffic_bytes,
("Blink.ResourceLoadScheduler.TotalTrafficBytes.MainframeNotThrottled", 0,
kMaximumReportSize1G, kReportBucketCount));
DEFINE_STATIC_LOCAL(
CustomCountHistogram, sub_frame_total_throttled_traffic_bytes,
("Blink.ResourceLoadScheduler.TotalTrafficBytes.SubframeThrottled", 0,
kMaximumReportSize1G, kReportBucketCount));
DEFINE_STATIC_LOCAL(
CustomCountHistogram, sub_frame_total_not_throttled_traffic_bytes,
("Blink.ResourceLoadScheduler.TotalTrafficBytes.SubframeNotThrottled", 0,
kMaximumReportSize1G, kReportBucketCount));
DEFINE_STATIC_LOCAL(
CustomCountHistogram, main_frame_total_throttled_decoded_bytes,
("Blink.ResourceLoadScheduler.TotalDecodedBytes.MainframeThrottled", 0,
kMaximumReportSize1G, kReportBucketCount));
DEFINE_STATIC_LOCAL(
CustomCountHistogram, main_frame_total_not_throttled_decoded_bytes,
("Blink.ResourceLoadScheduler.TotalDecodedBytes.MainframeNotThrottled", 0,
kMaximumReportSize1G, kReportBucketCount));
DEFINE_STATIC_LOCAL(
CustomCountHistogram, sub_frame_total_throttled_decoded_bytes,
("Blink.ResourceLoadScheduler.TotalDecodedBytes.SubframeThrottled", 0,
kMaximumReportSize1G, kReportBucketCount));
DEFINE_STATIC_LOCAL(
CustomCountHistogram, sub_frame_total_not_throttled_decoded_bytes,
("Blink.ResourceLoadScheduler.TotalDecodedBytes.SubframeNotThrottled", 0,
kMaximumReportSize1G, kReportBucketCount));
DEFINE_STATIC_LOCAL(CustomCountHistogram, throttling_state_change_count,
("Blink.ResourceLoadScheduler.ThrottlingStateChangeCount",
0, 100, kReportBucketCount));
if (is_main_frame_) {
main_frame_total_throttled_request_count.Count(
total_throttled_request_count_);
main_frame_total_not_throttled_request_count.Count(
total_not_throttled_request_count_);
main_frame_total_throttled_traffic_bytes.Count(
total_throttled_traffic_bytes_);
main_frame_total_not_throttled_traffic_bytes.Count(
total_not_throttled_traffic_bytes_);
main_frame_total_throttled_decoded_bytes.Count(
total_throttled_decoded_bytes_);
main_frame_total_not_throttled_decoded_bytes.Count(
total_not_throttled_decoded_bytes_);
} else {
sub_frame_total_throttled_request_count.Count(
total_throttled_request_count_);
sub_frame_total_not_throttled_request_count.Count(
total_not_throttled_request_count_);
sub_frame_total_throttled_traffic_bytes.Count(
total_throttled_traffic_bytes_);
sub_frame_total_not_throttled_traffic_bytes.Count(
total_not_throttled_traffic_bytes_);
sub_frame_total_throttled_decoded_bytes.Count(
total_throttled_decoded_bytes_);
sub_frame_total_not_throttled_decoded_bytes.Count(
total_not_throttled_decoded_bytes_);
}
throttling_state_change_count.Count(throttling_state_change_count_);
}
constexpr ResourceLoadScheduler::ClientId
ResourceLoadScheduler::kInvalidClientId;
ResourceLoadScheduler::ResourceLoadScheduler(FetchContext* context)
: outstanding_limit_for_throttled_frame_scheduler_(
GetOutstandingThrottledLimit(context)),
context_(context) {
traffic_monitor_ =
std::make_unique<ResourceLoadScheduler::TrafficMonitor>(context_);
if (!RuntimeEnabledFeatures::ResourceLoadSchedulerEnabled() &&
!Platform::Current()->IsRendererSideResourceSchedulerEnabled()) {
// Initialize TrafficMonitor's state to be |kNotThrottled| so that it
// reports metrics in a reasonable state group.
traffic_monitor_->OnThrottlingStateChanged(
WebFrameScheduler::ThrottlingState::kNotThrottled);
return;
}
auto* scheduler = context->GetFrameScheduler();
if (!scheduler)
return;
if (Platform::Current()->IsRendererSideResourceSchedulerEnabled()) {
policy_ = context->InitialLoadThrottlingPolicy();
normal_outstanding_limit_ =
GetFieldTrialUint32Param(kRendererSideResourceScheduler,
kLimitForRendererSideResourceSchedulerName,
kLimitForRendererSideResourceScheduler);
tight_outstanding_limit_ = GetFieldTrialUint32Param(
kRendererSideResourceScheduler,
kTightLimitForRendererSideResourceSchedulerName,
kTightLimitForRendererSideResourceScheduler);
}
is_enabled_ = true;
scheduler_observer_handle_ = scheduler->AddThrottlingObserver(
WebFrameScheduler::ObserverType::kLoader, this);
}
ResourceLoadScheduler* ResourceLoadScheduler::Create(FetchContext* context) {
return new ResourceLoadScheduler(context ? context
: &FetchContext::NullInstance());
}
ResourceLoadScheduler::~ResourceLoadScheduler() = default;
void ResourceLoadScheduler::Trace(blink::Visitor* visitor) {
visitor->Trace(pending_request_map_);
visitor->Trace(context_);
}
void ResourceLoadScheduler::LoosenThrottlingPolicy() {
switch (policy_) {
case ThrottlingPolicy::kTight:
break;
case ThrottlingPolicy::kNormal:
return;
}
policy_ = ThrottlingPolicy::kNormal;
MaybeRun();
}
void ResourceLoadScheduler::Shutdown() {
// Do nothing if the feature is not enabled, or Shutdown() was already called.
if (is_shutdown_)
return;
is_shutdown_ = true;
if (traffic_monitor_)
traffic_monitor_.reset();
scheduler_observer_handle_.reset();
}
void ResourceLoadScheduler::Request(ResourceLoadSchedulerClient* client,
ThrottleOption option,
ResourceLoadPriority priority,
int intra_priority,
ResourceLoadScheduler::ClientId* id) {
*id = GenerateClientId();
if (is_shutdown_)
return;
if (!Platform::Current()->IsRendererSideResourceSchedulerEnabled()) {
// Prioritization is effectively disabled as we use the constant priority.
priority = ResourceLoadPriority::kMedium;
intra_priority = 0;
}
if (!is_enabled_ || option == ThrottleOption::kCanNotBeThrottled ||
!IsThrottablePriority(priority)) {
Run(*id, client, false);
return;
}
pending_requests_.emplace(*id, priority, intra_priority);
pending_request_map_.insert(
*id, new ClientWithPriority(client, priority, intra_priority));
MaybeRun();
}
void ResourceLoadScheduler::SetPriority(ClientId client_id,
ResourceLoadPriority priority,
int intra_priority) {
if (!Platform::Current()->IsRendererSideResourceSchedulerEnabled())
return;
auto client_it = pending_request_map_.find(client_id);
if (client_it == pending_request_map_.end())
return;
auto it = pending_requests_.find(ClientIdWithPriority(
client_id, client_it->value->priority, client_it->value->intra_priority));
DCHECK(it != pending_requests_.end());
pending_requests_.erase(it);
client_it->value->priority = priority;
client_it->value->intra_priority = intra_priority;
pending_requests_.emplace(client_id, priority, intra_priority);
MaybeRun();
}
bool ResourceLoadScheduler::Release(
ResourceLoadScheduler::ClientId id,
ResourceLoadScheduler::ReleaseOption option,
const ResourceLoadScheduler::TrafficReportHints& hints) {
// Check kInvalidClientId that can not be passed to the HashSet.
if (id == kInvalidClientId)
return false;
if (running_requests_.find(id) != running_requests_.end()) {
running_requests_.erase(id);
running_throttlable_requests_.erase(id);
if (traffic_monitor_)
traffic_monitor_->Report(hints);
if (option == ReleaseOption::kReleaseAndSchedule)
MaybeRun();
return true;
}
auto found = pending_request_map_.find(id);
if (found != pending_request_map_.end()) {
pending_request_map_.erase(found);
// Intentionally does not remove it from |pending_requests_|.
// Didn't release any running requests, but the outstanding limit might be
// changed to allow another request.
if (option == ReleaseOption::kReleaseAndSchedule)
MaybeRun();
return true;
}
return false;
}
void ResourceLoadScheduler::SetOutstandingLimitForTesting(size_t tight_limit,
size_t normal_limit) {
tight_outstanding_limit_ = tight_limit;
normal_outstanding_limit_ = normal_limit;
MaybeRun();
}
void ResourceLoadScheduler::OnNetworkQuiet() {
DCHECK(IsMainThread());
// Flush out all traffic reports here for safety.
traffic_monitor_->ReportAll();
if (maximum_running_requests_seen_ == 0)
return;
DEFINE_STATIC_LOCAL(
CustomCountHistogram, main_frame_throttled,
("Blink.ResourceLoadScheduler.PeakRequests.MainframeThrottled", 0,
kMaximumReportSize10K, kReportBucketCount));
DEFINE_STATIC_LOCAL(
CustomCountHistogram, main_frame_not_throttled,
("Blink.ResourceLoadScheduler.PeakRequests.MainframeNotThrottled", 0,
kMaximumReportSize10K, kReportBucketCount));
DEFINE_STATIC_LOCAL(
CustomCountHistogram, sub_frame_throttled,
("Blink.ResourceLoadScheduler.PeakRequests.SubframeThrottled", 0,
kMaximumReportSize10K, kReportBucketCount));
DEFINE_STATIC_LOCAL(
CustomCountHistogram, sub_frame_not_throttled,
("Blink.ResourceLoadScheduler.PeakRequests.SubframeNotThrottled", 0,
kMaximumReportSize10K, kReportBucketCount));
switch (throttling_history_) {
case ThrottlingHistory::kInitial:
case ThrottlingHistory::kNotThrottled:
if (context_->IsMainFrame())
main_frame_not_throttled.Count(maximum_running_requests_seen_);
else
sub_frame_not_throttled.Count(maximum_running_requests_seen_);
break;
case ThrottlingHistory::kThrottled:
if (context_->IsMainFrame())
main_frame_throttled.Count(maximum_running_requests_seen_);
else
sub_frame_throttled.Count(maximum_running_requests_seen_);
break;
case ThrottlingHistory::kPartiallyThrottled:
break;
case ThrottlingHistory::kStopped:
break;
}
}
bool ResourceLoadScheduler::IsThrottablePriority(
ResourceLoadPriority priority) const {
if (!Platform::Current()->IsRendererSideResourceSchedulerEnabled())
return true;
if (RuntimeEnabledFeatures::ResourceLoadSchedulerEnabled()) {
// If this scheduler is throttled by the associated WebFrameScheduler,
// consider every prioritiy as throttlable.
const auto state = frame_scheduler_throttling_state_;
if (state == WebFrameScheduler::ThrottlingState::kThrottled ||
state == WebFrameScheduler::ThrottlingState::kStopped) {
return true;
}
}
return priority < ResourceLoadPriority::kHigh;
}
void ResourceLoadScheduler::OnThrottlingStateChanged(
WebFrameScheduler::ThrottlingState state) {
if (traffic_monitor_)
traffic_monitor_->OnThrottlingStateChanged(state);
frame_scheduler_throttling_state_ = state;
switch (state) {
case WebFrameScheduler::ThrottlingState::kThrottled:
if (throttling_history_ == ThrottlingHistory::kInitial)
throttling_history_ = ThrottlingHistory::kThrottled;
else if (throttling_history_ == ThrottlingHistory::kNotThrottled)
throttling_history_ = ThrottlingHistory::kPartiallyThrottled;
break;
case WebFrameScheduler::ThrottlingState::kNotThrottled:
if (throttling_history_ == ThrottlingHistory::kInitial)
throttling_history_ = ThrottlingHistory::kNotThrottled;
else if (throttling_history_ == ThrottlingHistory::kThrottled)
throttling_history_ = ThrottlingHistory::kPartiallyThrottled;
break;
case WebFrameScheduler::ThrottlingState::kStopped:
throttling_history_ = ThrottlingHistory::kStopped;
break;
}
MaybeRun();
}
ResourceLoadScheduler::ClientId ResourceLoadScheduler::GenerateClientId() {
ClientId id = ++current_id_;
CHECK_NE(0u, id);
return id;
}
void ResourceLoadScheduler::MaybeRun() {
// Requests for keep-alive loaders could be remained in the pending queue,
// but ignore them once Shutdown() is called.
if (is_shutdown_)
return;
while (!pending_requests_.empty()) {
// TODO(yhirano): Consider using a unified value.
const auto num_requests =
frame_scheduler_throttling_state_ ==
WebFrameScheduler::ThrottlingState::kNotThrottled
? running_throttlable_requests_.size()
: running_requests_.size();
const bool has_enough_running_requets =
num_requests >= GetOutstandingLimit();
if (IsThrottablePriority(pending_requests_.begin()->priority) &&
has_enough_running_requets) {
break;
}
if (IsThrottablePriority(pending_requests_.begin()->priority) &&
has_enough_running_requets) {
break;
}
ClientId id = pending_requests_.begin()->client_id;
pending_requests_.erase(pending_requests_.begin());
auto found = pending_request_map_.find(id);
if (found == pending_request_map_.end())
continue; // Already released.
ResourceLoadSchedulerClient* client = found->value->client;
pending_request_map_.erase(found);
Run(id, client, true);
}
}
void ResourceLoadScheduler::Run(ResourceLoadScheduler::ClientId id,
ResourceLoadSchedulerClient* client,
bool throttlable) {
running_requests_.insert(id);
if (throttlable)
running_throttlable_requests_.insert(id);
if (running_requests_.size() > maximum_running_requests_seen_) {
maximum_running_requests_seen_ = running_requests_.size();
}
client->Run();
}
size_t ResourceLoadScheduler::GetOutstandingLimit() const {
size_t limit = kOutstandingUnlimited;
switch (frame_scheduler_throttling_state_) {
case WebFrameScheduler::ThrottlingState::kThrottled:
limit = std::min(limit, outstanding_limit_for_throttled_frame_scheduler_);
break;
case WebFrameScheduler::ThrottlingState::kNotThrottled:
break;
case WebFrameScheduler::ThrottlingState::kStopped:
if (RuntimeEnabledFeatures::ResourceLoadSchedulerEnabled())
limit = 0;
break;
}
switch (policy_) {
case ThrottlingPolicy::kTight:
limit = std::min(limit, tight_outstanding_limit_);
break;
case ThrottlingPolicy::kNormal:
limit = std::min(limit, normal_outstanding_limit_);
break;
}
return limit;
}
} // namespace blink