Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2016 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 "media/base/android/media_service_throttler.h" | |
| 6 | |
| 7 #include "base/bind.h" | |
| 8 #include "base/threading/thread_task_runner_handle.h" | |
| 9 #include "base/time/default_tick_clock.h" | |
| 10 #include "media/base/android/media_server_crash_listener.h" | |
| 11 | |
| 12 namespace media { | |
| 13 | |
| 14 // Elapsed time between crashes needed to completely reset the media server | |
| 15 // crash count. | |
| 16 const base::TimeDelta kTimeUntilCrashReset = base::TimeDelta::FromMinutes(1); | |
|
DaleCurtis
2016/11/08 22:35:41
constexpr on all base::TimeDelta? Otherwise I thin
sandersd (OOO until July 31)
2016/11/08 22:53:44
These should probably all be in an anonymous names
tguilbert
2016/11/11 03:50:29
Done.
| |
| 17 | |
| 18 // Elapsed time between schedule calls needed to completely reset the | |
| 19 // scheduling clock. | |
| 20 const base::TimeDelta kTimeUntilScheduleReset = base::TimeDelta::FromMinutes(1); | |
| 21 | |
| 22 // Decay rate of server crashes, corresponding to a tolerable 'normal' crash | |
| 23 // rate. This means that we will decrement our crash rate by ~1 crash/minute. | |
| 24 const uint kCrashDecayRateInMs = 60000; | |
|
sandersd (OOO until July 31)
2016/11/08 22:53:44
This is a period (as opposed to a rate).
tguilbert
2016/11/11 03:50:29
Done.
| |
| 25 | |
| 26 // Rate at which client creations will be exponentially throttled based on the | |
| 27 // number of media server crashes. | |
| 28 // NOTE: Since our exponential delay formula is 2^(server crashes), 0 server | |
| 29 // crashes still result in this delay being added once. | |
|
sandersd (OOO until July 31)
2016/11/08 22:53:44
An alternative would be to use |n ? 2^(n-1) : 0|.
tguilbert
2016/11/11 03:50:29
That was how I had originally written it, but I fo
| |
| 30 const base::TimeDelta kBaseExponentialDelay = | |
| 31 base::TimeDelta::FromMilliseconds(120); | |
| 32 | |
| 33 // Base rate at which we schedule client creations. | |
| 34 // The minimal delay is |kLinearThrottlingRate| + |kBaseExponentialDelay|. | |
| 35 // This corresponds to 0.2s. | |
| 36 const base::TimeDelta kLinearThrottlingRate = | |
| 37 base::TimeDelta::FromMilliseconds(80); | |
|
sandersd (OOO until July 31)
2016/11/08 22:53:44
Also a period ("delay" and other synonyms are fine
tguilbert
2016/11/11 03:50:29
Done.
| |
| 38 | |
| 39 // Max exponential throttling rate from media server crashes. | |
| 40 // The max delay will still be |kLinearThrottlingRate| + |kMaxExponentialDelay|. | |
| 41 // This corresponds to 3s. | |
| 42 const base::TimeDelta kMaxExponentialDelay = | |
| 43 base::TimeDelta::FromMilliseconds(2920); | |
| 44 | |
| 45 // Max number of clients to schedule immediately (e.g when loading a new page). | |
| 46 const uint kMaxBurstClients = 10; | |
| 47 | |
| 48 // Sliding window of time during which we allow clients to be scheduled | |
| 49 // immediately, to accomodate for a "bursts" of requests when loading new pages. | |
| 50 const base::TimeDelta kMinDelayWindow = | |
| 51 (kLinearThrottlingRate + kBaseExponentialDelay) * kMaxBurstClients; | |
| 52 | |
| 53 // The throttling progression based on number of crashes looks as follows: | |
| 54 // | |
| 55 // | # crashes | period | clients/sec | clients/mins | # burst clients | |
| 56 // | 0 | 200 ms | 5.0 | 300 | 10 | |
| 57 // | 1 | 320 ms | 3.1 | 188 | 6 | |
| 58 // | 2 | 560 ms | 1.8 | 107 | 4 | |
| 59 // | 3 | 1040 ms | 1.0 | 58 | 2 | |
| 60 // | 4 | 2000 ms | 0.5 | 30 | 1 | |
| 61 // | 5 | 3000 ms | 0.3 | 20 | 1 | |
| 62 // | 6 | 3000 ms | 0.3 | 20 | 1 | |
| 63 // | |
| 64 // NOTE: Since we use the floor function and a decay rate of 1 crash/minute when | |
| 65 // calculating the effective # of crashes, a single crash per minute will result | |
| 66 // in 0 effective crashes (since floor(1.0 - 'tiny decay') is 0). If we | |
| 67 // experience slightly more than 1 crash per 60 seconds, the effective number of | |
| 68 // crashes will go up as expected. | |
| 69 | |
| 70 // static | |
| 71 MediaServiceThrottler* MediaServiceThrottler::GetInstance() { | |
| 72 return base::Singleton< | |
|
DaleCurtis
2016/11/08 22:35:41
Hmm, why do both classes need a singleton? Seems o
tguilbert
2016/11/11 03:50:29
Updated. The MediaServiceThrottler is now a lazy i
| |
| 73 MediaServiceThrottler, | |
| 74 base::LeakySingletonTraits<MediaServiceThrottler>>::get(); | |
| 75 } | |
| 76 | |
| 77 MediaServiceThrottler::~MediaServiceThrottler() {} | |
| 78 | |
| 79 MediaServiceThrottler::MediaServiceThrottler() | |
| 80 : clock_(new base::DefaultTickClock()), current_crashes_(0.0) { | |
| 81 // base::Unretained is safe here because both the MediaServiceThrottler and | |
| 82 // the MediaServerCrashListener live until the process is terminated. | |
| 83 MediaServerCrashListener::GetInstance()->SetOnServerCrashCallback( | |
| 84 base::Bind(&MediaServiceThrottler::OnMediaServerCrash, | |
| 85 base::Unretained(this)), | |
| 86 base::ThreadTaskRunnerHandle::Get()); | |
| 87 | |
| 88 EnsureCrashListenerStarted(); | |
| 89 } | |
| 90 | |
| 91 void MediaServiceThrottler::SetTickClockForTesting(base::TickClock* clock) { | |
| 92 clock_.reset(clock); | |
| 93 } | |
| 94 | |
| 95 base::TimeDelta MediaServiceThrottler::GetBaseThrottlingRateForTesting() { | |
| 96 return kBaseExponentialDelay + kLinearThrottlingRate; | |
| 97 } | |
| 98 | |
| 99 void MediaServiceThrottler::ResetInternalStateForTesting() { | |
| 100 last_server_crash_ = base::TimeTicks(); | |
| 101 last_schedule_call_ = base::TimeTicks(); | |
| 102 next_schedulable_slot_ = clock_->NowTicks(); | |
| 103 last_update_time_ = clock_->NowTicks(); | |
| 104 current_crashes_ = 0.0; | |
| 105 } | |
| 106 | |
| 107 base::TimeDelta MediaServiceThrottler::ScheduleClientCreation() { | |
|
DaleCurtis
2016/11/08 22:35:41
I'd rename this to GetCreationDelay() or something
tguilbert
2016/11/11 03:50:29
Done.
| |
| 108 // Make sure the listener is started and the crashes decayed. | |
| 109 EnsureCrashListenerStarted(); | |
| 110 UpdateServerCrashes(); | |
| 111 | |
| 112 base::TimeTicks now = clock_->NowTicks(); | |
| 113 | |
| 114 // If we are passed the next time slot or if it has been 1 minute since the | |
| 115 // last call to ScheduleClientCreation(), reset the next time to now. | |
| 116 if (now > next_schedulable_slot_ || | |
|
DaleCurtis
2016/11/08 22:35:41
{} for multi-line if.
tguilbert
2016/11/11 03:50:29
Done.
| |
| 117 (now - last_schedule_call_) > kTimeUntilScheduleReset) | |
| 118 next_schedulable_slot_ = now; | |
| 119 | |
| 120 last_schedule_call_ = now; | |
| 121 | |
| 122 // Increment the next scheduled time between 0.2s and 3s, which allows the | |
| 123 // creation of between 50 and 3 clients per 10s. | |
| 124 next_schedulable_slot_ += | |
| 125 kLinearThrottlingRate + GetThrottlingDelayFromServerCrashes(); | |
| 126 | |
| 127 // Calculate how long to delay the creation so it isn't scheduled before | |
| 128 // |next_schedulable_slot_|. | |
| 129 base::TimeDelta delay = next_schedulable_slot_ - now; | |
| 130 | |
| 131 // If the scheduling delay is low enough, schedule it immediately instead. | |
| 132 // This allows up to kMaxBurstClients clients to be scheduled immediately. | |
| 133 if (delay <= kMinDelayWindow) | |
| 134 return base::TimeDelta(); | |
| 135 | |
| 136 return delay; | |
| 137 } | |
| 138 | |
| 139 base::TimeDelta MediaServiceThrottler::GetThrottlingDelayFromServerCrashes() { | |
| 140 // The combination of rounding down the number of crashes down and decaying | |
| 141 // at the rate of 1 crash / min means that a single crash will very quickly be | |
| 142 // rounded down to 0. Effectively, this means that we only start exponentially | |
| 143 // backing off if we have more than 1 crash in a 60 second window. | |
| 144 uint number_crashes = std::floor(current_crashes_); | |
|
DaleCurtis
2016/11/08 22:35:41
No uint. Specify an actual type.
tguilbert
2016/11/11 03:50:29
Done.
| |
| 145 DCHECK(number_crashes >= 0); | |
|
DaleCurtis
2016/11/08 22:35:41
DCHECK_GE
tguilbert
2016/11/11 03:50:29
Done.
| |
| 146 | |
| 147 // Prevents overflow/undefined behavior. We already reach kMaxExponentialDelay | |
| 148 // at 5 crashes in any case. | |
| 149 number_crashes = std::min(number_crashes, (uint)10); | |
|
DaleCurtis
2016/11/08 22:35:41
No c style cast. static_cast<> if you need. This s
tguilbert
2016/11/11 03:50:29
Done.
| |
| 150 | |
| 151 return std::min(kBaseExponentialDelay * (1 << number_crashes), | |
| 152 kMaxExponentialDelay); | |
| 153 } | |
| 154 | |
| 155 void MediaServiceThrottler::OnMediaServerCrash() { | |
| 156 UpdateServerCrashes(); | |
| 157 | |
| 158 last_server_crash_ = clock_->NowTicks(); | |
| 159 current_crashes_ += 1.0; | |
| 160 } | |
| 161 | |
| 162 void MediaServiceThrottler::UpdateServerCrashes() { | |
| 163 base::TimeTicks now = clock_->NowTicks(); | |
| 164 base::TimeDelta time_since_last_crash = now - last_server_crash_; | |
| 165 | |
| 166 if (time_since_last_crash > kTimeUntilCrashReset) { | |
| 167 // Reset the number of crashes if we haven't had a crash in the past minute. | |
| 168 current_crashes_ = 0.0; | |
|
DaleCurtis
2016/11/08 22:35:41
Just 0 is fine.
tguilbert
2016/11/11 03:50:29
Done.
| |
| 169 } else { | |
| 170 // Decay at the rate of 1 crash/minute otherwise. | |
| 171 double decay = | |
| 172 (now - last_update_time_).InMillisecondsF() / kCrashDecayRateInMs; | |
| 173 current_crashes_ = std::max(0.0, current_crashes_ - decay); | |
| 174 } | |
| 175 | |
| 176 last_update_time_ = now; | |
| 177 } | |
| 178 | |
| 179 void MediaServiceThrottler::EnsureCrashListenerStarted() { | |
|
DaleCurtis
2016/11/08 22:35:41
Seems is_media_server_crash_listener_running_ is k
tguilbert
2016/11/11 03:50:29
Updated.
| |
| 180 if (!is_media_server_crash_listener_running_) { | |
| 181 is_media_server_crash_listener_running_ = | |
| 182 MediaServerCrashListener::GetInstance()->StartListening(); | |
| 183 } | |
| 184 } | |
| 185 | |
| 186 } // namespace media | |
| OLD | NEW |