blob: ffde1166d99949cf4d9cfc898e52704afe840f8f [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.
#ifndef ResourceLoadScheduler_h
#define ResourceLoadScheduler_h
#include <set>
#include "platform/WebFrameScheduler.h"
#include "platform/heap/GarbageCollected.h"
#include "platform/heap/HeapAllocator.h"
#include "platform/loader/fetch/Resource.h"
#include "platform/loader/fetch/ResourceLoaderOptions.h"
#include "platform/wtf/HashSet.h"
namespace blink {
class FetchContext;
// Client interface to use the throttling/scheduling functionality that
// ResourceLoadScheduler provides.
class PLATFORM_EXPORT ResourceLoadSchedulerClient
: public GarbageCollectedMixin {
public:
// Called when the request is granted to run.
virtual void Run() = 0;
void Trace(blink::Visitor* visitor) override {}
};
// The ResourceLoadScheduler provides a unified per-frame infrastructure to
// schedule loading requests. It receives resource requests as
// ResourceLoadSchedulerClient instances and calls Run() on them to dispatch
// them possibly with additional throttling/scheduling. This also keeps track of
// in-flight requests that are granted but are not released (by Release()) yet.
// Note: If FetchContext can not provide a WebFrameScheduler, throttling and
// scheduling functionalities will be completely disabled.
//
// TODO(yhirano): Provide the general algorithm and running experiments
// information.
class PLATFORM_EXPORT ResourceLoadScheduler final
: public GarbageCollectedFinalized<ResourceLoadScheduler>,
public WebFrameScheduler::Observer {
WTF_MAKE_NONCOPYABLE(ResourceLoadScheduler);
public:
// An option to use in calling Request(). If kCanNotBeThrottled is specified,
// the request should be granted and Run() should be called synchronously.
// Otherwise, OnRequestGranted() could be called later when other outstanding
// requests are finished.
enum class ThrottleOption { kCanBeThrottled, kCanNotBeThrottled };
// An option to use in calling Release(). If kReleaseOnly is specified,
// the specified request should be released, but no other requests should
// be scheduled within the call.
enum class ReleaseOption { kReleaseOnly, kReleaseAndSchedule };
// A class to pass traffic report hints on calling Release().
class TrafficReportHints {
public:
// |encoded_data_length| is payload size in bytes sent over the network.
// |decoded_body_length| is received resource data size in bytes.
TrafficReportHints(int64_t encoded_data_length, int64_t decoded_body_length)
: valid_(true),
encoded_data_length_(encoded_data_length),
decoded_body_length_(decoded_body_length) {}
// Takes a shared instance to represent an invalid instance that will be
// used when a caller don't want to report traffic, i.e. on a failure.
static PLATFORM_EXPORT TrafficReportHints& InvalidInstance();
bool IsValid() const { return valid_; }
int64_t encoded_data_length() const {
DCHECK(valid_);
return encoded_data_length_;
}
int64_t decoded_body_length() const {
DCHECK(valid_);
return decoded_body_length_;
}
private:
// Default constructor makes an invalid instance that won't be recorded.
TrafficReportHints() = default;
bool valid_ = false;
int64_t encoded_data_length_ = 0;
int64_t decoded_body_length_ = 0;
};
// ResourceLoadScheduler has two policies: |kTight| and |kNormal|. Currently
// this is used to support aggressive throttling while the corresponding frame
// is in layout-blocking phase. There is only one state transition,
// |kTight| => |kNormal|, which is done by |LoosenThrottlingPolicy|.
enum class ThrottlingPolicy { kTight, kNormal };
// Returned on Request(). Caller should need to return it via Release().
using ClientId = uint64_t;
static constexpr ClientId kInvalidClientId = 0u;
static constexpr size_t kOutstandingUnlimited =
std::numeric_limits<size_t>::max();
static ResourceLoadScheduler* Create(FetchContext* = nullptr);
~ResourceLoadScheduler();
void Trace(blink::Visitor*);
// Changes the policy from |kTight| to |kNormal|. This function can be called
// multiple times, and does nothing when the scheduler is already working with
// the normal policy. This function may initiate a new resource loading.
void LoosenThrottlingPolicy();
// Stops all operations including observing throttling signals.
// ResourceLoadSchedulerClient::Run() will not be called once this method is
// called. This method can be called multiple times safely.
void Shutdown();
// Makes a request. This may synchronously call
// ResourceLoadSchedulerClient::Run(), but it is guaranteed that ClientId is
// populated before ResourceLoadSchedulerClient::Run() is called, so that the
// caller can call Release() with the assigned ClientId correctly.
void Request(ResourceLoadSchedulerClient*,
ThrottleOption,
ResourceLoadPriority,
int intra_priority,
ClientId*);
// Updates the priority information of the given client. This function may
// initiate a new resource loading.
void SetPriority(ClientId, ResourceLoadPriority, int intra_priority);
// ResourceLoadSchedulerClient should call this method when the loading is
// finished, or canceled. This method can be called in a pre-finalization
// step, bug the ReleaseOption must be kReleaseOnly in such a case.
// TrafficReportHints is for reporting histograms.
// TrafficReportHints::InvalidInstance() can be used to omit reporting.
bool Release(ClientId, ReleaseOption, const TrafficReportHints&);
// Sets outstanding limit for testing.
void SetOutstandingLimitForTesting(size_t limit) {
SetOutstandingLimitForTesting(limit, limit);
}
void SetOutstandingLimitForTesting(size_t tight_limit, size_t limit);
void OnNetworkQuiet();
// Returns whether we can throttle a request with the given priority.
// This function returns false when RendererSideResourceScheduler is disabled.
bool IsThrottablePriority(ResourceLoadPriority) const;
// WebFrameScheduler::Observer overrides:
void OnThrottlingStateChanged(WebFrameScheduler::ThrottlingState) override;
private:
class TrafficMonitor;
class ClientIdWithPriority {
public:
struct Compare {
bool operator()(const ClientIdWithPriority& x,
const ClientIdWithPriority& y) const {
if (x.priority != y.priority)
return x.priority > y.priority;
if (x.intra_priority != y.intra_priority)
return x.intra_priority > y.intra_priority;
return x.client_id < y.client_id;
}
};
ClientIdWithPriority(ClientId client_id,
WebURLRequest::Priority priority,
int intra_priority)
: client_id(client_id),
priority(priority),
intra_priority(intra_priority) {}
const ClientId client_id;
const WebURLRequest::Priority priority;
const int intra_priority;
};
struct ClientWithPriority : public GarbageCollected<ClientWithPriority> {
ClientWithPriority(ResourceLoadSchedulerClient* client,
ResourceLoadPriority priority,
int intra_priority)
: client(client), priority(priority), intra_priority(intra_priority) {}
void Trace(blink::Visitor* visitor) { visitor->Trace(client); }
Member<ResourceLoadSchedulerClient> client;
ResourceLoadPriority priority;
int intra_priority;
};
ResourceLoadScheduler(FetchContext*);
// Generates the next ClientId.
ClientId GenerateClientId();
// Picks up one client if there is a budget and route it to run.
void MaybeRun();
// Grants a client to run,
void Run(ClientId, ResourceLoadSchedulerClient*);
void SetOutstandingLimitAndMaybeRun(size_t limit);
// A flag to indicate an internal running state.
// TODO(toyoshim): We may want to use enum once we start to have more states.
bool is_shutdown_ = false;
// A mutable flag to indicate if the throttling and scheduling are enabled.
// Can be modified by field trial flags or for testing.
bool is_enabled_ = false;
ThrottlingPolicy policy_ = ThrottlingPolicy::kNormal;
// Used to limit outstanding requests when |policy_| is kTight.
size_t tight_outstanding_limit_ = kOutstandingUnlimited;
// TODO(crbug.com/735410): If this throttling is enabled always, it makes some
// tests fail.
// Used to limit outstanding requests when |policy_| is kNormal.
size_t outstanding_limit_ = kOutstandingUnlimited;
// Outstanding limit for throttled frames. Managed via the field trial.
const size_t outstanding_throttled_limit_;
// The last used ClientId to calculate the next.
ClientId current_id_ = kInvalidClientId;
// Holds clients that were granted and are running.
HashSet<ClientId> running_requests_;
// Largest number of running requests seen so far.
unsigned maximum_running_requests_seen_ = 0;
enum class ThrottlingHistory {
kInitial,
kThrottled,
kNotThrottled,
kPartiallyThrottled,
kStopped,
};
ThrottlingHistory throttling_history_ = ThrottlingHistory::kInitial;
WebFrameScheduler::ThrottlingState frame_scheduler_throttling_state_ =
WebFrameScheduler::ThrottlingState::kNotThrottled;
// Holds clients that haven't been granted, and are waiting for a grant.
HeapHashMap<ClientId, Member<ClientWithPriority>> pending_request_map_;
// We use std::set here because WTF doesn't have its counterpart.
std::set<ClientIdWithPriority, ClientIdWithPriority::Compare>
pending_requests_;
// Holds an internal class instance to monitor and report traffic.
std::unique_ptr<TrafficMonitor> traffic_monitor_;
// Holds FetchContext reference to contact WebFrameScheduler.
Member<FetchContext> context_;
};
} // namespace blink
#endif