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