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:
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
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
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.
- IdlePeriodState::ClearIdleToken because it sends an IPC message. This is
- 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
- 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