зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1398423 - Make linked list of SchedulerGroups static (r=froydnj)
MozReview-Commit-ID: GgfdRlhPiHP
This commit is contained in:
Родитель
fe8e58f308
Коммит
b9683a5b18
|
@ -13,8 +13,28 @@
|
|||
|
||||
using namespace mozilla::dom;
|
||||
|
||||
LinkedList<SchedulerGroup>* LabeledEventQueue::sSchedulerGroups;
|
||||
size_t LabeledEventQueue::sLabeledEventQueueCount;
|
||||
SchedulerGroup* LabeledEventQueue::sCurrentSchedulerGroup;
|
||||
|
||||
LabeledEventQueue::LabeledEventQueue()
|
||||
{
|
||||
// LabeledEventQueue should only be used by one consumer since it uses a
|
||||
// single static sSchedulerGroups field. It's hard to assert this, though, so
|
||||
// we assert NS_IsMainThread(), which is a reasonable proxy.
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (sLabeledEventQueueCount++ == 0) {
|
||||
sSchedulerGroups = new LinkedList<SchedulerGroup>();
|
||||
}
|
||||
}
|
||||
|
||||
LabeledEventQueue::~LabeledEventQueue()
|
||||
{
|
||||
if (--sLabeledEventQueueCount == 0) {
|
||||
delete sSchedulerGroups;
|
||||
sSchedulerGroups = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static SchedulerGroup*
|
||||
|
@ -94,8 +114,14 @@ LabeledEventQueue::PutEvent(already_AddRefed<nsIRunnable>&& aEvent,
|
|||
RunnableEpochQueue* queue = isLabeled ? mLabeled.LookupOrAdd(group) : &mUnlabeled;
|
||||
queue->Push(QueueEntry(event.forget(), epoch->mEpochNumber));
|
||||
|
||||
if (group && !group->isInList()) {
|
||||
mSchedulerGroups.insertBack(group);
|
||||
if (group && group->EnqueueEvent() == SchedulerGroup::NewlyQueued) {
|
||||
// This group didn't have any events before. Add it to the
|
||||
// sSchedulerGroups list.
|
||||
MOZ_ASSERT(!group->isInList());
|
||||
sSchedulerGroups->insertBack(group);
|
||||
if (!sCurrentSchedulerGroup) {
|
||||
sCurrentSchedulerGroup = group;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -113,6 +139,18 @@ LabeledEventQueue::PopEpoch()
|
|||
mNumEvents--;
|
||||
}
|
||||
|
||||
// Returns the next SchedulerGroup after |aGroup| in sSchedulerGroups. Wraps
|
||||
// around to the beginning of the list when we hit the end.
|
||||
/* static */ SchedulerGroup*
|
||||
LabeledEventQueue::NextSchedulerGroup(SchedulerGroup* aGroup)
|
||||
{
|
||||
SchedulerGroup* result = aGroup->getNext();
|
||||
if (!result) {
|
||||
result = sSchedulerGroups->getFirst();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
already_AddRefed<nsIRunnable>
|
||||
LabeledEventQueue::GetEvent(EventPriority* aPriority,
|
||||
const MutexAutoLock& aProofOfLock)
|
||||
|
@ -133,13 +171,17 @@ LabeledEventQueue::GetEvent(EventPriority* aPriority,
|
|||
}
|
||||
}
|
||||
|
||||
if (!sCurrentSchedulerGroup) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Move active tabs to the front of the queue. The mAvoidActiveTabCount field
|
||||
// prevents us from preferentially processing events from active tabs twice in
|
||||
// a row. This scheme is designed to prevent starvation.
|
||||
if (TabChild::HasActiveTabs() && mAvoidActiveTabCount <= 0) {
|
||||
for (TabChild* tabChild : TabChild::GetActiveTabs()) {
|
||||
SchedulerGroup* group = tabChild->TabGroup();
|
||||
if (!group->isInList() || group == mSchedulerGroups.getFirst()) {
|
||||
if (!group->isInList() || group == sCurrentSchedulerGroup) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -148,40 +190,56 @@ LabeledEventQueue::GetEvent(EventPriority* aPriority,
|
|||
// a background group) before we prioritize active tabs again.
|
||||
mAvoidActiveTabCount += 2;
|
||||
|
||||
group->removeFrom(mSchedulerGroups);
|
||||
mSchedulerGroups.insertFront(group);
|
||||
// We move |group| right before sCurrentSchedulerGroup and then set
|
||||
// sCurrentSchedulerGroup to group.
|
||||
MOZ_ASSERT(group != sCurrentSchedulerGroup);
|
||||
group->removeFrom(*sSchedulerGroups);
|
||||
sCurrentSchedulerGroup->setPrevious(group);
|
||||
sCurrentSchedulerGroup = group;
|
||||
}
|
||||
}
|
||||
|
||||
// Iterate over SchedulerGroups in order. Each time we pass by a
|
||||
// SchedulerGroup, we move it to the back of the list. This ensures that we
|
||||
// process SchedulerGroups in a round-robin order (ignoring active tab
|
||||
// prioritization).
|
||||
SchedulerGroup* firstGroup = mSchedulerGroups.getFirst();
|
||||
// Iterate over each SchedulerGroup once, starting at sCurrentSchedulerGroup.
|
||||
SchedulerGroup* firstGroup = sCurrentSchedulerGroup;
|
||||
SchedulerGroup* group = firstGroup;
|
||||
do {
|
||||
RunnableEpochQueue* queue = mLabeled.Get(group);
|
||||
MOZ_ASSERT(queue);
|
||||
MOZ_ASSERT(!queue->IsEmpty());
|
||||
|
||||
mAvoidActiveTabCount--;
|
||||
SchedulerGroup* next = group->removeAndGetNext();
|
||||
mSchedulerGroups.insertBack(group);
|
||||
|
||||
RunnableEpochQueue* queue = mLabeled.Get(group);
|
||||
if (!queue) {
|
||||
// This can happen if |group| is in a different LabeledEventQueue than |this|.
|
||||
group = NextSchedulerGroup(group);
|
||||
continue;
|
||||
}
|
||||
MOZ_ASSERT(!queue->IsEmpty());
|
||||
|
||||
QueueEntry entry = queue->FirstElement();
|
||||
if (entry.mEpochNumber == epoch.mEpochNumber &&
|
||||
IsReadyToRun(entry.mRunnable, group)) {
|
||||
sCurrentSchedulerGroup = NextSchedulerGroup(group);
|
||||
|
||||
PopEpoch();
|
||||
|
||||
if (group->DequeueEvent() == SchedulerGroup::NoLongerQueued) {
|
||||
// Now we can take group out of sSchedulerGroups.
|
||||
if (sCurrentSchedulerGroup == group) {
|
||||
// Since we changed sCurrentSchedulerGroup above, we'll only get here
|
||||
// if |group| was the only element in sSchedulerGroups. In that case
|
||||
// set sCurrentSchedulerGroup to null.
|
||||
MOZ_ASSERT(group->getNext() == nullptr);
|
||||
MOZ_ASSERT(group->getPrevious() == nullptr);
|
||||
sCurrentSchedulerGroup = nullptr;
|
||||
}
|
||||
group->removeFrom(*sSchedulerGroups);
|
||||
}
|
||||
queue->Pop();
|
||||
if (queue->IsEmpty()) {
|
||||
mLabeled.Remove(group);
|
||||
group->removeFrom(mSchedulerGroups);
|
||||
}
|
||||
return entry.mRunnable.forget();
|
||||
}
|
||||
|
||||
group = next;
|
||||
group = NextSchedulerGroup(group);
|
||||
} while (group != firstGroup);
|
||||
|
||||
return nullptr;
|
||||
|
|
|
@ -30,6 +30,7 @@ class LabeledEventQueue final : public AbstractEventQueue
|
|||
{
|
||||
public:
|
||||
LabeledEventQueue();
|
||||
~LabeledEventQueue();
|
||||
|
||||
void PutEvent(already_AddRefed<nsIRunnable>&& aEvent,
|
||||
EventPriority aPriority,
|
||||
|
@ -127,13 +128,20 @@ private:
|
|||
};
|
||||
|
||||
void PopEpoch();
|
||||
static SchedulerGroup* NextSchedulerGroup(SchedulerGroup* aGroup);
|
||||
|
||||
using RunnableEpochQueue = Queue<QueueEntry, 32>;
|
||||
using LabeledMap = nsClassHashtable<nsRefPtrHashKey<SchedulerGroup>, RunnableEpochQueue>;
|
||||
using EpochQueue = Queue<Epoch, 8>;
|
||||
|
||||
// List of SchedulerGroups that have events in the queue.
|
||||
LinkedList<SchedulerGroup> mSchedulerGroups;
|
||||
// List of SchedulerGroups that might have events. This is static, so it
|
||||
// covers all LabeledEventQueues. If a SchedulerGroup is in this list, it may
|
||||
// not have an event in *this* LabeledEventQueue (although it will have an
|
||||
// event in *some* LabeledEventQueue). sCurrentSchedulerGroup cycles through
|
||||
// the elements of sSchedulerGroups in order.
|
||||
static LinkedList<SchedulerGroup>* sSchedulerGroups;
|
||||
static size_t sLabeledEventQueueCount;
|
||||
static SchedulerGroup* sCurrentSchedulerGroup;
|
||||
|
||||
LabeledMap mLabeled;
|
||||
RunnableEpochQueue mUnlabeled;
|
||||
|
|
|
@ -75,6 +75,36 @@ public:
|
|||
MOZ_ASSERT(IsSafeToRun());
|
||||
}
|
||||
|
||||
enum EnqueueStatus
|
||||
{
|
||||
NewlyQueued,
|
||||
AlreadyQueued,
|
||||
};
|
||||
|
||||
// Records that this SchedulerGroup had an event enqueued in some
|
||||
// queue. Returns whether the SchedulerGroup was already in a queue before
|
||||
// EnqueueEvent() was called.
|
||||
EnqueueStatus EnqueueEvent()
|
||||
{
|
||||
mEventCount++;
|
||||
return mEventCount == 1 ? NewlyQueued : AlreadyQueued;
|
||||
}
|
||||
|
||||
enum DequeueStatus
|
||||
{
|
||||
StillQueued,
|
||||
NoLongerQueued,
|
||||
};
|
||||
|
||||
// Records that this SchedulerGroup had an event dequeued from some
|
||||
// queue. Returns whether the SchedulerGroup is still in a queue after
|
||||
// DequeueEvent() returns.
|
||||
DequeueStatus DequeueEvent()
|
||||
{
|
||||
mEventCount--;
|
||||
return mEventCount == 0 ? NoLongerQueued : StillQueued;
|
||||
}
|
||||
|
||||
class Runnable final : public mozilla::Runnable
|
||||
, public nsIRunnablePriority
|
||||
, public nsILabelableRunnable
|
||||
|
@ -167,6 +197,10 @@ protected:
|
|||
|
||||
bool mIsRunning;
|
||||
|
||||
// Number of events that are currently enqueued for this SchedulerGroup
|
||||
// (across all queues).
|
||||
size_t mEventCount = 0;
|
||||
|
||||
nsCOMPtr<nsISerialEventTarget> mEventTargets[size_t(TaskCategory::Count)];
|
||||
RefPtr<AbstractThread> mAbstractThreads[size_t(TaskCategory::Count)];
|
||||
};
|
||||
|
|
Загрузка…
Ссылка в новой задаче