This source file includes following definitions.
- value
- is_null
- RequestQueue
- RequestQueue
- Insert
- Erase
- FirstMax
- GetNextHighestIterator
- IsQueued
- IsEmpty
- scheduler_
- ScheduledResourceRequest
- Start
- client_id
- url_request
- url_request
- OnMessageReceived
- WillStartRequest
- GetNameForLogging
- DidChangePriority
- ScheduleRequest
- RemoveRequest
- OnClientCreated
- OnClientDeleted
- OnNavigate
- OnWillInsertBody
- OnReceivedSpdyProxiedHttpResponse
- StartRequest
- ReprioritizeRequest
- LoadAnyStartablePendingRequests
- GetNumDelayableRequestsInFlight
- ShouldStartRequest
- MakeClientId
#include "content/browser/loader/resource_scheduler.h"
#include "base/stl_util.h"
#include "content/common/resource_messages.h"
#include "content/browser/loader/resource_message_delegate.h"
#include "content/public/browser/resource_controller.h"
#include "content/public/browser/resource_request_info.h"
#include "content/public/browser/resource_throttle.h"
#include "ipc/ipc_message_macros.h"
#include "net/base/host_port_pair.h"
#include "net/base/load_flags.h"
#include "net/base/request_priority.h"
#include "net/http/http_server_properties.h"
#include "net/url_request/url_request.h"
#include "net/url_request/url_request_context.h"
namespace content {
static const size_t kMaxNumDelayableRequestsPerClient = 10;
static const size_t kMaxNumDelayableRequestsPerHost = 6;
class ResourceScheduler::RequestQueue {
private:
typedef net::PriorityQueue<ScheduledResourceRequest*> NetQueue;
public:
class Iterator {
public:
Iterator(NetQueue* queue) : queue_(queue) {
DCHECK(queue != NULL);
current_pointer_ = queue_->FirstMax();
}
Iterator& operator++() {
current_pointer_ = queue_->GetNextTowardsLastMin(current_pointer_);
return *this;
}
Iterator operator++(int) {
Iterator result(*this);
++(*this);
return result;
}
ScheduledResourceRequest* value() {
return current_pointer_.value();
}
bool is_null() {
return current_pointer_.is_null();
}
private:
NetQueue* queue_;
NetQueue::Pointer current_pointer_;
};
RequestQueue() : queue_(net::NUM_PRIORITIES) {}
~RequestQueue() {}
void Insert(ScheduledResourceRequest* request,
net::RequestPriority priority) {
DCHECK(!ContainsKey(pointers_, request));
NetQueue::Pointer pointer = queue_.Insert(request, priority);
pointers_[request] = pointer;
}
void Erase(ScheduledResourceRequest* request) {
PointerMap::iterator it = pointers_.find(request);
DCHECK(it != pointers_.end());
if (it == pointers_.end())
return;
queue_.Erase(it->second);
pointers_.erase(it);
}
ScheduledResourceRequest* FirstMax() {
return queue_.FirstMax().value();
}
Iterator GetNextHighestIterator() {
return Iterator(&queue_);
}
bool IsQueued(ScheduledResourceRequest* request) const {
return ContainsKey(pointers_, request);
}
bool IsEmpty() const { return queue_.size() == 0; }
private:
typedef std::map<ScheduledResourceRequest*, NetQueue::Pointer> PointerMap;
NetQueue queue_;
PointerMap pointers_;
};
class ResourceScheduler::ScheduledResourceRequest
: public ResourceMessageDelegate,
public ResourceThrottle {
public:
ScheduledResourceRequest(const ClientId& client_id,
net::URLRequest* request,
ResourceScheduler* scheduler)
: ResourceMessageDelegate(request),
client_id_(client_id),
request_(request),
ready_(false),
deferred_(false),
scheduler_(scheduler) {
TRACE_EVENT_ASYNC_BEGIN1("net", "URLRequest", request_,
"url", request->url().spec());
}
virtual ~ScheduledResourceRequest() {
scheduler_->RemoveRequest(this);
}
void Start() {
TRACE_EVENT_ASYNC_STEP_PAST0("net", "URLRequest", request_, "Queued");
ready_ = true;
if (deferred_ && request_->status().is_success()) {
deferred_ = false;
controller()->Resume();
}
}
const ClientId& client_id() const { return client_id_; }
net::URLRequest* url_request() { return request_; }
const net::URLRequest* url_request() const { return request_; }
private:
virtual bool OnMessageReceived(const IPC::Message& message,
bool* message_was_ok) OVERRIDE {
bool handled = true;
IPC_BEGIN_MESSAGE_MAP_EX(ScheduledResourceRequest, message, *message_was_ok)
IPC_MESSAGE_HANDLER(ResourceHostMsg_DidChangePriority, DidChangePriority)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP_EX()
return handled;
}
virtual void WillStartRequest(bool* defer) OVERRIDE {
deferred_ = *defer = !ready_;
}
virtual const char* GetNameForLogging() const OVERRIDE {
return "ResourceScheduler";
}
void DidChangePriority(int request_id, net::RequestPriority new_priority) {
scheduler_->ReprioritizeRequest(this, new_priority);
}
ClientId client_id_;
net::URLRequest* request_;
bool ready_;
bool deferred_;
ResourceScheduler* scheduler_;
DISALLOW_COPY_AND_ASSIGN(ScheduledResourceRequest);
};
struct ResourceScheduler::Client {
Client() : has_body(false), using_spdy_proxy(false) {}
~Client() {}
bool has_body;
bool using_spdy_proxy;
RequestQueue pending_requests;
RequestSet in_flight_requests;
};
ResourceScheduler::ResourceScheduler() {
}
ResourceScheduler::~ResourceScheduler() {
DCHECK(unowned_requests_.empty());
DCHECK(client_map_.empty());
}
scoped_ptr<ResourceThrottle> ResourceScheduler::ScheduleRequest(
int child_id,
int route_id,
net::URLRequest* url_request) {
DCHECK(CalledOnValidThread());
ClientId client_id = MakeClientId(child_id, route_id);
scoped_ptr<ScheduledResourceRequest> request(
new ScheduledResourceRequest(client_id, url_request, this));
ClientMap::iterator it = client_map_.find(client_id);
if (it == client_map_.end()) {
unowned_requests_.insert(request.get());
request->Start();
return request.PassAs<ResourceThrottle>();
}
Client* client = it->second;
if (ShouldStartRequest(request.get(), client) == START_REQUEST) {
StartRequest(request.get(), client);
} else {
client->pending_requests.Insert(request.get(), url_request->priority());
}
return request.PassAs<ResourceThrottle>();
}
void ResourceScheduler::RemoveRequest(ScheduledResourceRequest* request) {
DCHECK(CalledOnValidThread());
if (ContainsKey(unowned_requests_, request)) {
unowned_requests_.erase(request);
return;
}
ClientMap::iterator client_it = client_map_.find(request->client_id());
if (client_it == client_map_.end()) {
return;
}
Client* client = client_it->second;
if (client->pending_requests.IsQueued(request)) {
client->pending_requests.Erase(request);
DCHECK(!ContainsKey(client->in_flight_requests, request));
} else {
size_t erased = client->in_flight_requests.erase(request);
DCHECK(erased);
LoadAnyStartablePendingRequests(client);
}
}
void ResourceScheduler::OnClientCreated(int child_id, int route_id) {
DCHECK(CalledOnValidThread());
ClientId client_id = MakeClientId(child_id, route_id);
DCHECK(!ContainsKey(client_map_, client_id));
client_map_[client_id] = new Client;
}
void ResourceScheduler::OnClientDeleted(int child_id, int route_id) {
DCHECK(CalledOnValidThread());
ClientId client_id = MakeClientId(child_id, route_id);
DCHECK(ContainsKey(client_map_, client_id));
ClientMap::iterator it = client_map_.find(client_id);
if (it == client_map_.end())
return;
Client* client = it->second;
for (RequestSet::iterator it = client->in_flight_requests.begin();
it != client->in_flight_requests.end(); ++it) {
unowned_requests_.insert(*it);
}
client->in_flight_requests.clear();
delete client;
client_map_.erase(it);
}
void ResourceScheduler::OnNavigate(int child_id, int route_id) {
DCHECK(CalledOnValidThread());
ClientId client_id = MakeClientId(child_id, route_id);
ClientMap::iterator it = client_map_.find(client_id);
if (it == client_map_.end()) {
return;
}
Client* client = it->second;
client->has_body = false;
}
void ResourceScheduler::OnWillInsertBody(int child_id, int route_id) {
DCHECK(CalledOnValidThread());
ClientId client_id = MakeClientId(child_id, route_id);
ClientMap::iterator it = client_map_.find(client_id);
if (it == client_map_.end()) {
return;
}
Client* client = it->second;
client->has_body = true;
LoadAnyStartablePendingRequests(client);
}
void ResourceScheduler::OnReceivedSpdyProxiedHttpResponse(
int child_id,
int route_id) {
DCHECK(CalledOnValidThread());
ClientId client_id = MakeClientId(child_id, route_id);
ClientMap::iterator client_it = client_map_.find(client_id);
if (client_it == client_map_.end()) {
return;
}
Client* client = client_it->second;
if (!client->using_spdy_proxy) {
client->using_spdy_proxy = true;
LoadAnyStartablePendingRequests(client);
}
}
void ResourceScheduler::StartRequest(ScheduledResourceRequest* request,
Client* client) {
client->in_flight_requests.insert(request);
request->Start();
}
void ResourceScheduler::ReprioritizeRequest(ScheduledResourceRequest* request,
net::RequestPriority new_priority) {
if (request->url_request()->load_flags() & net::LOAD_IGNORE_LIMITS) {
NOTREACHED();
return;
}
net::RequestPriority old_priority = request->url_request()->priority();
DCHECK_NE(new_priority, old_priority);
request->url_request()->SetPriority(new_priority);
ClientMap::iterator client_it = client_map_.find(request->client_id());
if (client_it == client_map_.end()) {
return;
}
Client *client = client_it->second;
if (!client->pending_requests.IsQueued(request)) {
DCHECK(ContainsKey(client->in_flight_requests, request));
return;
}
client->pending_requests.Erase(request);
client->pending_requests.Insert(request,
request->url_request()->priority());
if (new_priority > old_priority) {
LoadAnyStartablePendingRequests(client);
}
}
void ResourceScheduler::LoadAnyStartablePendingRequests(Client* client) {
RequestQueue::Iterator request_iter =
client->pending_requests.GetNextHighestIterator();
while (!request_iter.is_null()) {
ScheduledResourceRequest* request = request_iter.value();
ShouldStartReqResult query_result = ShouldStartRequest(request, client);
if (query_result == START_REQUEST) {
client->pending_requests.Erase(request);
StartRequest(request, client);
if (client->pending_requests.GetNextHighestIterator().is_null())
break;
request_iter = client->pending_requests.GetNextHighestIterator();
} else if (query_result == DO_NOT_START_REQUEST_AND_KEEP_SEARCHING) {
++request_iter;
continue;
} else {
DCHECK(query_result == DO_NOT_START_REQUEST_AND_STOP_SEARCHING);
break;
}
}
}
void ResourceScheduler::GetNumDelayableRequestsInFlight(
Client* client,
const net::HostPortPair& active_request_host,
size_t* total_delayable,
size_t* total_for_active_host) const {
DCHECK(client != NULL && total_delayable != NULL &&
total_for_active_host != NULL);
size_t total_delayable_count = 0;
size_t same_host_count = 0;
for (RequestSet::iterator it = client->in_flight_requests.begin();
it != client->in_flight_requests.end(); ++it) {
net::HostPortPair host_port_pair =
net::HostPortPair::FromURL((*it)->url_request()->url());
if (active_request_host.Equals(host_port_pair)) {
same_host_count++;
}
if ((*it)->url_request()->priority() < net::LOW) {
const net::HttpServerProperties& http_server_properties =
*(*it)->url_request()->context()->http_server_properties();
if (!http_server_properties.SupportsSpdy(host_port_pair)) {
++total_delayable_count;
}
}
}
*total_delayable = total_delayable_count;
*total_for_active_host = same_host_count;
}
ResourceScheduler::ShouldStartReqResult ResourceScheduler::ShouldStartRequest(
ScheduledResourceRequest* request,
Client* client) const {
const net::URLRequest& url_request = *request->url_request();
if (!url_request.url().SchemeIsHTTPOrHTTPS()) {
return START_REQUEST;
}
if (client->using_spdy_proxy && url_request.url().SchemeIs("http")) {
return START_REQUEST;
}
const net::HttpServerProperties& http_server_properties =
*url_request.context()->http_server_properties();
if (url_request.priority() >= net::LOW ||
!ResourceRequestInfo::ForRequest(&url_request)->IsAsync()) {
return START_REQUEST;
}
net::HostPortPair host_port_pair =
net::HostPortPair::FromURL(url_request.url());
if (http_server_properties.SupportsSpdy(host_port_pair)) {
return START_REQUEST;
}
size_t num_delayable_requests_in_flight = 0;
size_t num_requests_in_flight_for_host = 0;
GetNumDelayableRequestsInFlight(client, host_port_pair,
&num_delayable_requests_in_flight,
&num_requests_in_flight_for_host);
if (num_delayable_requests_in_flight >= kMaxNumDelayableRequestsPerClient) {
return DO_NOT_START_REQUEST_AND_STOP_SEARCHING;
}
if (num_requests_in_flight_for_host >= kMaxNumDelayableRequestsPerHost) {
return DO_NOT_START_REQUEST_AND_KEEP_SEARCHING;
}
bool have_immediate_requests_in_flight =
client->in_flight_requests.size() > num_delayable_requests_in_flight;
if (have_immediate_requests_in_flight && !client->has_body &&
num_delayable_requests_in_flight != 0) {
return DO_NOT_START_REQUEST_AND_STOP_SEARCHING;
}
return START_REQUEST;
}
ResourceScheduler::ClientId ResourceScheduler::MakeClientId(
int child_id, int route_id) {
return (static_cast<ResourceScheduler::ClientId>(child_id) << 32) | route_id;
}
}