gecko-dev/xpcom/threads/IdlePeriodState.cpp

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

252 строки
8.3 KiB
C++
Исходник Обычный вид История

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/IdlePeriodState.h"
#include "mozilla/StaticPrefs_idle_period.h"
#include "mozilla/ipc/IdleSchedulerChild.h"
#include "nsIIdlePeriod.h"
#include "nsThreadManager.h"
#include "nsThreadUtils.h"
#include "nsXPCOM.h"
#include "nsXULAppAPI.h"
static uint64_t sIdleRequestCounter = 0;
namespace mozilla {
IdlePeriodState::IdlePeriodState(already_AddRefed<nsIIdlePeriod>&& aIdlePeriod)
: mIdlePeriod(aIdlePeriod) {
MOZ_ASSERT(NS_IsMainThread(),
"Why are we touching idle state off the main thread?");
}
IdlePeriodState::~IdlePeriodState() {
MOZ_ASSERT(NS_IsMainThread(),
"Why are we touching idle state off the main thread?");
if (mIdleScheduler) {
mIdleScheduler->Disconnect();
}
}
size_t IdlePeriodState::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const {
size_t n = 0;
if (mIdlePeriod) {
n += aMallocSizeOf(mIdlePeriod);
}
return n;
}
Bug 1597158 part 4. Stop unlocking the provided mutex in IdlePeriodState methods. r=smaug The basic idea, suggested by Olli, is that we can try to get a runnable in ThreadEventQueue::GetEvent, and if that does not produce anything unlock our mutex, do whatever idle state updates we need to do, re-lock our mutex. Then always we need to try getting a runnable again, because a non-idle runnable might have gotten queued while we had the lock unlocked. So we can't sleep on our mutex, in the mayWait case, unless we try to get a runnable again first. My notes on the current (pre this patch) unlocking setup follow. ------------------------------------------------------------ There are four places where we currently unlock: 1) IdlePeriodState::GetIdleDeadlineInternal. Needed only when !aIsPeek, to RequestIdleToken, which can do IPC. The only caller, via GetDeadlineForIdleTask, is PrioritizedEventQueue::GetEvent and only when we selected the idle or deferred queue. We need this to set the proper deadline on the idle event. In the cases when this unlock happens we currently _never_ return an idle event, because if we got here that means that we do not have an idle token. 2) IdlePeriodState::GetLocalIdleDeadline. Needs to unlock to get the idle period hint. The can get called from GetIdleDeadlineInternal in _both_ cases: peek and get. The callstack for the get case is covered above. The peek case is called from PrioritizedEventQueue::HasReadyEvent which is called from ThreadEventQueue::HasPendingEvent. 3) IdlePeriodState::SetPaused, because it sends an IPC message. This is only called from EnsureIsPaused, which is called from: - IdlePeriodState::GetIdleDeadlineInternal. Only in the !aIsPeek case. - IdlePeriodState::RanOutOfTasks called from: - PrioritizedEventQueue::GetEvent if we fell into the idle case and our queues are empty. - PrioritizedEventQueue::DidRunEvent if we are empty. 4) IdlePeriodState::ClearIdleToken because it sends an IPC message. This is called from: - IdlePeriodState::RanOutOfTasks; see SetPaused. - IdlePeriodState::GetIdleDeadlineInternal like EnsureIsPaused. - IdlePeriodState::GetIdleToken if token is in the past. This is only called from GetIdleDeadlineInternal, both cases. - IdlePeriodState::FlagNotIdle called from PrioritizedEventQueue::GetEvent if we find an event in a non-idle queue. Or rewriting in terms of API entrypoints on IdlePeriodState that might need to unlock: * Anything to do with getting deadlines, whether we are peeking or getting. Basically, if we need an updated deadline we need to unlock. * When we have detected we are completely out of tasks (idle or not) to run. Right now we do that when either we're asked for an event and don't have one or if we run an event and are empty after that (before unlocking!). But the unlocking or not happens in nsThreadEventQueue::DidRunEvent, so separately from the getting of the event. In particular, we are unlocked before we enter DidRunEvent, and unlock again before we return from it, so we can do whatever updates we want there. * When we have detected that we have a non-idle event to run; this calls FlagNotIdle. Differential Revision: https://phabricator.services.mozilla.com/D53631 --HG-- extra : moz-landing-system : lando
2019-11-22 17:06:17 +03:00
void IdlePeriodState::FlagNotIdle() {
MOZ_ASSERT(NS_IsMainThread(),
"Why are we touching idle state off the main thread?");
EnsureIsActive();
if (mIdleToken && mIdleToken < TimeStamp::Now()) {
Bug 1597158 part 4. Stop unlocking the provided mutex in IdlePeriodState methods. r=smaug The basic idea, suggested by Olli, is that we can try to get a runnable in ThreadEventQueue::GetEvent, and if that does not produce anything unlock our mutex, do whatever idle state updates we need to do, re-lock our mutex. Then always we need to try getting a runnable again, because a non-idle runnable might have gotten queued while we had the lock unlocked. So we can't sleep on our mutex, in the mayWait case, unless we try to get a runnable again first. My notes on the current (pre this patch) unlocking setup follow. ------------------------------------------------------------ There are four places where we currently unlock: 1) IdlePeriodState::GetIdleDeadlineInternal. Needed only when !aIsPeek, to RequestIdleToken, which can do IPC. The only caller, via GetDeadlineForIdleTask, is PrioritizedEventQueue::GetEvent and only when we selected the idle or deferred queue. We need this to set the proper deadline on the idle event. In the cases when this unlock happens we currently _never_ return an idle event, because if we got here that means that we do not have an idle token. 2) IdlePeriodState::GetLocalIdleDeadline. Needs to unlock to get the idle period hint. The can get called from GetIdleDeadlineInternal in _both_ cases: peek and get. The callstack for the get case is covered above. The peek case is called from PrioritizedEventQueue::HasReadyEvent which is called from ThreadEventQueue::HasPendingEvent. 3) IdlePeriodState::SetPaused, because it sends an IPC message. This is only called from EnsureIsPaused, which is called from: - IdlePeriodState::GetIdleDeadlineInternal. Only in the !aIsPeek case. - IdlePeriodState::RanOutOfTasks called from: - PrioritizedEventQueue::GetEvent if we fell into the idle case and our queues are empty. - PrioritizedEventQueue::DidRunEvent if we are empty. 4) IdlePeriodState::ClearIdleToken because it sends an IPC message. This is called from: - IdlePeriodState::RanOutOfTasks; see SetPaused. - IdlePeriodState::GetIdleDeadlineInternal like EnsureIsPaused. - IdlePeriodState::GetIdleToken if token is in the past. This is only called from GetIdleDeadlineInternal, both cases. - IdlePeriodState::FlagNotIdle called from PrioritizedEventQueue::GetEvent if we find an event in a non-idle queue. Or rewriting in terms of API entrypoints on IdlePeriodState that might need to unlock: * Anything to do with getting deadlines, whether we are peeking or getting. Basically, if we need an updated deadline we need to unlock. * When we have detected we are completely out of tasks (idle or not) to run. Right now we do that when either we're asked for an event and don't have one or if we run an event and are empty after that (before unlocking!). But the unlocking or not happens in nsThreadEventQueue::DidRunEvent, so separately from the getting of the event. In particular, we are unlocked before we enter DidRunEvent, and unlock again before we return from it, so we can do whatever updates we want there. * When we have detected that we have a non-idle event to run; this calls FlagNotIdle. Differential Revision: https://phabricator.services.mozilla.com/D53631 --HG-- extra : moz-landing-system : lando
2019-11-22 17:06:17 +03:00
ClearIdleToken();
}
}
Bug 1597158 part 4. Stop unlocking the provided mutex in IdlePeriodState methods. r=smaug The basic idea, suggested by Olli, is that we can try to get a runnable in ThreadEventQueue::GetEvent, and if that does not produce anything unlock our mutex, do whatever idle state updates we need to do, re-lock our mutex. Then always we need to try getting a runnable again, because a non-idle runnable might have gotten queued while we had the lock unlocked. So we can't sleep on our mutex, in the mayWait case, unless we try to get a runnable again first. My notes on the current (pre this patch) unlocking setup follow. ------------------------------------------------------------ There are four places where we currently unlock: 1) IdlePeriodState::GetIdleDeadlineInternal. Needed only when !aIsPeek, to RequestIdleToken, which can do IPC. The only caller, via GetDeadlineForIdleTask, is PrioritizedEventQueue::GetEvent and only when we selected the idle or deferred queue. We need this to set the proper deadline on the idle event. In the cases when this unlock happens we currently _never_ return an idle event, because if we got here that means that we do not have an idle token. 2) IdlePeriodState::GetLocalIdleDeadline. Needs to unlock to get the idle period hint. The can get called from GetIdleDeadlineInternal in _both_ cases: peek and get. The callstack for the get case is covered above. The peek case is called from PrioritizedEventQueue::HasReadyEvent which is called from ThreadEventQueue::HasPendingEvent. 3) IdlePeriodState::SetPaused, because it sends an IPC message. This is only called from EnsureIsPaused, which is called from: - IdlePeriodState::GetIdleDeadlineInternal. Only in the !aIsPeek case. - IdlePeriodState::RanOutOfTasks called from: - PrioritizedEventQueue::GetEvent if we fell into the idle case and our queues are empty. - PrioritizedEventQueue::DidRunEvent if we are empty. 4) IdlePeriodState::ClearIdleToken because it sends an IPC message. This is called from: - IdlePeriodState::RanOutOfTasks; see SetPaused. - IdlePeriodState::GetIdleDeadlineInternal like EnsureIsPaused. - IdlePeriodState::GetIdleToken if token is in the past. This is only called from GetIdleDeadlineInternal, both cases. - IdlePeriodState::FlagNotIdle called from PrioritizedEventQueue::GetEvent if we find an event in a non-idle queue. Or rewriting in terms of API entrypoints on IdlePeriodState that might need to unlock: * Anything to do with getting deadlines, whether we are peeking or getting. Basically, if we need an updated deadline we need to unlock. * When we have detected we are completely out of tasks (idle or not) to run. Right now we do that when either we're asked for an event and don't have one or if we run an event and are empty after that (before unlocking!). But the unlocking or not happens in nsThreadEventQueue::DidRunEvent, so separately from the getting of the event. In particular, we are unlocked before we enter DidRunEvent, and unlock again before we return from it, so we can do whatever updates we want there. * When we have detected that we have a non-idle event to run; this calls FlagNotIdle. Differential Revision: https://phabricator.services.mozilla.com/D53631 --HG-- extra : moz-landing-system : lando
2019-11-22 17:06:17 +03:00
void IdlePeriodState::RanOutOfTasks(const MutexAutoUnlock& aProofOfUnlock) {
MOZ_ASSERT(NS_IsMainThread(),
"Why are we touching idle state off the main thread?");
MOZ_ASSERT(!mHasPendingEventsPromisedIdleEvent);
Bug 1597158 part 4. Stop unlocking the provided mutex in IdlePeriodState methods. r=smaug The basic idea, suggested by Olli, is that we can try to get a runnable in ThreadEventQueue::GetEvent, and if that does not produce anything unlock our mutex, do whatever idle state updates we need to do, re-lock our mutex. Then always we need to try getting a runnable again, because a non-idle runnable might have gotten queued while we had the lock unlocked. So we can't sleep on our mutex, in the mayWait case, unless we try to get a runnable again first. My notes on the current (pre this patch) unlocking setup follow. ------------------------------------------------------------ There are four places where we currently unlock: 1) IdlePeriodState::GetIdleDeadlineInternal. Needed only when !aIsPeek, to RequestIdleToken, which can do IPC. The only caller, via GetDeadlineForIdleTask, is PrioritizedEventQueue::GetEvent and only when we selected the idle or deferred queue. We need this to set the proper deadline on the idle event. In the cases when this unlock happens we currently _never_ return an idle event, because if we got here that means that we do not have an idle token. 2) IdlePeriodState::GetLocalIdleDeadline. Needs to unlock to get the idle period hint. The can get called from GetIdleDeadlineInternal in _both_ cases: peek and get. The callstack for the get case is covered above. The peek case is called from PrioritizedEventQueue::HasReadyEvent which is called from ThreadEventQueue::HasPendingEvent. 3) IdlePeriodState::SetPaused, because it sends an IPC message. This is only called from EnsureIsPaused, which is called from: - IdlePeriodState::GetIdleDeadlineInternal. Only in the !aIsPeek case. - IdlePeriodState::RanOutOfTasks called from: - PrioritizedEventQueue::GetEvent if we fell into the idle case and our queues are empty. - PrioritizedEventQueue::DidRunEvent if we are empty. 4) IdlePeriodState::ClearIdleToken because it sends an IPC message. This is called from: - IdlePeriodState::RanOutOfTasks; see SetPaused. - IdlePeriodState::GetIdleDeadlineInternal like EnsureIsPaused. - IdlePeriodState::GetIdleToken if token is in the past. This is only called from GetIdleDeadlineInternal, both cases. - IdlePeriodState::FlagNotIdle called from PrioritizedEventQueue::GetEvent if we find an event in a non-idle queue. Or rewriting in terms of API entrypoints on IdlePeriodState that might need to unlock: * Anything to do with getting deadlines, whether we are peeking or getting. Basically, if we need an updated deadline we need to unlock. * When we have detected we are completely out of tasks (idle or not) to run. Right now we do that when either we're asked for an event and don't have one or if we run an event and are empty after that (before unlocking!). But the unlocking or not happens in nsThreadEventQueue::DidRunEvent, so separately from the getting of the event. In particular, we are unlocked before we enter DidRunEvent, and unlock again before we return from it, so we can do whatever updates we want there. * When we have detected that we have a non-idle event to run; this calls FlagNotIdle. Differential Revision: https://phabricator.services.mozilla.com/D53631 --HG-- extra : moz-landing-system : lando
2019-11-22 17:06:17 +03:00
EnsureIsPaused(aProofOfUnlock);
ClearIdleToken();
}
Bug 1597158 part 4. Stop unlocking the provided mutex in IdlePeriodState methods. r=smaug The basic idea, suggested by Olli, is that we can try to get a runnable in ThreadEventQueue::GetEvent, and if that does not produce anything unlock our mutex, do whatever idle state updates we need to do, re-lock our mutex. Then always we need to try getting a runnable again, because a non-idle runnable might have gotten queued while we had the lock unlocked. So we can't sleep on our mutex, in the mayWait case, unless we try to get a runnable again first. My notes on the current (pre this patch) unlocking setup follow. ------------------------------------------------------------ There are four places where we currently unlock: 1) IdlePeriodState::GetIdleDeadlineInternal. Needed only when !aIsPeek, to RequestIdleToken, which can do IPC. The only caller, via GetDeadlineForIdleTask, is PrioritizedEventQueue::GetEvent and only when we selected the idle or deferred queue. We need this to set the proper deadline on the idle event. In the cases when this unlock happens we currently _never_ return an idle event, because if we got here that means that we do not have an idle token. 2) IdlePeriodState::GetLocalIdleDeadline. Needs to unlock to get the idle period hint. The can get called from GetIdleDeadlineInternal in _both_ cases: peek and get. The callstack for the get case is covered above. The peek case is called from PrioritizedEventQueue::HasReadyEvent which is called from ThreadEventQueue::HasPendingEvent. 3) IdlePeriodState::SetPaused, because it sends an IPC message. This is only called from EnsureIsPaused, which is called from: - IdlePeriodState::GetIdleDeadlineInternal. Only in the !aIsPeek case. - IdlePeriodState::RanOutOfTasks called from: - PrioritizedEventQueue::GetEvent if we fell into the idle case and our queues are empty. - PrioritizedEventQueue::DidRunEvent if we are empty. 4) IdlePeriodState::ClearIdleToken because it sends an IPC message. This is called from: - IdlePeriodState::RanOutOfTasks; see SetPaused. - IdlePeriodState::GetIdleDeadlineInternal like EnsureIsPaused. - IdlePeriodState::GetIdleToken if token is in the past. This is only called from GetIdleDeadlineInternal, both cases. - IdlePeriodState::FlagNotIdle called from PrioritizedEventQueue::GetEvent if we find an event in a non-idle queue. Or rewriting in terms of API entrypoints on IdlePeriodState that might need to unlock: * Anything to do with getting deadlines, whether we are peeking or getting. Basically, if we need an updated deadline we need to unlock. * When we have detected we are completely out of tasks (idle or not) to run. Right now we do that when either we're asked for an event and don't have one or if we run an event and are empty after that (before unlocking!). But the unlocking or not happens in nsThreadEventQueue::DidRunEvent, so separately from the getting of the event. In particular, we are unlocked before we enter DidRunEvent, and unlock again before we return from it, so we can do whatever updates we want there. * When we have detected that we have a non-idle event to run; this calls FlagNotIdle. Differential Revision: https://phabricator.services.mozilla.com/D53631 --HG-- extra : moz-landing-system : lando
2019-11-22 17:06:17 +03:00
TimeStamp IdlePeriodState::GetIdleDeadlineInternal(
bool aIsPeek, const MutexAutoUnlock& aProofOfUnlock) {
MOZ_ASSERT(NS_IsMainThread(),
"Why are we touching idle state off the main thread?");
bool shuttingDown;
TimeStamp localIdleDeadline =
Bug 1597158 part 4. Stop unlocking the provided mutex in IdlePeriodState methods. r=smaug The basic idea, suggested by Olli, is that we can try to get a runnable in ThreadEventQueue::GetEvent, and if that does not produce anything unlock our mutex, do whatever idle state updates we need to do, re-lock our mutex. Then always we need to try getting a runnable again, because a non-idle runnable might have gotten queued while we had the lock unlocked. So we can't sleep on our mutex, in the mayWait case, unless we try to get a runnable again first. My notes on the current (pre this patch) unlocking setup follow. ------------------------------------------------------------ There are four places where we currently unlock: 1) IdlePeriodState::GetIdleDeadlineInternal. Needed only when !aIsPeek, to RequestIdleToken, which can do IPC. The only caller, via GetDeadlineForIdleTask, is PrioritizedEventQueue::GetEvent and only when we selected the idle or deferred queue. We need this to set the proper deadline on the idle event. In the cases when this unlock happens we currently _never_ return an idle event, because if we got here that means that we do not have an idle token. 2) IdlePeriodState::GetLocalIdleDeadline. Needs to unlock to get the idle period hint. The can get called from GetIdleDeadlineInternal in _both_ cases: peek and get. The callstack for the get case is covered above. The peek case is called from PrioritizedEventQueue::HasReadyEvent which is called from ThreadEventQueue::HasPendingEvent. 3) IdlePeriodState::SetPaused, because it sends an IPC message. This is only called from EnsureIsPaused, which is called from: - IdlePeriodState::GetIdleDeadlineInternal. Only in the !aIsPeek case. - IdlePeriodState::RanOutOfTasks called from: - PrioritizedEventQueue::GetEvent if we fell into the idle case and our queues are empty. - PrioritizedEventQueue::DidRunEvent if we are empty. 4) IdlePeriodState::ClearIdleToken because it sends an IPC message. This is called from: - IdlePeriodState::RanOutOfTasks; see SetPaused. - IdlePeriodState::GetIdleDeadlineInternal like EnsureIsPaused. - IdlePeriodState::GetIdleToken if token is in the past. This is only called from GetIdleDeadlineInternal, both cases. - IdlePeriodState::FlagNotIdle called from PrioritizedEventQueue::GetEvent if we find an event in a non-idle queue. Or rewriting in terms of API entrypoints on IdlePeriodState that might need to unlock: * Anything to do with getting deadlines, whether we are peeking or getting. Basically, if we need an updated deadline we need to unlock. * When we have detected we are completely out of tasks (idle or not) to run. Right now we do that when either we're asked for an event and don't have one or if we run an event and are empty after that (before unlocking!). But the unlocking or not happens in nsThreadEventQueue::DidRunEvent, so separately from the getting of the event. In particular, we are unlocked before we enter DidRunEvent, and unlock again before we return from it, so we can do whatever updates we want there. * When we have detected that we have a non-idle event to run; this calls FlagNotIdle. Differential Revision: https://phabricator.services.mozilla.com/D53631 --HG-- extra : moz-landing-system : lando
2019-11-22 17:06:17 +03:00
GetLocalIdleDeadline(shuttingDown, aProofOfUnlock);
if (!localIdleDeadline) {
if (!aIsPeek) {
Bug 1597158 part 4. Stop unlocking the provided mutex in IdlePeriodState methods. r=smaug The basic idea, suggested by Olli, is that we can try to get a runnable in ThreadEventQueue::GetEvent, and if that does not produce anything unlock our mutex, do whatever idle state updates we need to do, re-lock our mutex. Then always we need to try getting a runnable again, because a non-idle runnable might have gotten queued while we had the lock unlocked. So we can't sleep on our mutex, in the mayWait case, unless we try to get a runnable again first. My notes on the current (pre this patch) unlocking setup follow. ------------------------------------------------------------ There are four places where we currently unlock: 1) IdlePeriodState::GetIdleDeadlineInternal. Needed only when !aIsPeek, to RequestIdleToken, which can do IPC. The only caller, via GetDeadlineForIdleTask, is PrioritizedEventQueue::GetEvent and only when we selected the idle or deferred queue. We need this to set the proper deadline on the idle event. In the cases when this unlock happens we currently _never_ return an idle event, because if we got here that means that we do not have an idle token. 2) IdlePeriodState::GetLocalIdleDeadline. Needs to unlock to get the idle period hint. The can get called from GetIdleDeadlineInternal in _both_ cases: peek and get. The callstack for the get case is covered above. The peek case is called from PrioritizedEventQueue::HasReadyEvent which is called from ThreadEventQueue::HasPendingEvent. 3) IdlePeriodState::SetPaused, because it sends an IPC message. This is only called from EnsureIsPaused, which is called from: - IdlePeriodState::GetIdleDeadlineInternal. Only in the !aIsPeek case. - IdlePeriodState::RanOutOfTasks called from: - PrioritizedEventQueue::GetEvent if we fell into the idle case and our queues are empty. - PrioritizedEventQueue::DidRunEvent if we are empty. 4) IdlePeriodState::ClearIdleToken because it sends an IPC message. This is called from: - IdlePeriodState::RanOutOfTasks; see SetPaused. - IdlePeriodState::GetIdleDeadlineInternal like EnsureIsPaused. - IdlePeriodState::GetIdleToken if token is in the past. This is only called from GetIdleDeadlineInternal, both cases. - IdlePeriodState::FlagNotIdle called from PrioritizedEventQueue::GetEvent if we find an event in a non-idle queue. Or rewriting in terms of API entrypoints on IdlePeriodState that might need to unlock: * Anything to do with getting deadlines, whether we are peeking or getting. Basically, if we need an updated deadline we need to unlock. * When we have detected we are completely out of tasks (idle or not) to run. Right now we do that when either we're asked for an event and don't have one or if we run an event and are empty after that (before unlocking!). But the unlocking or not happens in nsThreadEventQueue::DidRunEvent, so separately from the getting of the event. In particular, we are unlocked before we enter DidRunEvent, and unlock again before we return from it, so we can do whatever updates we want there. * When we have detected that we have a non-idle event to run; this calls FlagNotIdle. Differential Revision: https://phabricator.services.mozilla.com/D53631 --HG-- extra : moz-landing-system : lando
2019-11-22 17:06:17 +03:00
EnsureIsPaused(aProofOfUnlock);
ClearIdleToken();
}
return TimeStamp();
}
TimeStamp idleDeadline =
mHasPendingEventsPromisedIdleEvent || shuttingDown
? localIdleDeadline
Bug 1597158 part 4. Stop unlocking the provided mutex in IdlePeriodState methods. r=smaug The basic idea, suggested by Olli, is that we can try to get a runnable in ThreadEventQueue::GetEvent, and if that does not produce anything unlock our mutex, do whatever idle state updates we need to do, re-lock our mutex. Then always we need to try getting a runnable again, because a non-idle runnable might have gotten queued while we had the lock unlocked. So we can't sleep on our mutex, in the mayWait case, unless we try to get a runnable again first. My notes on the current (pre this patch) unlocking setup follow. ------------------------------------------------------------ There are four places where we currently unlock: 1) IdlePeriodState::GetIdleDeadlineInternal. Needed only when !aIsPeek, to RequestIdleToken, which can do IPC. The only caller, via GetDeadlineForIdleTask, is PrioritizedEventQueue::GetEvent and only when we selected the idle or deferred queue. We need this to set the proper deadline on the idle event. In the cases when this unlock happens we currently _never_ return an idle event, because if we got here that means that we do not have an idle token. 2) IdlePeriodState::GetLocalIdleDeadline. Needs to unlock to get the idle period hint. The can get called from GetIdleDeadlineInternal in _both_ cases: peek and get. The callstack for the get case is covered above. The peek case is called from PrioritizedEventQueue::HasReadyEvent which is called from ThreadEventQueue::HasPendingEvent. 3) IdlePeriodState::SetPaused, because it sends an IPC message. This is only called from EnsureIsPaused, which is called from: - IdlePeriodState::GetIdleDeadlineInternal. Only in the !aIsPeek case. - IdlePeriodState::RanOutOfTasks called from: - PrioritizedEventQueue::GetEvent if we fell into the idle case and our queues are empty. - PrioritizedEventQueue::DidRunEvent if we are empty. 4) IdlePeriodState::ClearIdleToken because it sends an IPC message. This is called from: - IdlePeriodState::RanOutOfTasks; see SetPaused. - IdlePeriodState::GetIdleDeadlineInternal like EnsureIsPaused. - IdlePeriodState::GetIdleToken if token is in the past. This is only called from GetIdleDeadlineInternal, both cases. - IdlePeriodState::FlagNotIdle called from PrioritizedEventQueue::GetEvent if we find an event in a non-idle queue. Or rewriting in terms of API entrypoints on IdlePeriodState that might need to unlock: * Anything to do with getting deadlines, whether we are peeking or getting. Basically, if we need an updated deadline we need to unlock. * When we have detected we are completely out of tasks (idle or not) to run. Right now we do that when either we're asked for an event and don't have one or if we run an event and are empty after that (before unlocking!). But the unlocking or not happens in nsThreadEventQueue::DidRunEvent, so separately from the getting of the event. In particular, we are unlocked before we enter DidRunEvent, and unlock again before we return from it, so we can do whatever updates we want there. * When we have detected that we have a non-idle event to run; this calls FlagNotIdle. Differential Revision: https://phabricator.services.mozilla.com/D53631 --HG-- extra : moz-landing-system : lando
2019-11-22 17:06:17 +03:00
: GetIdleToken(localIdleDeadline, aProofOfUnlock);
if (!idleDeadline) {
if (!aIsPeek) {
Bug 1597158 part 4. Stop unlocking the provided mutex in IdlePeriodState methods. r=smaug The basic idea, suggested by Olli, is that we can try to get a runnable in ThreadEventQueue::GetEvent, and if that does not produce anything unlock our mutex, do whatever idle state updates we need to do, re-lock our mutex. Then always we need to try getting a runnable again, because a non-idle runnable might have gotten queued while we had the lock unlocked. So we can't sleep on our mutex, in the mayWait case, unless we try to get a runnable again first. My notes on the current (pre this patch) unlocking setup follow. ------------------------------------------------------------ There are four places where we currently unlock: 1) IdlePeriodState::GetIdleDeadlineInternal. Needed only when !aIsPeek, to RequestIdleToken, which can do IPC. The only caller, via GetDeadlineForIdleTask, is PrioritizedEventQueue::GetEvent and only when we selected the idle or deferred queue. We need this to set the proper deadline on the idle event. In the cases when this unlock happens we currently _never_ return an idle event, because if we got here that means that we do not have an idle token. 2) IdlePeriodState::GetLocalIdleDeadline. Needs to unlock to get the idle period hint. The can get called from GetIdleDeadlineInternal in _both_ cases: peek and get. The callstack for the get case is covered above. The peek case is called from PrioritizedEventQueue::HasReadyEvent which is called from ThreadEventQueue::HasPendingEvent. 3) IdlePeriodState::SetPaused, because it sends an IPC message. This is only called from EnsureIsPaused, which is called from: - IdlePeriodState::GetIdleDeadlineInternal. Only in the !aIsPeek case. - IdlePeriodState::RanOutOfTasks called from: - PrioritizedEventQueue::GetEvent if we fell into the idle case and our queues are empty. - PrioritizedEventQueue::DidRunEvent if we are empty. 4) IdlePeriodState::ClearIdleToken because it sends an IPC message. This is called from: - IdlePeriodState::RanOutOfTasks; see SetPaused. - IdlePeriodState::GetIdleDeadlineInternal like EnsureIsPaused. - IdlePeriodState::GetIdleToken if token is in the past. This is only called from GetIdleDeadlineInternal, both cases. - IdlePeriodState::FlagNotIdle called from PrioritizedEventQueue::GetEvent if we find an event in a non-idle queue. Or rewriting in terms of API entrypoints on IdlePeriodState that might need to unlock: * Anything to do with getting deadlines, whether we are peeking or getting. Basically, if we need an updated deadline we need to unlock. * When we have detected we are completely out of tasks (idle or not) to run. Right now we do that when either we're asked for an event and don't have one or if we run an event and are empty after that (before unlocking!). But the unlocking or not happens in nsThreadEventQueue::DidRunEvent, so separately from the getting of the event. In particular, we are unlocked before we enter DidRunEvent, and unlock again before we return from it, so we can do whatever updates we want there. * When we have detected that we have a non-idle event to run; this calls FlagNotIdle. Differential Revision: https://phabricator.services.mozilla.com/D53631 --HG-- extra : moz-landing-system : lando
2019-11-22 17:06:17 +03:00
EnsureIsPaused(aProofOfUnlock);
// Don't call ClearIdleToken() here, since we may have a pending
// request already.
Bug 1597158 part 4. Stop unlocking the provided mutex in IdlePeriodState methods. r=smaug The basic idea, suggested by Olli, is that we can try to get a runnable in ThreadEventQueue::GetEvent, and if that does not produce anything unlock our mutex, do whatever idle state updates we need to do, re-lock our mutex. Then always we need to try getting a runnable again, because a non-idle runnable might have gotten queued while we had the lock unlocked. So we can't sleep on our mutex, in the mayWait case, unless we try to get a runnable again first. My notes on the current (pre this patch) unlocking setup follow. ------------------------------------------------------------ There are four places where we currently unlock: 1) IdlePeriodState::GetIdleDeadlineInternal. Needed only when !aIsPeek, to RequestIdleToken, which can do IPC. The only caller, via GetDeadlineForIdleTask, is PrioritizedEventQueue::GetEvent and only when we selected the idle or deferred queue. We need this to set the proper deadline on the idle event. In the cases when this unlock happens we currently _never_ return an idle event, because if we got here that means that we do not have an idle token. 2) IdlePeriodState::GetLocalIdleDeadline. Needs to unlock to get the idle period hint. The can get called from GetIdleDeadlineInternal in _both_ cases: peek and get. The callstack for the get case is covered above. The peek case is called from PrioritizedEventQueue::HasReadyEvent which is called from ThreadEventQueue::HasPendingEvent. 3) IdlePeriodState::SetPaused, because it sends an IPC message. This is only called from EnsureIsPaused, which is called from: - IdlePeriodState::GetIdleDeadlineInternal. Only in the !aIsPeek case. - IdlePeriodState::RanOutOfTasks called from: - PrioritizedEventQueue::GetEvent if we fell into the idle case and our queues are empty. - PrioritizedEventQueue::DidRunEvent if we are empty. 4) IdlePeriodState::ClearIdleToken because it sends an IPC message. This is called from: - IdlePeriodState::RanOutOfTasks; see SetPaused. - IdlePeriodState::GetIdleDeadlineInternal like EnsureIsPaused. - IdlePeriodState::GetIdleToken if token is in the past. This is only called from GetIdleDeadlineInternal, both cases. - IdlePeriodState::FlagNotIdle called from PrioritizedEventQueue::GetEvent if we find an event in a non-idle queue. Or rewriting in terms of API entrypoints on IdlePeriodState that might need to unlock: * Anything to do with getting deadlines, whether we are peeking or getting. Basically, if we need an updated deadline we need to unlock. * When we have detected we are completely out of tasks (idle or not) to run. Right now we do that when either we're asked for an event and don't have one or if we run an event and are empty after that (before unlocking!). But the unlocking or not happens in nsThreadEventQueue::DidRunEvent, so separately from the getting of the event. In particular, we are unlocked before we enter DidRunEvent, and unlock again before we return from it, so we can do whatever updates we want there. * When we have detected that we have a non-idle event to run; this calls FlagNotIdle. Differential Revision: https://phabricator.services.mozilla.com/D53631 --HG-- extra : moz-landing-system : lando
2019-11-22 17:06:17 +03:00
//
// RequestIdleToken can do all sorts of IPC stuff that might
// take mutexes. This is one reason why we need the
// MutexAutoUnlock reference!
RequestIdleToken(localIdleDeadline);
}
return TimeStamp();
}
if (!aIsPeek) {
EnsureIsActive();
}
return idleDeadline;
}
Bug 1597158 part 4. Stop unlocking the provided mutex in IdlePeriodState methods. r=smaug The basic idea, suggested by Olli, is that we can try to get a runnable in ThreadEventQueue::GetEvent, and if that does not produce anything unlock our mutex, do whatever idle state updates we need to do, re-lock our mutex. Then always we need to try getting a runnable again, because a non-idle runnable might have gotten queued while we had the lock unlocked. So we can't sleep on our mutex, in the mayWait case, unless we try to get a runnable again first. My notes on the current (pre this patch) unlocking setup follow. ------------------------------------------------------------ There are four places where we currently unlock: 1) IdlePeriodState::GetIdleDeadlineInternal. Needed only when !aIsPeek, to RequestIdleToken, which can do IPC. The only caller, via GetDeadlineForIdleTask, is PrioritizedEventQueue::GetEvent and only when we selected the idle or deferred queue. We need this to set the proper deadline on the idle event. In the cases when this unlock happens we currently _never_ return an idle event, because if we got here that means that we do not have an idle token. 2) IdlePeriodState::GetLocalIdleDeadline. Needs to unlock to get the idle period hint. The can get called from GetIdleDeadlineInternal in _both_ cases: peek and get. The callstack for the get case is covered above. The peek case is called from PrioritizedEventQueue::HasReadyEvent which is called from ThreadEventQueue::HasPendingEvent. 3) IdlePeriodState::SetPaused, because it sends an IPC message. This is only called from EnsureIsPaused, which is called from: - IdlePeriodState::GetIdleDeadlineInternal. Only in the !aIsPeek case. - IdlePeriodState::RanOutOfTasks called from: - PrioritizedEventQueue::GetEvent if we fell into the idle case and our queues are empty. - PrioritizedEventQueue::DidRunEvent if we are empty. 4) IdlePeriodState::ClearIdleToken because it sends an IPC message. This is called from: - IdlePeriodState::RanOutOfTasks; see SetPaused. - IdlePeriodState::GetIdleDeadlineInternal like EnsureIsPaused. - IdlePeriodState::GetIdleToken if token is in the past. This is only called from GetIdleDeadlineInternal, both cases. - IdlePeriodState::FlagNotIdle called from PrioritizedEventQueue::GetEvent if we find an event in a non-idle queue. Or rewriting in terms of API entrypoints on IdlePeriodState that might need to unlock: * Anything to do with getting deadlines, whether we are peeking or getting. Basically, if we need an updated deadline we need to unlock. * When we have detected we are completely out of tasks (idle or not) to run. Right now we do that when either we're asked for an event and don't have one or if we run an event and are empty after that (before unlocking!). But the unlocking or not happens in nsThreadEventQueue::DidRunEvent, so separately from the getting of the event. In particular, we are unlocked before we enter DidRunEvent, and unlock again before we return from it, so we can do whatever updates we want there. * When we have detected that we have a non-idle event to run; this calls FlagNotIdle. Differential Revision: https://phabricator.services.mozilla.com/D53631 --HG-- extra : moz-landing-system : lando
2019-11-22 17:06:17 +03:00
TimeStamp IdlePeriodState::GetLocalIdleDeadline(
bool& aShuttingDown, const MutexAutoUnlock& aProofOfUnlock) {
MOZ_ASSERT(NS_IsMainThread(),
"Why are we touching idle state off the main thread?");
// If we are shutting down, we won't honor the idle period, and we will
// always process idle runnables. This will ensure that the idle queue
// gets exhausted at shutdown time to prevent intermittently leaking
// some runnables inside that queue and even worse potentially leaving
// some important cleanup work unfinished.
if (gXPCOMThreadsShutDown ||
nsThreadManager::get().GetCurrentThread()->ShuttingDown()) {
aShuttingDown = true;
return TimeStamp::Now();
}
aShuttingDown = false;
TimeStamp idleDeadline;
Bug 1597158 part 4. Stop unlocking the provided mutex in IdlePeriodState methods. r=smaug The basic idea, suggested by Olli, is that we can try to get a runnable in ThreadEventQueue::GetEvent, and if that does not produce anything unlock our mutex, do whatever idle state updates we need to do, re-lock our mutex. Then always we need to try getting a runnable again, because a non-idle runnable might have gotten queued while we had the lock unlocked. So we can't sleep on our mutex, in the mayWait case, unless we try to get a runnable again first. My notes on the current (pre this patch) unlocking setup follow. ------------------------------------------------------------ There are four places where we currently unlock: 1) IdlePeriodState::GetIdleDeadlineInternal. Needed only when !aIsPeek, to RequestIdleToken, which can do IPC. The only caller, via GetDeadlineForIdleTask, is PrioritizedEventQueue::GetEvent and only when we selected the idle or deferred queue. We need this to set the proper deadline on the idle event. In the cases when this unlock happens we currently _never_ return an idle event, because if we got here that means that we do not have an idle token. 2) IdlePeriodState::GetLocalIdleDeadline. Needs to unlock to get the idle period hint. The can get called from GetIdleDeadlineInternal in _both_ cases: peek and get. The callstack for the get case is covered above. The peek case is called from PrioritizedEventQueue::HasReadyEvent which is called from ThreadEventQueue::HasPendingEvent. 3) IdlePeriodState::SetPaused, because it sends an IPC message. This is only called from EnsureIsPaused, which is called from: - IdlePeriodState::GetIdleDeadlineInternal. Only in the !aIsPeek case. - IdlePeriodState::RanOutOfTasks called from: - PrioritizedEventQueue::GetEvent if we fell into the idle case and our queues are empty. - PrioritizedEventQueue::DidRunEvent if we are empty. 4) IdlePeriodState::ClearIdleToken because it sends an IPC message. This is called from: - IdlePeriodState::RanOutOfTasks; see SetPaused. - IdlePeriodState::GetIdleDeadlineInternal like EnsureIsPaused. - IdlePeriodState::GetIdleToken if token is in the past. This is only called from GetIdleDeadlineInternal, both cases. - IdlePeriodState::FlagNotIdle called from PrioritizedEventQueue::GetEvent if we find an event in a non-idle queue. Or rewriting in terms of API entrypoints on IdlePeriodState that might need to unlock: * Anything to do with getting deadlines, whether we are peeking or getting. Basically, if we need an updated deadline we need to unlock. * When we have detected we are completely out of tasks (idle or not) to run. Right now we do that when either we're asked for an event and don't have one or if we run an event and are empty after that (before unlocking!). But the unlocking or not happens in nsThreadEventQueue::DidRunEvent, so separately from the getting of the event. In particular, we are unlocked before we enter DidRunEvent, and unlock again before we return from it, so we can do whatever updates we want there. * When we have detected that we have a non-idle event to run; this calls FlagNotIdle. Differential Revision: https://phabricator.services.mozilla.com/D53631 --HG-- extra : moz-landing-system : lando
2019-11-22 17:06:17 +03:00
// This GetIdlePeriodHint() call is the reason we need a MutexAutoUnlock here.
mIdlePeriod->GetIdlePeriodHint(&idleDeadline);
// If HasPendingEvents() has been called and it has returned true because of
// pending idle events, there is a risk that we may decide here that we aren't
// idle and return null, in which case HasPendingEvents() has effectively
// lied. Since we can't go back and fix the past, we have to adjust what we
// do here and forcefully pick the idle queue task here. Note that this means
// that we are choosing to run a task from the idle queue when we would
// normally decide that we aren't in an idle period, but this can only happen
// if we fall out of the idle period in between the call to HasPendingEvents()
// and here, which should hopefully be quite rare. We are effectively
// choosing to prioritize the sanity of our API semantics over the optimal
// scheduling.
if (!mHasPendingEventsPromisedIdleEvent &&
(!idleDeadline || idleDeadline < TimeStamp::Now())) {
return TimeStamp();
}
if (mHasPendingEventsPromisedIdleEvent && !idleDeadline) {
// If HasPendingEvents() has been called and it has returned true, but we're
// no longer in the idle period, we must return a valid timestamp to pretend
// that we are still in the idle period.
return TimeStamp::Now();
}
return idleDeadline;
}
TimeStamp IdlePeriodState::GetIdleToken(TimeStamp aLocalIdlePeriodHint,
Bug 1597158 part 4. Stop unlocking the provided mutex in IdlePeriodState methods. r=smaug The basic idea, suggested by Olli, is that we can try to get a runnable in ThreadEventQueue::GetEvent, and if that does not produce anything unlock our mutex, do whatever idle state updates we need to do, re-lock our mutex. Then always we need to try getting a runnable again, because a non-idle runnable might have gotten queued while we had the lock unlocked. So we can't sleep on our mutex, in the mayWait case, unless we try to get a runnable again first. My notes on the current (pre this patch) unlocking setup follow. ------------------------------------------------------------ There are four places where we currently unlock: 1) IdlePeriodState::GetIdleDeadlineInternal. Needed only when !aIsPeek, to RequestIdleToken, which can do IPC. The only caller, via GetDeadlineForIdleTask, is PrioritizedEventQueue::GetEvent and only when we selected the idle or deferred queue. We need this to set the proper deadline on the idle event. In the cases when this unlock happens we currently _never_ return an idle event, because if we got here that means that we do not have an idle token. 2) IdlePeriodState::GetLocalIdleDeadline. Needs to unlock to get the idle period hint. The can get called from GetIdleDeadlineInternal in _both_ cases: peek and get. The callstack for the get case is covered above. The peek case is called from PrioritizedEventQueue::HasReadyEvent which is called from ThreadEventQueue::HasPendingEvent. 3) IdlePeriodState::SetPaused, because it sends an IPC message. This is only called from EnsureIsPaused, which is called from: - IdlePeriodState::GetIdleDeadlineInternal. Only in the !aIsPeek case. - IdlePeriodState::RanOutOfTasks called from: - PrioritizedEventQueue::GetEvent if we fell into the idle case and our queues are empty. - PrioritizedEventQueue::DidRunEvent if we are empty. 4) IdlePeriodState::ClearIdleToken because it sends an IPC message. This is called from: - IdlePeriodState::RanOutOfTasks; see SetPaused. - IdlePeriodState::GetIdleDeadlineInternal like EnsureIsPaused. - IdlePeriodState::GetIdleToken if token is in the past. This is only called from GetIdleDeadlineInternal, both cases. - IdlePeriodState::FlagNotIdle called from PrioritizedEventQueue::GetEvent if we find an event in a non-idle queue. Or rewriting in terms of API entrypoints on IdlePeriodState that might need to unlock: * Anything to do with getting deadlines, whether we are peeking or getting. Basically, if we need an updated deadline we need to unlock. * When we have detected we are completely out of tasks (idle or not) to run. Right now we do that when either we're asked for an event and don't have one or if we run an event and are empty after that (before unlocking!). But the unlocking or not happens in nsThreadEventQueue::DidRunEvent, so separately from the getting of the event. In particular, we are unlocked before we enter DidRunEvent, and unlock again before we return from it, so we can do whatever updates we want there. * When we have detected that we have a non-idle event to run; this calls FlagNotIdle. Differential Revision: https://phabricator.services.mozilla.com/D53631 --HG-- extra : moz-landing-system : lando
2019-11-22 17:06:17 +03:00
const MutexAutoUnlock& aProofOfUnlock) {
MOZ_ASSERT(NS_IsMainThread(),
"Why are we touching idle state off the main thread?");
if (!ShouldGetIdleToken()) {
return aLocalIdlePeriodHint;
}
if (mIdleToken) {
TimeStamp now = TimeStamp::Now();
if (mIdleToken < now) {
Bug 1597158 part 4. Stop unlocking the provided mutex in IdlePeriodState methods. r=smaug The basic idea, suggested by Olli, is that we can try to get a runnable in ThreadEventQueue::GetEvent, and if that does not produce anything unlock our mutex, do whatever idle state updates we need to do, re-lock our mutex. Then always we need to try getting a runnable again, because a non-idle runnable might have gotten queued while we had the lock unlocked. So we can't sleep on our mutex, in the mayWait case, unless we try to get a runnable again first. My notes on the current (pre this patch) unlocking setup follow. ------------------------------------------------------------ There are four places where we currently unlock: 1) IdlePeriodState::GetIdleDeadlineInternal. Needed only when !aIsPeek, to RequestIdleToken, which can do IPC. The only caller, via GetDeadlineForIdleTask, is PrioritizedEventQueue::GetEvent and only when we selected the idle or deferred queue. We need this to set the proper deadline on the idle event. In the cases when this unlock happens we currently _never_ return an idle event, because if we got here that means that we do not have an idle token. 2) IdlePeriodState::GetLocalIdleDeadline. Needs to unlock to get the idle period hint. The can get called from GetIdleDeadlineInternal in _both_ cases: peek and get. The callstack for the get case is covered above. The peek case is called from PrioritizedEventQueue::HasReadyEvent which is called from ThreadEventQueue::HasPendingEvent. 3) IdlePeriodState::SetPaused, because it sends an IPC message. This is only called from EnsureIsPaused, which is called from: - IdlePeriodState::GetIdleDeadlineInternal. Only in the !aIsPeek case. - IdlePeriodState::RanOutOfTasks called from: - PrioritizedEventQueue::GetEvent if we fell into the idle case and our queues are empty. - PrioritizedEventQueue::DidRunEvent if we are empty. 4) IdlePeriodState::ClearIdleToken because it sends an IPC message. This is called from: - IdlePeriodState::RanOutOfTasks; see SetPaused. - IdlePeriodState::GetIdleDeadlineInternal like EnsureIsPaused. - IdlePeriodState::GetIdleToken if token is in the past. This is only called from GetIdleDeadlineInternal, both cases. - IdlePeriodState::FlagNotIdle called from PrioritizedEventQueue::GetEvent if we find an event in a non-idle queue. Or rewriting in terms of API entrypoints on IdlePeriodState that might need to unlock: * Anything to do with getting deadlines, whether we are peeking or getting. Basically, if we need an updated deadline we need to unlock. * When we have detected we are completely out of tasks (idle or not) to run. Right now we do that when either we're asked for an event and don't have one or if we run an event and are empty after that (before unlocking!). But the unlocking or not happens in nsThreadEventQueue::DidRunEvent, so separately from the getting of the event. In particular, we are unlocked before we enter DidRunEvent, and unlock again before we return from it, so we can do whatever updates we want there. * When we have detected that we have a non-idle event to run; this calls FlagNotIdle. Differential Revision: https://phabricator.services.mozilla.com/D53631 --HG-- extra : moz-landing-system : lando
2019-11-22 17:06:17 +03:00
ClearIdleToken();
return mIdleToken;
}
return mIdleToken < aLocalIdlePeriodHint ? mIdleToken
: aLocalIdlePeriodHint;
}
return TimeStamp();
}
void IdlePeriodState::RequestIdleToken(TimeStamp aLocalIdlePeriodHint) {
MOZ_ASSERT(NS_IsMainThread(),
"Why are we touching idle state off the main thread?");
MOZ_ASSERT(!mActive);
if (!mIdleSchedulerInitialized) {
mIdleSchedulerInitialized = true;
if (ShouldGetIdleToken()) {
// For now cross-process idle scheduler is supported only on the main
// threads of the child processes.
mIdleScheduler = ipc::IdleSchedulerChild::GetMainThreadIdleScheduler();
if (mIdleScheduler) {
mIdleScheduler->Init(this);
}
}
}
if (mIdleScheduler && !mIdleRequestId) {
TimeStamp now = TimeStamp::Now();
if (aLocalIdlePeriodHint <= now) {
return;
}
mIdleRequestId = ++sIdleRequestCounter;
mIdleScheduler->SendRequestIdleTime(mIdleRequestId,
aLocalIdlePeriodHint - now);
}
}
void IdlePeriodState::SetIdleToken(uint64_t aId, TimeDuration aDuration) {
MOZ_ASSERT(NS_IsMainThread(),
"Why are we touching idle state off the main thread?");
if (mIdleRequestId == aId) {
mIdleToken = TimeStamp::Now() + aDuration;
}
}
void IdlePeriodState::SetActive() {
MOZ_ASSERT(NS_IsMainThread(),
"Why are we touching idle state off the main thread?");
MOZ_ASSERT(!mActive);
if (mIdleScheduler) {
mIdleScheduler->SetActive();
}
mActive = true;
}
Bug 1597158 part 4. Stop unlocking the provided mutex in IdlePeriodState methods. r=smaug The basic idea, suggested by Olli, is that we can try to get a runnable in ThreadEventQueue::GetEvent, and if that does not produce anything unlock our mutex, do whatever idle state updates we need to do, re-lock our mutex. Then always we need to try getting a runnable again, because a non-idle runnable might have gotten queued while we had the lock unlocked. So we can't sleep on our mutex, in the mayWait case, unless we try to get a runnable again first. My notes on the current (pre this patch) unlocking setup follow. ------------------------------------------------------------ There are four places where we currently unlock: 1) IdlePeriodState::GetIdleDeadlineInternal. Needed only when !aIsPeek, to RequestIdleToken, which can do IPC. The only caller, via GetDeadlineForIdleTask, is PrioritizedEventQueue::GetEvent and only when we selected the idle or deferred queue. We need this to set the proper deadline on the idle event. In the cases when this unlock happens we currently _never_ return an idle event, because if we got here that means that we do not have an idle token. 2) IdlePeriodState::GetLocalIdleDeadline. Needs to unlock to get the idle period hint. The can get called from GetIdleDeadlineInternal in _both_ cases: peek and get. The callstack for the get case is covered above. The peek case is called from PrioritizedEventQueue::HasReadyEvent which is called from ThreadEventQueue::HasPendingEvent. 3) IdlePeriodState::SetPaused, because it sends an IPC message. This is only called from EnsureIsPaused, which is called from: - IdlePeriodState::GetIdleDeadlineInternal. Only in the !aIsPeek case. - IdlePeriodState::RanOutOfTasks called from: - PrioritizedEventQueue::GetEvent if we fell into the idle case and our queues are empty. - PrioritizedEventQueue::DidRunEvent if we are empty. 4) IdlePeriodState::ClearIdleToken because it sends an IPC message. This is called from: - IdlePeriodState::RanOutOfTasks; see SetPaused. - IdlePeriodState::GetIdleDeadlineInternal like EnsureIsPaused. - IdlePeriodState::GetIdleToken if token is in the past. This is only called from GetIdleDeadlineInternal, both cases. - IdlePeriodState::FlagNotIdle called from PrioritizedEventQueue::GetEvent if we find an event in a non-idle queue. Or rewriting in terms of API entrypoints on IdlePeriodState that might need to unlock: * Anything to do with getting deadlines, whether we are peeking or getting. Basically, if we need an updated deadline we need to unlock. * When we have detected we are completely out of tasks (idle or not) to run. Right now we do that when either we're asked for an event and don't have one or if we run an event and are empty after that (before unlocking!). But the unlocking or not happens in nsThreadEventQueue::DidRunEvent, so separately from the getting of the event. In particular, we are unlocked before we enter DidRunEvent, and unlock again before we return from it, so we can do whatever updates we want there. * When we have detected that we have a non-idle event to run; this calls FlagNotIdle. Differential Revision: https://phabricator.services.mozilla.com/D53631 --HG-- extra : moz-landing-system : lando
2019-11-22 17:06:17 +03:00
void IdlePeriodState::SetPaused(const MutexAutoUnlock& aProofOfUnlock) {
MOZ_ASSERT(NS_IsMainThread(),
"Why are we touching idle state off the main thread?");
MOZ_ASSERT(mActive);
if (mIdleScheduler && mIdleScheduler->SetPaused()) {
// We may have gotten a free cpu core for running idle tasks.
// We don't try to catch the case when there are prioritized processes
// running.
Bug 1597158 part 4. Stop unlocking the provided mutex in IdlePeriodState methods. r=smaug The basic idea, suggested by Olli, is that we can try to get a runnable in ThreadEventQueue::GetEvent, and if that does not produce anything unlock our mutex, do whatever idle state updates we need to do, re-lock our mutex. Then always we need to try getting a runnable again, because a non-idle runnable might have gotten queued while we had the lock unlocked. So we can't sleep on our mutex, in the mayWait case, unless we try to get a runnable again first. My notes on the current (pre this patch) unlocking setup follow. ------------------------------------------------------------ There are four places where we currently unlock: 1) IdlePeriodState::GetIdleDeadlineInternal. Needed only when !aIsPeek, to RequestIdleToken, which can do IPC. The only caller, via GetDeadlineForIdleTask, is PrioritizedEventQueue::GetEvent and only when we selected the idle or deferred queue. We need this to set the proper deadline on the idle event. In the cases when this unlock happens we currently _never_ return an idle event, because if we got here that means that we do not have an idle token. 2) IdlePeriodState::GetLocalIdleDeadline. Needs to unlock to get the idle period hint. The can get called from GetIdleDeadlineInternal in _both_ cases: peek and get. The callstack for the get case is covered above. The peek case is called from PrioritizedEventQueue::HasReadyEvent which is called from ThreadEventQueue::HasPendingEvent. 3) IdlePeriodState::SetPaused, because it sends an IPC message. This is only called from EnsureIsPaused, which is called from: - IdlePeriodState::GetIdleDeadlineInternal. Only in the !aIsPeek case. - IdlePeriodState::RanOutOfTasks called from: - PrioritizedEventQueue::GetEvent if we fell into the idle case and our queues are empty. - PrioritizedEventQueue::DidRunEvent if we are empty. 4) IdlePeriodState::ClearIdleToken because it sends an IPC message. This is called from: - IdlePeriodState::RanOutOfTasks; see SetPaused. - IdlePeriodState::GetIdleDeadlineInternal like EnsureIsPaused. - IdlePeriodState::GetIdleToken if token is in the past. This is only called from GetIdleDeadlineInternal, both cases. - IdlePeriodState::FlagNotIdle called from PrioritizedEventQueue::GetEvent if we find an event in a non-idle queue. Or rewriting in terms of API entrypoints on IdlePeriodState that might need to unlock: * Anything to do with getting deadlines, whether we are peeking or getting. Basically, if we need an updated deadline we need to unlock. * When we have detected we are completely out of tasks (idle or not) to run. Right now we do that when either we're asked for an event and don't have one or if we run an event and are empty after that (before unlocking!). But the unlocking or not happens in nsThreadEventQueue::DidRunEvent, so separately from the getting of the event. In particular, we are unlocked before we enter DidRunEvent, and unlock again before we return from it, so we can do whatever updates we want there. * When we have detected that we have a non-idle event to run; this calls FlagNotIdle. Differential Revision: https://phabricator.services.mozilla.com/D53631 --HG-- extra : moz-landing-system : lando
2019-11-22 17:06:17 +03:00
// This SendSchedule call is why we need the MutexAutoUnlock here, because
// IPC can do weird things with mutexes.
mIdleScheduler->SendSchedule();
}
mActive = false;
}
Bug 1597158 part 4. Stop unlocking the provided mutex in IdlePeriodState methods. r=smaug The basic idea, suggested by Olli, is that we can try to get a runnable in ThreadEventQueue::GetEvent, and if that does not produce anything unlock our mutex, do whatever idle state updates we need to do, re-lock our mutex. Then always we need to try getting a runnable again, because a non-idle runnable might have gotten queued while we had the lock unlocked. So we can't sleep on our mutex, in the mayWait case, unless we try to get a runnable again first. My notes on the current (pre this patch) unlocking setup follow. ------------------------------------------------------------ There are four places where we currently unlock: 1) IdlePeriodState::GetIdleDeadlineInternal. Needed only when !aIsPeek, to RequestIdleToken, which can do IPC. The only caller, via GetDeadlineForIdleTask, is PrioritizedEventQueue::GetEvent and only when we selected the idle or deferred queue. We need this to set the proper deadline on the idle event. In the cases when this unlock happens we currently _never_ return an idle event, because if we got here that means that we do not have an idle token. 2) IdlePeriodState::GetLocalIdleDeadline. Needs to unlock to get the idle period hint. The can get called from GetIdleDeadlineInternal in _both_ cases: peek and get. The callstack for the get case is covered above. The peek case is called from PrioritizedEventQueue::HasReadyEvent which is called from ThreadEventQueue::HasPendingEvent. 3) IdlePeriodState::SetPaused, because it sends an IPC message. This is only called from EnsureIsPaused, which is called from: - IdlePeriodState::GetIdleDeadlineInternal. Only in the !aIsPeek case. - IdlePeriodState::RanOutOfTasks called from: - PrioritizedEventQueue::GetEvent if we fell into the idle case and our queues are empty. - PrioritizedEventQueue::DidRunEvent if we are empty. 4) IdlePeriodState::ClearIdleToken because it sends an IPC message. This is called from: - IdlePeriodState::RanOutOfTasks; see SetPaused. - IdlePeriodState::GetIdleDeadlineInternal like EnsureIsPaused. - IdlePeriodState::GetIdleToken if token is in the past. This is only called from GetIdleDeadlineInternal, both cases. - IdlePeriodState::FlagNotIdle called from PrioritizedEventQueue::GetEvent if we find an event in a non-idle queue. Or rewriting in terms of API entrypoints on IdlePeriodState that might need to unlock: * Anything to do with getting deadlines, whether we are peeking or getting. Basically, if we need an updated deadline we need to unlock. * When we have detected we are completely out of tasks (idle or not) to run. Right now we do that when either we're asked for an event and don't have one or if we run an event and are empty after that (before unlocking!). But the unlocking or not happens in nsThreadEventQueue::DidRunEvent, so separately from the getting of the event. In particular, we are unlocked before we enter DidRunEvent, and unlock again before we return from it, so we can do whatever updates we want there. * When we have detected that we have a non-idle event to run; this calls FlagNotIdle. Differential Revision: https://phabricator.services.mozilla.com/D53631 --HG-- extra : moz-landing-system : lando
2019-11-22 17:06:17 +03:00
void IdlePeriodState::ClearIdleToken() {
MOZ_ASSERT(NS_IsMainThread(),
"Why are we touching idle state off the main thread?");
if (mIdleRequestId) {
if (mIdleScheduler) {
Bug 1597158 part 4. Stop unlocking the provided mutex in IdlePeriodState methods. r=smaug The basic idea, suggested by Olli, is that we can try to get a runnable in ThreadEventQueue::GetEvent, and if that does not produce anything unlock our mutex, do whatever idle state updates we need to do, re-lock our mutex. Then always we need to try getting a runnable again, because a non-idle runnable might have gotten queued while we had the lock unlocked. So we can't sleep on our mutex, in the mayWait case, unless we try to get a runnable again first. My notes on the current (pre this patch) unlocking setup follow. ------------------------------------------------------------ There are four places where we currently unlock: 1) IdlePeriodState::GetIdleDeadlineInternal. Needed only when !aIsPeek, to RequestIdleToken, which can do IPC. The only caller, via GetDeadlineForIdleTask, is PrioritizedEventQueue::GetEvent and only when we selected the idle or deferred queue. We need this to set the proper deadline on the idle event. In the cases when this unlock happens we currently _never_ return an idle event, because if we got here that means that we do not have an idle token. 2) IdlePeriodState::GetLocalIdleDeadline. Needs to unlock to get the idle period hint. The can get called from GetIdleDeadlineInternal in _both_ cases: peek and get. The callstack for the get case is covered above. The peek case is called from PrioritizedEventQueue::HasReadyEvent which is called from ThreadEventQueue::HasPendingEvent. 3) IdlePeriodState::SetPaused, because it sends an IPC message. This is only called from EnsureIsPaused, which is called from: - IdlePeriodState::GetIdleDeadlineInternal. Only in the !aIsPeek case. - IdlePeriodState::RanOutOfTasks called from: - PrioritizedEventQueue::GetEvent if we fell into the idle case and our queues are empty. - PrioritizedEventQueue::DidRunEvent if we are empty. 4) IdlePeriodState::ClearIdleToken because it sends an IPC message. This is called from: - IdlePeriodState::RanOutOfTasks; see SetPaused. - IdlePeriodState::GetIdleDeadlineInternal like EnsureIsPaused. - IdlePeriodState::GetIdleToken if token is in the past. This is only called from GetIdleDeadlineInternal, both cases. - IdlePeriodState::FlagNotIdle called from PrioritizedEventQueue::GetEvent if we find an event in a non-idle queue. Or rewriting in terms of API entrypoints on IdlePeriodState that might need to unlock: * Anything to do with getting deadlines, whether we are peeking or getting. Basically, if we need an updated deadline we need to unlock. * When we have detected we are completely out of tasks (idle or not) to run. Right now we do that when either we're asked for an event and don't have one or if we run an event and are empty after that (before unlocking!). But the unlocking or not happens in nsThreadEventQueue::DidRunEvent, so separately from the getting of the event. In particular, we are unlocked before we enter DidRunEvent, and unlock again before we return from it, so we can do whatever updates we want there. * When we have detected that we have a non-idle event to run; this calls FlagNotIdle. Differential Revision: https://phabricator.services.mozilla.com/D53631 --HG-- extra : moz-landing-system : lando
2019-11-22 17:06:17 +03:00
// This SendIdleTimeUsed call is why we need to not be holding
// any locks here, because IPC can do weird things with mutexes.
// Ideally we'd have a MutexAutoUnlock& reference here, but some
// callers end up here while just not holding any locks at all.
mIdleScheduler->SendIdleTimeUsed(mIdleRequestId);
}
mIdleRequestId = 0;
mIdleToken = TimeStamp();
}
}
bool IdlePeriodState::ShouldGetIdleToken() {
return StaticPrefs::idle_period_cross_process_scheduling() &&
XRE_IsContentProcess();
}
} // namespace mozilla