Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "components/data_usage/android/traffic_stats_amortizer.h" | |
| 6 | |
| 7 #include <algorithm> // For std::min. | |
|
tbansal1
2015/11/10 18:53:55
Is algorithm not banned in Chromium?
sclittle
2015/11/11 02:10:07
I don't think it's banned, lots of stuff uses it,
| |
| 8 #include <cmath> // For std::modf. | |
| 9 | |
| 10 #include "base/location.h" | |
| 11 #include "base/time/default_tick_clock.h" | |
| 12 #include "base/timer/timer.h" | |
| 13 #include "components/data_usage/core/data_use.h" | |
| 14 #include "net/android/traffic_stats.h" | |
| 15 | |
| 16 namespace data_usage { | |
| 17 namespace android { | |
| 18 | |
| 19 namespace { | |
| 20 | |
| 21 // Convenience typedef. | |
| 22 typedef std::vector< | |
| 23 std::pair<linked_ptr<DataUse>, DataUseAmortizer::DataUseConsumerCallback>> | |
| 24 DataUseBuffer; | |
| 25 | |
| 26 // The delay between receiving DataUse and querying TrafficStats byte counts for | |
| 27 // amortization. | |
| 28 // TODO(sclittle): Control this with a field trial parameter. | |
| 29 const int64_t kDefaultTrafficStatsQueryDelayMs = 50; | |
| 30 | |
| 31 // The longest amount of time that an amortization run can be delayed for. | |
| 32 // TODO(sclittle): Control this with a field trial parameter. | |
| 33 const int64_t kDefaultMaxAmortizationDelayMs = 500; | |
| 34 | |
| 35 // The maximum allowed size of the DataUse buffer. If the buffer ever exceeds | |
| 36 // this size, then DataUse will be amortized immediately and the buffer will be | |
| 37 // flushed. | |
| 38 // TODO(sclittle): Control this with a field trial parameter. | |
| 39 const size_t kDefaultMaxDataUseBufferSize = 128; | |
|
bengr
2015/11/10 18:12:50
So does this mean we will lose accuracy whenever t
sclittle
2015/11/11 02:10:07
Yes, this is true. I doubt it will be a problem in
| |
| 40 | |
| 41 // Scales |bytes| by |ratio|, using |remainder| to hold the running rounding | |
| 42 // error. | |
| 43 int64_t ScaleByteCount(int64_t bytes, double ratio, double* remainder) { | |
| 44 double intpart; | |
| 45 *remainder = | |
| 46 std::modf(static_cast<double>(bytes) * ratio + (*remainder), &intpart); | |
|
bengr
2015/11/10 18:12:50
Are there any assumptions about ratio? E.g., >= 1.
sclittle
2015/11/11 02:10:07
Added DCHECKs and comments. |ratio| can be any dou
| |
| 47 | |
| 48 DCHECK_GE(intpart, 0.0); | |
| 49 DCHECK_LE(intpart, static_cast<double>(INT64_MAX)); | |
| 50 DCHECK_GE(*remainder, 0.0); | |
| 51 DCHECK_LT(*remainder, 1.0); | |
| 52 return static_cast<int64_t>(intpart); | |
| 53 } | |
| 54 | |
| 55 // Amortize the difference between |desired_post_amortization_total_tx_bytes| | |
| 56 // and |pre_amortization_total_tx_bytes| into each of the DataUse objects in | |
| 57 // |data_use_sequence| by scaling the DataUse's |tx_bytes| appropriately. Does | |
| 58 // the same with the |rx_bytes| using those respective parameters. | |
| 59 void AmortizeDataUseSequence(DataUseBuffer* data_use_sequence, | |
| 60 int64_t pre_amortization_total_tx_bytes, | |
| 61 int64_t desired_post_amortization_total_tx_bytes, | |
| 62 int64_t pre_amortization_total_rx_bytes, | |
| 63 int64_t desired_post_amortization_total_rx_bytes) { | |
| 64 DCHECK(data_use_sequence); | |
| 65 DCHECK(!data_use_sequence->empty()); | |
| 66 | |
| 67 if (pre_amortization_total_tx_bytes != 0) { | |
|
tbansal1
2015/11/10 18:53:55
if |pre_amortization_total_tx_bytes| is 0 but |des
sclittle
2015/11/11 02:10:07
Added a TODO to handle this case gracefully.
I se
| |
| 68 const double ratio = | |
| 69 static_cast<double>(desired_post_amortization_total_tx_bytes) / | |
| 70 static_cast<double>(pre_amortization_total_tx_bytes); | |
| 71 double remainder = 0.0; | |
| 72 for (auto& data_use_buffer_pair : *data_use_sequence) { | |
| 73 data_use_buffer_pair.first->tx_bytes = ScaleByteCount( | |
| 74 data_use_buffer_pair.first->tx_bytes, ratio, &remainder); | |
| 75 // TODO(sclittle): Record UMA about values before vs. after amortization. | |
| 76 } | |
| 77 } | |
| 78 | |
| 79 if (pre_amortization_total_rx_bytes != 0) { | |
| 80 const double ratio = | |
| 81 static_cast<double>(desired_post_amortization_total_rx_bytes) / | |
| 82 static_cast<double>(pre_amortization_total_rx_bytes); | |
| 83 double remainder = 0.0; | |
| 84 for (auto& data_use_buffer_pair : *data_use_sequence) { | |
| 85 data_use_buffer_pair.first->rx_bytes = ScaleByteCount( | |
| 86 data_use_buffer_pair.first->rx_bytes, ratio, &remainder); | |
| 87 // TODO(sclittle): Record UMA about values before vs. after amortization. | |
| 88 } | |
| 89 } | |
| 90 } | |
| 91 | |
| 92 } // namespace | |
| 93 | |
| 94 TrafficStatsAmortizer::TrafficStatsAmortizer() | |
| 95 : TrafficStatsAmortizer( | |
| 96 scoped_ptr<base::TickClock>(new base::DefaultTickClock()), | |
| 97 scoped_ptr<base::Timer>(new base::Timer(false, false)), | |
| 98 base::TimeDelta::FromMilliseconds(kDefaultTrafficStatsQueryDelayMs), | |
| 99 base::TimeDelta::FromMilliseconds(kDefaultMaxAmortizationDelayMs), | |
| 100 kDefaultMaxDataUseBufferSize) {} | |
|
bengr
2015/11/10 18:12:50
Since there should only be one instance, would it
sclittle
2015/11/11 02:10:07
On second thought, that's not exactly true, I've r
| |
| 101 | |
| 102 TrafficStatsAmortizer::~TrafficStatsAmortizer() {} | |
| 103 | |
| 104 void TrafficStatsAmortizer::AmortizeDataUse( | |
| 105 scoped_ptr<DataUse> data_use, | |
| 106 const DataUseConsumerCallback& callback) { | |
| 107 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 108 DCHECK(!callback.is_null()); | |
| 109 int64_t tx_bytes = data_use->tx_bytes, rx_bytes = data_use->rx_bytes; | |
| 110 | |
| 111 // TODO(sclittle): Combine consecutive buffered DataUse objects that are | |
| 112 // identical except for byte counts and have the same callback. | |
| 113 buffered_data_use_.push_back( | |
| 114 std::make_pair(linked_ptr<DataUse>(data_use.release()), callback)); | |
| 115 | |
| 116 AddPreAmortizationBytes(tx_bytes, rx_bytes); | |
| 117 } | |
| 118 | |
| 119 void TrafficStatsAmortizer::OnExtraBytes(int64_t extra_tx_bytes, | |
| 120 int64_t extra_rx_bytes) { | |
| 121 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 122 AddPreAmortizationBytes(extra_tx_bytes, extra_rx_bytes); | |
| 123 } | |
| 124 | |
| 125 base::WeakPtr<TrafficStatsAmortizer> TrafficStatsAmortizer::GetWeakPtr() { | |
| 126 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 127 return weak_ptr_factory_.GetWeakPtr(); | |
| 128 } | |
| 129 | |
| 130 TrafficStatsAmortizer::TrafficStatsAmortizer( | |
| 131 scoped_ptr<base::TickClock> tick_clock, | |
| 132 scoped_ptr<base::Timer> traffic_stats_query_timer, | |
| 133 const base::TimeDelta& traffic_stats_query_delay, | |
| 134 const base::TimeDelta& max_amortization_delay, | |
| 135 size_t max_data_use_buffer_size) | |
| 136 : tick_clock_(tick_clock.Pass()), | |
| 137 traffic_stats_query_timer_(traffic_stats_query_timer.Pass()), | |
| 138 traffic_stats_query_delay_(traffic_stats_query_delay), | |
| 139 max_amortization_delay_(max_amortization_delay), | |
| 140 max_data_use_buffer_size_(max_data_use_buffer_size), | |
| 141 is_amortization_run_in_progress_(false), | |
|
tbansal1
2015/11/10 18:53:55
Is it possible to get rid of is_amortization_run_i
sclittle
2015/11/11 02:10:07
The null TimeTicks (i.e. base::TimeTicks()) is act
| |
| 142 are_last_amortization_traffic_stats_available_(false), | |
| 143 last_amortization_traffic_stats_tx_bytes_(-1), | |
| 144 last_amortization_traffic_stats_rx_bytes_(-1), | |
| 145 pre_amortization_tx_bytes_(0), | |
| 146 pre_amortization_rx_bytes_(0), | |
| 147 weak_ptr_factory_(this) {} | |
| 148 | |
| 149 bool TrafficStatsAmortizer::QueryTrafficStats(int64_t* tx_bytes, | |
| 150 int64_t* rx_bytes) const { | |
| 151 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 152 return net::android::traffic_stats::GetCurrentUidTxBytes(tx_bytes) && | |
| 153 net::android::traffic_stats::GetCurrentUidRxBytes(rx_bytes); | |
| 154 } | |
| 155 | |
| 156 void TrafficStatsAmortizer::AddPreAmortizationBytes(int64_t tx_bytes, | |
| 157 int64_t rx_bytes) { | |
| 158 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 159 DCHECK_GE(tx_bytes, 0); | |
| 160 DCHECK_GE(rx_bytes, 0); | |
| 161 base::TimeTicks now_ticks = tick_clock_->NowTicks(); | |
| 162 | |
| 163 if (!is_amortization_run_in_progress_) { | |
|
bengr
2015/11/10 18:12:50
I'd just call this is_amortization_in_progress_.
sclittle
2015/11/11 02:10:07
Done.
| |
| 164 is_amortization_run_in_progress_ = true; | |
| 165 current_amortization_run_start_time_ = now_ticks; | |
| 166 } | |
| 167 | |
| 168 pre_amortization_tx_bytes_ += tx_bytes; | |
| 169 pre_amortization_rx_bytes_ += rx_bytes; | |
| 170 | |
| 171 if (buffered_data_use_.size() > max_data_use_buffer_size_) { | |
| 172 // Enforce a maximum limit on the size of |buffered_data_use_| to avoid | |
| 173 // hogging memory. Note that this will likely cause the post-amortization | |
| 174 // byte counts calculated here to be less accurate than if the amortizer | |
| 175 // waited to perform amortization. | |
| 176 traffic_stats_query_timer_->Stop(); | |
|
bengr
2015/11/10 18:12:50
Add UMA (or a TODO for UMA).
sclittle
2015/11/11 02:10:07
Done.
| |
| 177 AmortizeNow(); | |
| 178 return; | |
| 179 } | |
| 180 | |
| 181 // Cap any amortization delay to |max_amortization_delay_|. Note that if | |
| 182 // |max_amortization_delay_| comes earlier, then this will likely cause the | |
| 183 // post-amortization byte counts calculated here to be less accurate than if | |
| 184 // the amortizer waited to perform amortization. | |
| 185 base::TimeDelta query_delay = std::min( | |
| 186 traffic_stats_query_delay_, current_amortization_run_start_time_ + | |
| 187 max_amortization_delay_ - now_ticks); | |
| 188 | |
| 189 // Set the timer to query TrafficStats and amortize after a delay. If the | |
| 190 // timer was already set, then this overrides the previous delay. | |
|
bengr
2015/11/10 18:12:50
So the point of this is that we will hopefully amo
sclittle
2015/11/11 02:10:07
Yes, added comment.
| |
| 191 traffic_stats_query_timer_->Start( | |
|
tbansal1
2015/11/10 18:53:55
does this post immediately if the delay is negativ
sclittle
2015/11/11 02:10:07
Yes, see https://code.google.com/p/chromium/codese
| |
| 192 FROM_HERE, query_delay, | |
| 193 base::Bind(&TrafficStatsAmortizer::AmortizeNow, GetWeakPtr())); | |
| 194 } | |
| 195 | |
| 196 void TrafficStatsAmortizer::AmortizeNow() { | |
| 197 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 198 | |
| 199 int64_t current_traffic_stats_tx_bytes = -1; | |
|
bengr
2015/11/10 18:12:50
What's the significance of -1?
sclittle
2015/11/11 02:10:07
It's just for debug purposes. If something goes wr
bengr
2015/11/11 15:41:37
Please add a comment.
| |
| 200 int64_t current_traffic_stats_rx_bytes = -1; | |
| 201 bool are_current_traffic_stats_available = QueryTrafficStats( | |
| 202 ¤t_traffic_stats_tx_bytes, ¤t_traffic_stats_rx_bytes); | |
| 203 | |
| 204 if (are_current_traffic_stats_available && | |
| 205 are_last_amortization_traffic_stats_available_ && | |
| 206 !buffered_data_use_.empty()) { | |
| 207 // These TrafficStats byte counts are guaranteed to increase monotonically | |
| 208 // since device boot. | |
| 209 DCHECK_GE(current_traffic_stats_tx_bytes, | |
| 210 last_amortization_traffic_stats_tx_bytes_); | |
| 211 DCHECK_GE(current_traffic_stats_rx_bytes, | |
| 212 last_amortization_traffic_stats_rx_bytes_); | |
| 213 | |
| 214 int64_t desired_post_amortization_total_tx_bytes = | |
| 215 current_traffic_stats_tx_bytes - | |
| 216 last_amortization_traffic_stats_tx_bytes_; | |
| 217 int64_t desired_post_amortization_total_rx_bytes = | |
| 218 current_traffic_stats_rx_bytes - | |
| 219 last_amortization_traffic_stats_rx_bytes_; | |
| 220 | |
| 221 AmortizeDataUseSequence(&buffered_data_use_, pre_amortization_tx_bytes_, | |
| 222 desired_post_amortization_total_tx_bytes, | |
| 223 pre_amortization_rx_bytes_, | |
| 224 desired_post_amortization_total_rx_bytes); | |
| 225 } | |
| 226 | |
| 227 // TODO(sclittle): Record some UMA about the delay before amortizing and how | |
| 228 // big the buffer was before amortizing. | |
| 229 | |
| 230 // Reset state now that the amortization run has finished. | |
| 231 is_amortization_run_in_progress_ = false; | |
| 232 current_amortization_run_start_time_ = base::TimeTicks(); | |
| 233 | |
| 234 are_last_amortization_traffic_stats_available_ = | |
| 235 are_current_traffic_stats_available; | |
| 236 last_amortization_traffic_stats_tx_bytes_ = current_traffic_stats_tx_bytes; | |
| 237 last_amortization_traffic_stats_rx_bytes_ = current_traffic_stats_rx_bytes; | |
| 238 | |
| 239 pre_amortization_tx_bytes_ = 0; | |
| 240 pre_amortization_rx_bytes_ = 0; | |
| 241 | |
| 242 // Pass post-amortization DataUse to their respective callbacks. | |
|
bengr
2015/11/10 18:12:50
DataUse -> DataUse objects
sclittle
2015/11/11 02:10:07
Done.
| |
| 243 DataUseBuffer data_use_sequence; | |
| 244 data_use_sequence.swap(buffered_data_use_); | |
| 245 for (auto& data_use_buffer_pair : data_use_sequence) { | |
| 246 scoped_ptr<DataUse> data_use(data_use_buffer_pair.first.release()); | |
| 247 data_use_buffer_pair.second.Run(data_use.Pass()); | |
| 248 } | |
| 249 } | |
| 250 | |
| 251 } // namespace android | |
| 252 } // namespace data_usage | |
| OLD | NEW |