1*6777b538SAndroid Build Coastguard Worker // Copyright 2012 The Chromium Authors
2*6777b538SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*6777b538SAndroid Build Coastguard Worker // found in the LICENSE file.
4*6777b538SAndroid Build Coastguard Worker
5*6777b538SAndroid Build Coastguard Worker #include "net/base/prioritized_dispatcher.h"
6*6777b538SAndroid Build Coastguard Worker
7*6777b538SAndroid Build Coastguard Worker #include <ostream>
8*6777b538SAndroid Build Coastguard Worker
9*6777b538SAndroid Build Coastguard Worker #include "base/check_op.h"
10*6777b538SAndroid Build Coastguard Worker
11*6777b538SAndroid Build Coastguard Worker namespace net {
12*6777b538SAndroid Build Coastguard Worker
Limits(Priority num_priorities,size_t total_jobs)13*6777b538SAndroid Build Coastguard Worker PrioritizedDispatcher::Limits::Limits(Priority num_priorities,
14*6777b538SAndroid Build Coastguard Worker size_t total_jobs)
15*6777b538SAndroid Build Coastguard Worker : total_jobs(total_jobs), reserved_slots(num_priorities) {}
16*6777b538SAndroid Build Coastguard Worker
17*6777b538SAndroid Build Coastguard Worker PrioritizedDispatcher::Limits::Limits(const Limits& other) = default;
18*6777b538SAndroid Build Coastguard Worker
19*6777b538SAndroid Build Coastguard Worker PrioritizedDispatcher::Limits::~Limits() = default;
20*6777b538SAndroid Build Coastguard Worker
PrioritizedDispatcher(const Limits & limits)21*6777b538SAndroid Build Coastguard Worker PrioritizedDispatcher::PrioritizedDispatcher(const Limits& limits)
22*6777b538SAndroid Build Coastguard Worker : queue_(limits.reserved_slots.size()),
23*6777b538SAndroid Build Coastguard Worker max_running_jobs_(limits.reserved_slots.size()) {
24*6777b538SAndroid Build Coastguard Worker SetLimits(limits);
25*6777b538SAndroid Build Coastguard Worker }
26*6777b538SAndroid Build Coastguard Worker
27*6777b538SAndroid Build Coastguard Worker PrioritizedDispatcher::~PrioritizedDispatcher() = default;
28*6777b538SAndroid Build Coastguard Worker
Add(Job * job,Priority priority)29*6777b538SAndroid Build Coastguard Worker PrioritizedDispatcher::Handle PrioritizedDispatcher::Add(
30*6777b538SAndroid Build Coastguard Worker Job* job, Priority priority) {
31*6777b538SAndroid Build Coastguard Worker DCHECK(job);
32*6777b538SAndroid Build Coastguard Worker DCHECK_LT(priority, num_priorities());
33*6777b538SAndroid Build Coastguard Worker if (num_running_jobs_ < max_running_jobs_[priority]) {
34*6777b538SAndroid Build Coastguard Worker ++num_running_jobs_;
35*6777b538SAndroid Build Coastguard Worker job->Start();
36*6777b538SAndroid Build Coastguard Worker return Handle();
37*6777b538SAndroid Build Coastguard Worker }
38*6777b538SAndroid Build Coastguard Worker return queue_.Insert(job, priority);
39*6777b538SAndroid Build Coastguard Worker }
40*6777b538SAndroid Build Coastguard Worker
AddAtHead(Job * job,Priority priority)41*6777b538SAndroid Build Coastguard Worker PrioritizedDispatcher::Handle PrioritizedDispatcher::AddAtHead(
42*6777b538SAndroid Build Coastguard Worker Job* job, Priority priority) {
43*6777b538SAndroid Build Coastguard Worker DCHECK(job);
44*6777b538SAndroid Build Coastguard Worker DCHECK_LT(priority, num_priorities());
45*6777b538SAndroid Build Coastguard Worker if (num_running_jobs_ < max_running_jobs_[priority]) {
46*6777b538SAndroid Build Coastguard Worker ++num_running_jobs_;
47*6777b538SAndroid Build Coastguard Worker job->Start();
48*6777b538SAndroid Build Coastguard Worker return Handle();
49*6777b538SAndroid Build Coastguard Worker }
50*6777b538SAndroid Build Coastguard Worker return queue_.InsertAtFront(job, priority);
51*6777b538SAndroid Build Coastguard Worker }
52*6777b538SAndroid Build Coastguard Worker
Cancel(const Handle & handle)53*6777b538SAndroid Build Coastguard Worker void PrioritizedDispatcher::Cancel(const Handle& handle) {
54*6777b538SAndroid Build Coastguard Worker queue_.Erase(handle);
55*6777b538SAndroid Build Coastguard Worker }
56*6777b538SAndroid Build Coastguard Worker
EvictOldestLowest()57*6777b538SAndroid Build Coastguard Worker PrioritizedDispatcher::Job* PrioritizedDispatcher::EvictOldestLowest() {
58*6777b538SAndroid Build Coastguard Worker Handle handle = queue_.FirstMin();
59*6777b538SAndroid Build Coastguard Worker if (handle.is_null())
60*6777b538SAndroid Build Coastguard Worker return nullptr;
61*6777b538SAndroid Build Coastguard Worker Job* job = handle.value();
62*6777b538SAndroid Build Coastguard Worker Cancel(handle);
63*6777b538SAndroid Build Coastguard Worker return job;
64*6777b538SAndroid Build Coastguard Worker }
65*6777b538SAndroid Build Coastguard Worker
ChangePriority(const Handle & handle,Priority priority)66*6777b538SAndroid Build Coastguard Worker PrioritizedDispatcher::Handle PrioritizedDispatcher::ChangePriority(
67*6777b538SAndroid Build Coastguard Worker const Handle& handle, Priority priority) {
68*6777b538SAndroid Build Coastguard Worker DCHECK(!handle.is_null());
69*6777b538SAndroid Build Coastguard Worker DCHECK_LT(priority, num_priorities());
70*6777b538SAndroid Build Coastguard Worker DCHECK_GE(num_running_jobs_, max_running_jobs_[handle.priority()]) <<
71*6777b538SAndroid Build Coastguard Worker "Job should not be in queue when limits permit it to start.";
72*6777b538SAndroid Build Coastguard Worker
73*6777b538SAndroid Build Coastguard Worker if (handle.priority() == priority)
74*6777b538SAndroid Build Coastguard Worker return handle;
75*6777b538SAndroid Build Coastguard Worker
76*6777b538SAndroid Build Coastguard Worker if (MaybeDispatchJob(handle, priority))
77*6777b538SAndroid Build Coastguard Worker return Handle();
78*6777b538SAndroid Build Coastguard Worker Job* job = handle.value();
79*6777b538SAndroid Build Coastguard Worker queue_.Erase(handle);
80*6777b538SAndroid Build Coastguard Worker return queue_.Insert(job, priority);
81*6777b538SAndroid Build Coastguard Worker }
82*6777b538SAndroid Build Coastguard Worker
OnJobFinished()83*6777b538SAndroid Build Coastguard Worker void PrioritizedDispatcher::OnJobFinished() {
84*6777b538SAndroid Build Coastguard Worker DCHECK_GT(num_running_jobs_, 0u);
85*6777b538SAndroid Build Coastguard Worker --num_running_jobs_;
86*6777b538SAndroid Build Coastguard Worker MaybeDispatchNextJob();
87*6777b538SAndroid Build Coastguard Worker }
88*6777b538SAndroid Build Coastguard Worker
GetLimits() const89*6777b538SAndroid Build Coastguard Worker PrioritizedDispatcher::Limits PrioritizedDispatcher::GetLimits() const {
90*6777b538SAndroid Build Coastguard Worker size_t num_priorities = max_running_jobs_.size();
91*6777b538SAndroid Build Coastguard Worker Limits limits(num_priorities, max_running_jobs_.back());
92*6777b538SAndroid Build Coastguard Worker
93*6777b538SAndroid Build Coastguard Worker // Calculate the number of jobs reserved for each priority and higher. Leave
94*6777b538SAndroid Build Coastguard Worker // the number of jobs reserved for the lowest priority or higher as 0.
95*6777b538SAndroid Build Coastguard Worker for (size_t i = 1; i < num_priorities; ++i) {
96*6777b538SAndroid Build Coastguard Worker limits.reserved_slots[i] = max_running_jobs_[i] - max_running_jobs_[i - 1];
97*6777b538SAndroid Build Coastguard Worker }
98*6777b538SAndroid Build Coastguard Worker
99*6777b538SAndroid Build Coastguard Worker return limits;
100*6777b538SAndroid Build Coastguard Worker }
101*6777b538SAndroid Build Coastguard Worker
SetLimits(const Limits & limits)102*6777b538SAndroid Build Coastguard Worker void PrioritizedDispatcher::SetLimits(const Limits& limits) {
103*6777b538SAndroid Build Coastguard Worker DCHECK_EQ(queue_.num_priorities(), limits.reserved_slots.size());
104*6777b538SAndroid Build Coastguard Worker size_t total = 0;
105*6777b538SAndroid Build Coastguard Worker for (size_t i = 0; i < limits.reserved_slots.size(); ++i) {
106*6777b538SAndroid Build Coastguard Worker total += limits.reserved_slots[i];
107*6777b538SAndroid Build Coastguard Worker max_running_jobs_[i] = total;
108*6777b538SAndroid Build Coastguard Worker }
109*6777b538SAndroid Build Coastguard Worker // Unreserved slots are available for all priorities.
110*6777b538SAndroid Build Coastguard Worker DCHECK_LE(total, limits.total_jobs) << "sum(reserved_slots) <= total_jobs";
111*6777b538SAndroid Build Coastguard Worker size_t spare = limits.total_jobs - total;
112*6777b538SAndroid Build Coastguard Worker for (size_t i = limits.reserved_slots.size(); i > 0; --i) {
113*6777b538SAndroid Build Coastguard Worker max_running_jobs_[i - 1] += spare;
114*6777b538SAndroid Build Coastguard Worker }
115*6777b538SAndroid Build Coastguard Worker
116*6777b538SAndroid Build Coastguard Worker // Start pending jobs, if limits permit.
117*6777b538SAndroid Build Coastguard Worker while (true) {
118*6777b538SAndroid Build Coastguard Worker if (!MaybeDispatchNextJob())
119*6777b538SAndroid Build Coastguard Worker break;
120*6777b538SAndroid Build Coastguard Worker }
121*6777b538SAndroid Build Coastguard Worker }
122*6777b538SAndroid Build Coastguard Worker
SetLimitsToZero()123*6777b538SAndroid Build Coastguard Worker void PrioritizedDispatcher::SetLimitsToZero() {
124*6777b538SAndroid Build Coastguard Worker SetLimits(Limits(queue_.num_priorities(), 0));
125*6777b538SAndroid Build Coastguard Worker }
126*6777b538SAndroid Build Coastguard Worker
MaybeDispatchJob(const Handle & handle,Priority job_priority)127*6777b538SAndroid Build Coastguard Worker bool PrioritizedDispatcher::MaybeDispatchJob(const Handle& handle,
128*6777b538SAndroid Build Coastguard Worker Priority job_priority) {
129*6777b538SAndroid Build Coastguard Worker DCHECK_LT(job_priority, num_priorities());
130*6777b538SAndroid Build Coastguard Worker if (num_running_jobs_ >= max_running_jobs_[job_priority])
131*6777b538SAndroid Build Coastguard Worker return false;
132*6777b538SAndroid Build Coastguard Worker Job* job = handle.value();
133*6777b538SAndroid Build Coastguard Worker queue_.Erase(handle);
134*6777b538SAndroid Build Coastguard Worker ++num_running_jobs_;
135*6777b538SAndroid Build Coastguard Worker job->Start();
136*6777b538SAndroid Build Coastguard Worker return true;
137*6777b538SAndroid Build Coastguard Worker }
138*6777b538SAndroid Build Coastguard Worker
MaybeDispatchNextJob()139*6777b538SAndroid Build Coastguard Worker bool PrioritizedDispatcher::MaybeDispatchNextJob() {
140*6777b538SAndroid Build Coastguard Worker Handle handle = queue_.FirstMax();
141*6777b538SAndroid Build Coastguard Worker if (handle.is_null()) {
142*6777b538SAndroid Build Coastguard Worker DCHECK_EQ(0u, queue_.size());
143*6777b538SAndroid Build Coastguard Worker return false;
144*6777b538SAndroid Build Coastguard Worker }
145*6777b538SAndroid Build Coastguard Worker return MaybeDispatchJob(handle, handle.priority());
146*6777b538SAndroid Build Coastguard Worker }
147*6777b538SAndroid Build Coastguard Worker
148*6777b538SAndroid Build Coastguard Worker } // namespace net
149