Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(233)

Side by Side Diff: media/base/android/media_service_throttler.cc

Issue 2471903002: Add MediaServiceThrottler (Closed)
Patch Set: Limit crashes from watchdog creation failures Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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 = std::floor(current_crashes_);
DaleCurtis 2016/11/15 00:49:32 Just casting to uint32_t should be sufficient.
tguilbert 2016/11/15 19:29:07 Done.
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() {
169 UpdateServerCrashes();
170
171 last_server_crash_ = clock_->NowTicks();
172 current_crashes_ += 1.0;
173 }
174
175 void MediaServiceThrottler::UpdateServerCrashes() {
176 base::TimeTicks now = clock_->NowTicks();
177 base::TimeDelta time_since_last_crash = now - last_server_crash_;
178
179 if (time_since_last_crash > kTimeUntilCrashReset) {
180 // Reset the number of crashes if we haven't had a crash in the past minute.
181 current_crashes_ = 0.0;
182 } else {
183 // Decay at the rate of 1 crash/minute otherwise.
184 double decay = (now - last_current_crash_update_time_).InMillisecondsF() /
185 kCrashDecayPeriodInMs;
186 current_crashes_ = std::max(0.0, current_crashes_ - decay);
187 }
188
189 last_current_crash_update_time_ = now;
190 }
191
192 void MediaServiceThrottler::ReleaseCrashListener() {
193 crash_listener_.reset(nullptr);
194 }
195
196 void MediaServiceThrottler::EnsureCrashListenerStarted() {
197 if (!crash_listener_) {
198 // base::Unretained is safe here because both the MediaServiceThrottler and
199 // the MediaServerCrashListener live until the process is terminated.
200 crash_listener_ = base::MakeUnique<MediaServerCrashListener>(
201 base::Bind(&MediaServiceThrottler::OnMediaServerCrash,
202 base::Unretained(this)),
203 crash_listener_task_runner_);
204 } else {
205 crash_listener_->EnsureListening();
206 }
207
208 // Cancels outstanding/pending versions of the callback.
209 cancelable_release_crash_listener_cb_.Reset(release_crash_listener_cb_);
210
211 // Schedule the release of |crash_listener_| a minute from now. This will be
212 // updated anytime GetDelayForClientCreation() is called.
213 crash_listener_task_runner_->PostDelayedTask(
214 FROM_HERE, cancelable_release_crash_listener_cb_.callback(),
215 kReleaseInactivityDelay);
216 }
217
218 bool MediaServiceThrottler::IsCrashListenerAliveForTesting() {
219 return !!crash_listener_;
220 }
221
222 void MediaServiceThrottler::SetCrashListenerTaskRunnerForTesting(
223 scoped_refptr<base::SingleThreadTaskRunner> crash_listener_task_runner) {
224 // Set the task runner so |crash_listener_| be deleted on the right thread.
225 crash_listener_task_runner_ = crash_listener_task_runner;
226
227 // Re-create the crash listener.
228 crash_listener_ = base::MakeUnique<MediaServerCrashListener>(
229 base::Closure(), crash_listener_task_runner_);
230 }
231
232 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698