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
Most event queues don't ever get many events queued at one time, but the
MainThread Input and Normal queues may.
Differential Revision: https://phabricator.services.mozilla.com/D53912
--HG--
extra : moz-landing-system : lando
This should avoid freeing and reallocating the buffer every N events, and
make it simpler to use smaller buffers, especially for non-MainThread queues.
Differential Revision: https://phabricator.services.mozilla.com/D53911
--HG--
extra : moz-landing-system : lando
Threadpools run an event that then runs other events, so we need to tweak
things for GetRunningEventDelay()
Differential Revision: https://phabricator.services.mozilla.com/D44058
--HG--
extra : moz-landing-system : lando
This lets us determine the time that an event has been running, and the time
that the event spent queued - which can be used to figure out 'jank' at the
time the event was queued. For PrioritizedEventQueues, only if such queuing
would delay an input event then the queuing delay is reported.
Differential Revision: https://phabricator.services.mozilla.com/D41279
--HG--
extra : moz-landing-system : lando
The static analysis caught this for me in Bug 1593812, I was just to
dumb to actually apply this change prior to commit.
Differential Revision: https://phabricator.services.mozilla.com/D52170
--HG--
extra : moz-landing-system : lando
Threadpools run an event that then runs other events, so we need to tweak
things for GetRunningEventDelay()
Differential Revision: https://phabricator.services.mozilla.com/D44058
--HG--
extra : moz-landing-system : lando
This lets us determine the time that an event has been running, and the time
that the event spent queued - which can be used to figure out 'jank' at the
time the event was queued. For PrioritizedEventQueues, only if such queuing
would delay an input event then the queuing delay is reported.
Differential Revision: https://phabricator.services.mozilla.com/D41279
--HG--
extra : moz-landing-system : lando
We need some way of differentiating "tasks that just consume CPU"
vs. "tasks that block on some external resource" like reading from a
socket or a file. If we didn't have this, we'd either a) have a thread
pool sized for the number of CPUs where having all the threads blocked
on I/O--and therefore no new tasks are able to run--or b) have a thread
pool that tries to increase the number of working threads based on the
number of submitted tasks and winds up having too many tasks running
with not enough CPUs to run them on.
This flag enables us to theoretically get the best of both worlds: we
can set aside `~#CPUs` threads for CPU-intensive work, and
`$SOME_NUMBER` threads for I/O work. The latter number can be adjusted
up if the I/O load on the system is particularly heavy.
The implementation strategy of this patch is to use two separate thread
pools for the two different kinds of work. It's entirely possible that
we'll want to use a single thread pool to coordinate thread create
between the two kinds of work, or even migrate threads from one kind of
work to the other, but such improvements can be future work. The focus
right now is providing the rest of Gecko with a common funnel to put
tasks into, and we can adjust what's at the end of the funnel at a later
point.
Differential Revision: https://phabricator.services.mozilla.com/D51708
--HG--
extra : moz-landing-system : lando
`TaskQueue` wraps an `nsIEventTarget` to provide "one runnable at a
time" execution policies regardless of the underlying implementation of
the wrapped `nsIEventTarget` (e.g. a thread pool). `TaskQueue` also
provides a `nsISerialEventTarget` wrapper, `EventTargetWrapper`, around
itself (!) for consumers who want to continue to provide a more
XPCOM-ish API.
One would think that dispatching tasks to `EventTargetWrapper` with a
given set of flags would pass that set of flags through, unchanged, to
the underlying event target of the wrapped `TaskQueue`.
This pass-through is not the case. `TaskQueue` supports a "tail
dispatch" mode of operation that is somewhat underdocumented. Roughly,
tail dispatch to a `TaskQueue` says (with several other conditions) that
dispatched tasks are held separately and not passed through to the
underlying event target. If a given `TaskQueue` supports tail dispatch
and the dispatcher also supports tail dispatch, any tasks dispatched to
said `TaskQueue` are silently converted to tail dispatched tasks. Since
tail dispatched tasks can't meaningfully have flags associated with
them, the current implementation simply drops any passed flags on the floor.
These flags, however, might be meaningful, and we should attempt to
honor them in the cases we're not doing tail dispatch. (And when we are
doing tail dispatch, we can verify that the requested flags are not
asking for peculiar things.)
Differential Revision: https://phabricator.services.mozilla.com/D51702
--HG--
extra : moz-landing-system : lando
The current API name is bad: we want it to be read "some background
thread", but it could just as easily be read "a singular background
thread", which would lead people to assume that for:
```
NS_DispatchToBackgroundThread(...);
NS_DispatchToBackgroundThread(...);
```
the dispatched tasks will necessarily run in the order they are
dispatched, which is not the case.
Let's try to head off that interpretation by renaming this function.
Differential Revision: https://phabricator.services.mozilla.com/D51703
--HG--
extra : moz-landing-system : lando
Small changes:
- Ordered #includes.
- Fixed some comments (obsolete remarks, or typos).
- Set `nsMainThreadPtrHolder::mRawPtr` from constructor initializers.
- Modernized `nsMainThreadPtrHolder` copy-prevention.
- Default-initialize `nsMainThreadPtrHolder` members, for extra safety.
- Made `nsMainThreadPtrHandle::get()` `const` (consistent with others).
- Moved nsMainThreadPtrHandle private member to the end.
- Removed now-unused `mozilla::PtrHolder` and `mozilla::PtrHandle` aliases.
Differential Revision: https://phabricator.services.mozilla.com/D51056
--HG--
extra : moz-landing-system : lando
We could try to move the EnforcePendingTaskGuarantee() bit into
PeekIdleDeadline, but then we'd need to check HasReadyEvent() on
mDeferredTimersQueue and mIdleQueue before we possibly unlock the mutex under
PeekIdleDeadline, and it's not clear that that state cannot change once the
mutex is unlocked...
The EnsureIsActive() call at the end of GetIdleDeadlineInternal in the !aIsPeek
case only makes sense if there are in fact idle tasks available to run when
GetDeadlineForIdleTask is called, because otherwise it would incorrectly set us
active when we are not running any tasks.
Differential Revision: https://phabricator.services.mozilla.com/D49696
--HG--
rename : xpcom/threads/PrioritizedEventQueue.cpp => xpcom/threads/IdlePeriodState.cpp
rename : xpcom/threads/PrioritizedEventQueue.h => xpcom/threads/IdlePeriodState.h
extra : moz-landing-system : lando
We could try to move the EnforcePendingTaskGuarantee() bit into PeekIdleDeadline, but
then we'd need to check HasReadyEvent() on mDeferredTimersQueue and mIdleQueue
before we unlock the mutex and PeekIdleDeadline, and it's not clear that that
state cannot change once the mutex is unlocked...
The EnsureIsActive() call at the end of GetIdleDeadlineInternal in the !aIsPeek
case only makes sense if there are in fact idle tasks available to run when
GetDeadlineForIdleTask is called, because otherwise it would incorrectly set us
active when we are not running any tasks.
Differential Revision: https://phabricator.services.mozilla.com/D49696
--HG--
rename : xpcom/threads/PrioritizedEventQueue.cpp => xpcom/threads/IdlePeriodState.cpp
rename : xpcom/threads/PrioritizedEventQueue.h => xpcom/threads/IdlePeriodState.h
extra : moz-landing-system : lando
This function is needed for people whose needs don't map cleanly to
`NS_DispatchToBackgroundThread`, usually because they're using the event
target to do thread consistency checks. Once we have this function, we
can start converting singleton threads/thread pools that want to use
functionality like the above.
Differential Revision: https://phabricator.services.mozilla.com/D47454
--HG--
extra : moz-landing-system : lando
Eventually, we're going to want to hand out an `nsIEventTarget*` to people
wanting to dispatch to background threads, but for whatever reason not
wanting to use the `NS_DispatchToBackgroundThread` API (probably because
they want to verify correctness by checking that certain methods are, in
fact, running on the background event target). And because we're going to
want to have some sort of division between CPU-bound and IO-bound tasks, we
can't just hand out references to a single thread pool. We need some sort
of intermediate object for both of these goals, and that is what the added
`BackgroundEventTarget` class is.
Differential Revision: https://phabricator.services.mozilla.com/D47343
--HG--
extra : moz-landing-system : lando
It's fairly common for clients to hold a pointer to some private thread
and then inquire about `IsOnCurrentThread` in debug checks. As we
migrate these private threads into some `nsIEventTarget` implementation
that might be running on thread pools, we need to ensure that those
`IsOnCurrentThread` continues to be relatively cheap. The current
implementation for thread pools is not particularly efficient.
The inefficiency comes from having to iterate over all the threads in
the pool. But there's no need to do this; we can just have each thread
set a particular thread-local variable to the thread pool it's running
on, and check the value of that thread-local variable instead.
Differential Revision: https://phabricator.services.mozilla.com/D47139
--HG--
extra : moz-landing-system : lando
We have a number of people starting up singleton threads for the sole
purpose of running a single runnable on them. These consumers often
leave the thread running until some point close to shutdown, or they
never shut it down at all. Let's add a helper function to do the thing
they actually want to do, and then we can modify the implementation of
that function as necessary as we merge singleton threads (and even
thread pools) together.
Differential Revision: https://phabricator.services.mozilla.com/D46997
--HG--
extra : moz-landing-system : lando
To remove the blocking inside nsThread::Init, two things needed
to happen:
- Switch the ThreadInitData value passed as the argument for
ThreadFunc to a heap allocation, so that it can outlive the call
to nsThread::Init.
- Initialize mThread and mEventTarget->mThread to the return
value of PR_CreateThread, so that to the callers, checks which
depend on these values being set can continue to function.
Differential Revision: https://phabricator.services.mozilla.com/D41248
--HG--
extra : moz-landing-system : lando
GetCurrentPhysicalThread and GetCurrentVirtualThread are, in practice,
identical, as the TLS override that GetCurrentVirtualThread depends on
is never actually set. This simply removes that and renames some things/
deletes some comments.
Differential Revision: https://phabricator.services.mozilla.com/D41247
--HG--
extra : moz-landing-system : lando
This is to get initial feedback/review.
PIdleScheduler.ipdl has the documentation about the basic architecture.
(v15)
Differential Revision: https://phabricator.services.mozilla.com/D45162
--HG--
extra : moz-landing-system : lando
Similar to MozPromise::FromGeckoResult.
Allows to create a MozPromise that will be resolved/rejected when the JS promise does the same.
It would be nice to be able to chain the two promise types, but it would be an additional effort.
MozPromise::FromDomPromise is limited to primitive types only and the reject value type must be nsresult.
Differential Revision: https://phabricator.services.mozilla.com/D46017
--HG--
extra : moz-landing-system : lando
This is to get initial feedback/review.
PIdleScheduler.ipdl has the documentation about the basic architecture.
(v15)
Differential Revision: https://phabricator.services.mozilla.com/D45162
--HG--
extra : moz-landing-system : lando
Similar to MozPromise::FromGeckoResult.
Allows to create a MozPromise that will be resolved/rejected when the JS promise does the same.
It would be nice to be able to chain the two promise types, but it would be an additional effort.
MozPromise::FromDomPromise is limited to primitive types only and the reject value type must be nsresult.
Differential Revision: https://phabricator.services.mozilla.com/D46017
--HG--
extra : moz-landing-system : lando
This is to get initial feedback/review.
PIdleScheduler.ipdl has the documentation about the basic architecture.
(v15)
Differential Revision: https://phabricator.services.mozilla.com/D45162
--HG--
extra : moz-landing-system : lando
EXIT_FAILURE is 'implementation defined' but can be defined to be 1.
In our case, pingsender exits with EXIT_FAILURE but nsIProcess wasn't
reporting it as failure because it thought failures were always negative.
Differential Revision: https://phabricator.services.mozilla.com/D45038
--HG--
extra : moz-landing-system : lando
EXIT_FAILURE is 'implementation defined' but can be defined to be 1.
In our case, pingsender exits with EXIT_FAILURE but nsIProcess wasn't
reporting it as failure because it thought failures were always negative.
Differential Revision: https://phabricator.services.mozilla.com/D45038
--HG--
extra : moz-landing-system : lando
EXIT_FAILURE is 'implementation defined' but can be defined to be 1.
In our case, pingsender exits with EXIT_FAILURE but nsIProcess wasn't
reporting it as failure because it thought failures were always negative.
Differential Revision: https://phabricator.services.mozilla.com/D45038
--HG--
extra : moz-landing-system : lando