Bug 1398423 - Make linked list of SchedulerGroups static (r=froydnj)

MozReview-Commit-ID: GgfdRlhPiHP
This commit is contained in:
Bill McCloskey 2017-09-08 15:54:39 -07:00
Родитель fe8e58f308
Коммит b9683a5b18
3 изменённых файлов: 120 добавлений и 20 удалений

Просмотреть файл

@ -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)];
};