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

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

Issue 2471903002: Add MediaServiceThrottler (Closed)
Patch Set: Release resources after 1 min. +2 UTS. 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 {
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698