Bug 1932613 - temporarily disable browser_ml_end_to_end.js for permanent failures...
[gecko.git] / xpcom / threads / IdlePeriodState.cpp
blobff1a9f4096c31d8fef21516c23d8fe01b1c58df1
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/AppShutdown.h"
8 #include "mozilla/IdlePeriodState.h"
9 #include "mozilla/StaticPrefs_idle_period.h"
10 #include "mozilla/ipc/IdleSchedulerChild.h"
11 #include "mozilla/dom/ContentChild.h"
12 #include "nsIIdlePeriod.h"
13 #include "nsThreadManager.h"
14 #include "nsXPCOM.h"
15 #include "nsXULAppAPI.h"
17 static uint64_t sIdleRequestCounter = 0;
19 namespace mozilla {
21 IdlePeriodState::IdlePeriodState(already_AddRefed<nsIIdlePeriod>&& aIdlePeriod)
22 : mIdlePeriod(aIdlePeriod) {
23 MOZ_ASSERT(NS_IsMainThread(),
24 "Why are we touching idle state off the main thread?");
27 IdlePeriodState::~IdlePeriodState() {
28 MOZ_ASSERT(NS_IsMainThread(),
29 "Why are we touching idle state off the main thread?");
30 if (mIdleScheduler) {
31 mIdleScheduler->Disconnect();
35 size_t IdlePeriodState::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const {
36 size_t n = 0;
37 if (mIdlePeriod) {
38 n += aMallocSizeOf(mIdlePeriod);
41 return n;
44 void IdlePeriodState::FlagNotIdle() {
45 MOZ_ASSERT(NS_IsMainThread(),
46 "Why are we touching idle state off the main thread?");
48 EnsureIsActive();
49 if (mIdleToken && mIdleToken < TimeStamp::Now()) {
50 ClearIdleToken();
54 void IdlePeriodState::RanOutOfTasks(const MutexAutoUnlock& aProofOfUnlock) {
55 MOZ_ASSERT(NS_IsMainThread(),
56 "Why are we touching idle state off the main thread?");
57 MOZ_ASSERT(!mHasPendingEventsPromisedIdleEvent);
58 EnsureIsPaused(aProofOfUnlock);
59 ClearIdleToken();
62 TimeStamp IdlePeriodState::GetIdleDeadlineInternal(
63 bool aIsPeek, const MutexAutoUnlock& aProofOfUnlock) {
64 MOZ_ASSERT(NS_IsMainThread(),
65 "Why are we touching idle state off the main thread?");
67 bool shuttingDown;
68 TimeStamp localIdleDeadline =
69 GetLocalIdleDeadline(shuttingDown, aProofOfUnlock);
70 if (!localIdleDeadline) {
71 if (!aIsPeek) {
72 EnsureIsPaused(aProofOfUnlock);
73 ClearIdleToken();
75 return TimeStamp();
78 TimeStamp idleDeadline =
79 mHasPendingEventsPromisedIdleEvent || shuttingDown
80 ? localIdleDeadline
81 : GetIdleToken(localIdleDeadline, aProofOfUnlock);
82 if (!idleDeadline) {
83 if (!aIsPeek) {
84 EnsureIsPaused(aProofOfUnlock);
86 // Don't call ClearIdleToken() here, since we may have a pending
87 // request already.
89 // RequestIdleToken can do all sorts of IPC stuff that might
90 // take mutexes. This is one reason why we need the
91 // MutexAutoUnlock reference!
92 RequestIdleToken(localIdleDeadline);
94 return TimeStamp();
97 if (!aIsPeek) {
98 EnsureIsActive();
100 return idleDeadline;
103 TimeStamp IdlePeriodState::GetLocalIdleDeadline(
104 bool& aShuttingDown, const MutexAutoUnlock& aProofOfUnlock) {
105 MOZ_ASSERT(NS_IsMainThread(),
106 "Why are we touching idle state off the main thread?");
107 // If we are shutting down, we won't honor the idle period, and we will
108 // always process idle runnables. This will ensure that the idle queue
109 // gets exhausted at shutdown time to prevent intermittently leaking
110 // some runnables inside that queue and even worse potentially leaving
111 // some important cleanup work unfinished.
112 if (AppShutdown::IsInOrBeyond(ShutdownPhase::XPCOMShutdownThreads) ||
113 nsThreadManager::get().GetCurrentThread()->ShuttingDown()) {
114 aShuttingDown = true;
115 return TimeStamp::Now();
118 aShuttingDown = false;
119 TimeStamp idleDeadline;
120 // This GetIdlePeriodHint() call is the reason we need a MutexAutoUnlock here.
121 mIdlePeriod->GetIdlePeriodHint(&idleDeadline);
123 // If HasPendingEvents() has been called and it has returned true because of
124 // pending idle events, there is a risk that we may decide here that we aren't
125 // idle and return null, in which case HasPendingEvents() has effectively
126 // lied. Since we can't go back and fix the past, we have to adjust what we
127 // do here and forcefully pick the idle queue task here. Note that this means
128 // that we are choosing to run a task from the idle queue when we would
129 // normally decide that we aren't in an idle period, but this can only happen
130 // if we fall out of the idle period in between the call to HasPendingEvents()
131 // and here, which should hopefully be quite rare. We are effectively
132 // choosing to prioritize the sanity of our API semantics over the optimal
133 // scheduling.
134 if (!mHasPendingEventsPromisedIdleEvent &&
135 (!idleDeadline || idleDeadline < TimeStamp::Now())) {
136 return TimeStamp();
138 if (mHasPendingEventsPromisedIdleEvent && !idleDeadline) {
139 // If HasPendingEvents() has been called and it has returned true, but we're
140 // no longer in the idle period, we must return a valid timestamp to pretend
141 // that we are still in the idle period.
142 return TimeStamp::Now();
144 return idleDeadline;
147 TimeStamp IdlePeriodState::GetIdleToken(TimeStamp aLocalIdlePeriodHint,
148 const MutexAutoUnlock& aProofOfUnlock) {
149 MOZ_ASSERT(NS_IsMainThread(),
150 "Why are we touching idle state off the main thread?");
152 if (!ShouldGetIdleToken()) {
153 // If the process was in background, it may have an idle token, but it can
154 // be cleared now.
155 ClearIdleToken();
156 return aLocalIdlePeriodHint;
159 if (mIdleToken) {
160 TimeStamp now = TimeStamp::Now();
161 if (mIdleToken < now) {
162 ClearIdleToken();
163 return mIdleToken;
165 return mIdleToken < aLocalIdlePeriodHint ? mIdleToken
166 : aLocalIdlePeriodHint;
168 return TimeStamp();
171 void IdlePeriodState::RequestIdleToken(TimeStamp aLocalIdlePeriodHint) {
172 MOZ_ASSERT(NS_IsMainThread(),
173 "Why are we touching idle state off the main thread?");
174 MOZ_ASSERT(!mActive);
176 if (!mIdleScheduler && ShouldGetIdleToken()) {
177 // For now cross-process idle scheduler is supported only on the main
178 // threads of the child processes.
179 mIdleScheduler = ipc::IdleSchedulerChild::GetMainThreadIdleScheduler();
180 if (mIdleScheduler) {
181 mIdleScheduler->Init(this);
185 if (mIdleScheduler && !mIdleRequestId) {
186 TimeStamp now = TimeStamp::Now();
187 if (aLocalIdlePeriodHint <= now) {
188 return;
191 mIdleRequestId = ++sIdleRequestCounter;
192 mIdleScheduler->SendRequestIdleTime(mIdleRequestId,
193 aLocalIdlePeriodHint - now);
197 void IdlePeriodState::SetIdleToken(uint64_t aId, TimeDuration aDuration) {
198 MOZ_ASSERT(NS_IsMainThread(),
199 "Why are we touching idle state off the main thread?");
201 // We check the request ID. It's possible that the server may be granting a
202 // an ealier request that the client has since cancelled and re-requested.
203 if (mIdleRequestId == aId) {
204 mIdleToken = TimeStamp::Now() + aDuration;
208 void IdlePeriodState::SetActive() {
209 MOZ_ASSERT(NS_IsMainThread(),
210 "Why are we touching idle state off the main thread?");
211 MOZ_ASSERT(!mActive);
212 if (mIdleScheduler) {
213 mIdleScheduler->SetActive();
215 mActive = true;
218 void IdlePeriodState::SetPaused(const MutexAutoUnlock& aProofOfUnlock) {
219 MOZ_ASSERT(NS_IsMainThread(),
220 "Why are we touching idle state off the main thread?");
221 MOZ_ASSERT(mActive);
222 if (mIdleScheduler && mIdleScheduler->SetPaused()) {
223 // We may have gotten a free cpu core for running idle tasks.
224 // We don't try to catch the case when there are prioritized processes
225 // running.
227 // This SendSchedule call is why we need the MutexAutoUnlock here, because
228 // IPC can do weird things with mutexes.
229 mIdleScheduler->SendSchedule();
231 mActive = false;
234 void IdlePeriodState::ClearIdleToken() {
235 MOZ_ASSERT(NS_IsMainThread(),
236 "Why are we touching idle state off the main thread?");
238 if (mIdleRequestId) {
239 if (mIdleScheduler) {
240 // This SendIdleTimeUsed call is why we need to not be holding
241 // any locks here, because IPC can do weird things with mutexes.
242 // Ideally we'd have a MutexAutoUnlock& reference here, but some
243 // callers end up here while just not holding any locks at all.
244 mIdleScheduler->SendIdleTimeUsed(mIdleRequestId);
246 mIdleRequestId = 0;
247 mIdleToken = TimeStamp();
251 bool IdlePeriodState::ShouldGetIdleToken() {
252 return StaticPrefs::idle_period_cross_process_scheduling() &&
253 dom::ContentChild::GetSingleton() &&
254 dom::ContentChild::GetSingleton()->GetProcessPriority() <
255 hal::ProcessPriority::PROCESS_PRIORITY_FOREGROUND;
257 } // namespace mozilla