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/memory/ptr_util.h" | |
| 9 #include "base/threading/thread_task_runner_handle.h" | |
| 10 #include "base/time/default_tick_clock.h" | |
| 11 #include "media/base/android/media_server_crash_listener.h" | |
| 12 | |
| 13 namespace { | |
|
sandersd (OOO until July 31)
2016/11/11 18:55:14
Nit: The anonymous namespace is inside the named n
tguilbert
2016/11/14 23:18:08
Done.
| |
| 14 | |
| 15 static base::LazyInstance<media::MediaServiceThrottler>::Leaky | |
| 16 g_media_service_throttler = LAZY_INSTANCE_INITIALIZER; | |
| 17 | |
| 18 // Elapsed time between crashes needed to completely reset the media server | |
| 19 // crash count. | |
| 20 constexpr base::TimeDelta kTimeUntilCrashReset = | |
| 21 base::TimeDelta::FromMinutes(1); | |
| 22 | |
| 23 // Elapsed time between schedule calls needed to completely reset the | |
| 24 // scheduling clock. | |
| 25 constexpr base::TimeDelta kTimeUntilScheduleReset = | |
| 26 base::TimeDelta::FromMinutes(1); | |
| 27 | |
| 28 // Decay rate of server crashes, corresponding to a tolerable 'normal' crash | |
| 29 // rate. This means that we will decrement our crash rate by ~1 crash/minute. | |
| 30 const uint32_t kCrashDecayPeriodInMs = 60000; | |
| 31 | |
| 32 // Rate at which client creations will be exponentially throttled based on the | |
| 33 // number of media server crashes. | |
| 34 // NOTE: Since our exponential delay formula is 2^(server crashes), 0 server | |
| 35 // crashes still result in this delay being added once. | |
| 36 constexpr base::TimeDelta kBaseExponentialDelay = | |
| 37 base::TimeDelta::FromMilliseconds(120); | |
| 38 | |
| 39 // Base rate at which we schedule client creations. | |
| 40 // The minimal delay is |kLinearThrottlingDelay| + |kBaseExponentialDelay|. | |
| 41 // This corresponds to 0.2s. | |
| 42 constexpr base::TimeDelta kLinearThrottlingDelay = | |
| 43 base::TimeDelta::FromMilliseconds(80); | |
| 44 | |
| 45 // Max exponential throttling rate from media server crashes. | |
| 46 // The max delay will still be |kLinearThrottlingDelay| + | |
| 47 // |kMaxExponentialDelay|. This corresponds to 3s. | |
| 48 constexpr base::TimeDelta kMaxExponentialDelay = | |
| 49 base::TimeDelta::FromMilliseconds(2920); | |
| 50 | |
| 51 // Max number of clients to schedule immediately (e.g when loading a new page). | |
| 52 const uint32_t kMaxBurstClients = 10; | |
| 53 | |
| 54 // Sliding window of time during which we allow clients to be scheduled | |
| 55 // immediately, to accomodate for a "bursts" of requests when loading new pages. | |
| 56 const base::TimeDelta kMinDelayWindow = | |
| 57 (kLinearThrottlingDelay + kBaseExponentialDelay) * kMaxBurstClients; | |
| 58 | |
| 59 // The throttling progression based on number of crashes looks as follows: | |
| 60 // | |
| 61 // | # crashes | period | clients/sec | clients/mins | # burst clients | |
| 62 // | 0 | 200 ms | 5.0 | 300 | 10 | |
| 63 // | 1 | 320 ms | 3.1 | 188 | 6 | |
| 64 // | 2 | 560 ms | 1.8 | 107 | 4 | |
| 65 // | 3 | 1040 ms | 1.0 | 58 | 2 | |
| 66 // | 4 | 2000 ms | 0.5 | 30 | 1 | |
| 67 // | 5 | 3000 ms | 0.3 | 20 | 1 | |
| 68 // | 6 | 3000 ms | 0.3 | 20 | 1 | |
| 69 // | |
| 70 // NOTE: Since we use the floor function and a decay rate of 1 crash/minute when | |
| 71 // calculating the effective # of crashes, a single crash per minute will result | |
| 72 // in 0 effective crashes (since floor(1.0 - 'tiny decay') is 0). If we | |
| 73 // experience slightly more than 1 crash per 60 seconds, the effective number of | |
| 74 // crashes will go up as expected. | |
| 75 } | |
| 76 | |
| 77 namespace media { | |
| 78 | |
| 79 // static | |
| 80 MediaServiceThrottler* MediaServiceThrottler::GetInstance() { | |
| 81 return g_media_service_throttler.Pointer(); | |
| 82 } | |
| 83 | |
| 84 MediaServiceThrottler::~MediaServiceThrottler() {} | |
| 85 | |
| 86 MediaServiceThrottler::MediaServiceThrottler() | |
| 87 : clock_(new base::DefaultTickClock()), | |
| 88 current_crashes_(0), | |
| 89 crash_listener_task_runner_(base::ThreadTaskRunnerHandle::Get()) { | |
| 90 // base::Unretained is safe because the MediaServiceThrottler is supposed to | |
| 91 // live until the process dies. | |
| 92 release_crash_listener_cb_ = base::Bind( | |
| 93 &MediaServiceThrottler::ReleaseCrashListener, base::Unretained(this)); | |
| 94 EnsureCrashListenerStarted(); | |
| 95 } | |
| 96 | |
| 97 void MediaServiceThrottler::SetTickClockForTesting(base::TickClock* clock) { | |
| 98 clock_.reset(clock); | |
| 99 } | |
| 100 | |
| 101 base::TimeDelta MediaServiceThrottler::GetBaseThrottlingRateForTesting() { | |
| 102 return kBaseExponentialDelay + kLinearThrottlingDelay; | |
| 103 } | |
| 104 | |
| 105 void MediaServiceThrottler::ResetInternalStateForTesting() { | |
| 106 last_server_crash_ = base::TimeTicks(); | |
| 107 last_schedule_call_ = base::TimeTicks(); | |
| 108 next_schedulable_slot_ = clock_->NowTicks(); | |
| 109 last_current_crash_update_time_ = clock_->NowTicks(); | |
| 110 current_crashes_ = 0.0; | |
| 111 } | |
| 112 | |
| 113 base::TimeDelta MediaServiceThrottler::GetDelayForClientCreation() { | |
| 114 // Make sure the listener is started and the crashes decayed. | |
| 115 EnsureCrashListenerStarted(); | |
| 116 UpdateServerCrashes(); | |
| 117 | |
| 118 base::TimeTicks now = clock_->NowTicks(); | |
| 119 | |
| 120 // If we are passed the next time slot or if it has been 1 minute since the | |
| 121 // last call to GetDelayForClientCreation(), reset the next time to now. | |
| 122 if (now > next_schedulable_slot_ || | |
| 123 (now - last_schedule_call_) > kTimeUntilScheduleReset) { | |
| 124 next_schedulable_slot_ = now; | |
| 125 } | |
| 126 | |
| 127 last_schedule_call_ = now; | |
| 128 | |
| 129 // Increment the next scheduled time between 0.2s and 3s, which allows the | |
| 130 // creation of between 50 and 3 clients per 10s. | |
| 131 next_schedulable_slot_ += | |
| 132 kLinearThrottlingDelay + GetThrottlingDelayFromServerCrashes(); | |
| 133 | |
| 134 // Calculate how long to delay the creation so it isn't scheduled before | |
| 135 // |next_schedulable_slot_|. | |
| 136 base::TimeDelta delay = next_schedulable_slot_ - now; | |
| 137 | |
| 138 // If the scheduling delay is low enough, schedule it immediately instead. | |
| 139 // This allows up to kMaxBurstClients clients to be scheduled immediately. | |
| 140 if (delay <= kMinDelayWindow) | |
| 141 return base::TimeDelta(); | |
| 142 | |
| 143 return delay; | |
| 144 } | |
| 145 | |
| 146 base::TimeDelta MediaServiceThrottler::GetThrottlingDelayFromServerCrashes() { | |
| 147 // The combination of rounding down the number of crashes down and decaying | |
| 148 // at the rate of 1 crash / min means that a single crash will very quickly be | |
| 149 // rounded down to 0. Effectively, this means that we only start exponentially | |
| 150 // backing off if we have more than 1 crash in a 60 second window. | |
| 151 uint32_t number_crashes = std::floor(current_crashes_); | |
|
watk
2016/11/12 04:22:16
number_crashes sounds weird but num_crashes doesn'
tguilbert
2016/11/14 23:18:08
Done.
| |
| 152 DCHECK_GE(number_crashes, 0u); | |
| 153 | |
| 154 // Prevents overflow/undefined behavior. We already reach kMaxExponentialDelay | |
| 155 // at 5 crashes in any case. | |
| 156 number_crashes = std::min(number_crashes, 10u); | |
| 157 | |
| 158 return std::min(kBaseExponentialDelay * (1 << number_crashes), | |
| 159 kMaxExponentialDelay); | |
| 160 } | |
| 161 | |
| 162 void MediaServiceThrottler::OnMediaServerCrash() { | |
| 163 UpdateServerCrashes(); | |
| 164 | |
| 165 last_server_crash_ = clock_->NowTicks(); | |
| 166 current_crashes_ += 1.0; | |
| 167 } | |
| 168 | |
| 169 void MediaServiceThrottler::UpdateServerCrashes() { | |
| 170 base::TimeTicks now = clock_->NowTicks(); | |
| 171 base::TimeDelta time_since_last_crash = now - last_server_crash_; | |
| 172 | |
| 173 if (time_since_last_crash > kTimeUntilCrashReset) { | |
|
watk
2016/11/12 04:22:16
Sorry if this is a dumb question, but is this spec
tguilbert
2016/11/14 23:18:08
Not a dumb question :)
We decay at a rate of 1/mi
| |
| 174 // Reset the number of crashes if we haven't had a crash in the past minute. | |
| 175 current_crashes_ = 0.0; | |
| 176 } else { | |
| 177 // Decay at the rate of 1 crash/minute otherwise. | |
| 178 double decay = (now - last_current_crash_update_time_).InMillisecondsF() / | |
| 179 kCrashDecayPeriodInMs; | |
| 180 current_crashes_ = std::max(0.0, current_crashes_ - decay); | |
| 181 } | |
| 182 | |
| 183 last_current_crash_update_time_ = now; | |
| 184 } | |
| 185 | |
| 186 void MediaServiceThrottler::ReleaseCrashListener() { | |
| 187 crash_listener_.reset(nullptr); | |
| 188 } | |
| 189 | |
| 190 void MediaServiceThrottler::EnsureCrashListenerStarted() { | |
| 191 if (!crash_listener_) { | |
| 192 // base::Unretained is safe here because both the MediaServiceThrottler and | |
| 193 // the MediaServerCrashListener live until the process is terminated. | |
| 194 crash_listener_ = base::MakeUnique<MediaServerCrashListener>( | |
| 195 base::Bind(&MediaServiceThrottler::OnMediaServerCrash, | |
| 196 base::Unretained(this)), | |
| 197 crash_listener_task_runner_); | |
| 198 } else { | |
| 199 crash_listener_->EnsureListening(); | |
| 200 } | |
| 201 | |
| 202 // Cancels outstanding/pending versions of the callback. | |
| 203 cancelable_release_crash_listener_cb_.Reset(release_crash_listener_cb_); | |
| 204 | |
| 205 crash_listener_task_runner_->PostDelayedTask( | |
| 206 FROM_HERE, cancelable_release_crash_listener_cb_.callback(), | |
| 207 base::TimeDelta::FromMinutes(1)); | |
|
DaleCurtis
2016/11/11 23:32:16
This should probably be more clearly related to yo
tguilbert
2016/11/14 23:18:08
Added a new constant and comments.
| |
| 208 } | |
| 209 | |
| 210 bool MediaServiceThrottler::IsCrashListenerAliveForTesting() { | |
| 211 return !!crash_listener_; | |
| 212 } | |
| 213 | |
| 214 void MediaServiceThrottler::SetCrashListenerTaskRunnerForTesting( | |
| 215 scoped_refptr<base::SingleThreadTaskRunner> crash_listener_task_runner) { | |
| 216 // Set the task runner so |crash_listener_| be deleted on the right thread. | |
| 217 crash_listener_task_runner_ = crash_listener_task_runner; | |
| 218 | |
| 219 // Re-create the crash listener. | |
| 220 crash_listener_ = base::MakeUnique<MediaServerCrashListener>( | |
| 221 base::Closure(), crash_listener_task_runner_); | |
| 222 } | |
| 223 | |
| 224 } // namespace media | |
| OLD | NEW |