-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathPrioritizedEventQueue.h
177 lines (144 loc) · 6.53 KB
/
PrioritizedEventQueue.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
/* -*- 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/. */
#ifndef mozilla_PrioritizedEventQueue_h
#define mozilla_PrioritizedEventQueue_h
#include "mozilla/AbstractEventQueue.h"
#include "mozilla/Atomics.h"
#include "mozilla/EventQueue.h"
#include "mozilla/IdlePeriodState.h"
#include "mozilla/TaskController.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/UniquePtr.h"
#include "nsCOMPtr.h"
class nsIIdlePeriod;
class nsIRunnable;
namespace mozilla {
namespace ipc {
class IdleSchedulerChild;
}
class InputTaskManager : public TaskManager {
public:
InputTaskManager() : mInputQueueState(STATE_DISABLED) {}
int32_t GetPriorityModifierForEventLoopTurn(
const MutexAutoLock& aProofOfLock) final;
void WillRunTask() final;
void DidRunTask() final;
enum InputEventQueueState {
STATE_DISABLED,
STATE_FLUSHING,
STATE_SUSPEND,
STATE_ENABLED
};
void EnableInputEventPrioritization();
void FlushInputEventPrioritization();
void SuspendInputEventPrioritization();
void ResumeInputEventPrioritization();
InputEventQueueState State() { return mInputQueueState; }
void SetState(InputEventQueueState aState) { mInputQueueState = aState; }
TimeStamp InputHandlingStartTime() { return mInputHandlingStartTime; }
void SetInputHandlingStartTime(TimeStamp aStartTime) {
mInputHandlingStartTime = aStartTime;
}
private:
TimeStamp mInputHandlingStartTime;
Atomic<InputEventQueueState> mInputQueueState;
AutoTArray<TimeStamp, 4> mStartTimes;
};
// This AbstractEventQueue implementation has one queue for each
// EventQueuePriority. The type of queue used for each priority is determined by
// the template parameter.
//
// When an event is pushed, its priority is determined by QIing the runnable to
// nsIRunnablePriority, or by falling back to the aPriority parameter if the QI
// fails.
//
// When an event is popped, a queue is selected based on heuristics that
// optimize for performance. Roughly, events are selected from the highest
// priority queue that is non-empty. However, there are a few exceptions:
// - We try to avoid processing too many high-priority events in a row so
// that the normal priority queue is not starved. When there are high-
// and normal-priority events available, we interleave popping from the
// normal and high queues.
// - We do not select events from the idle queue if the current idle period
// is almost over.
class PrioritizedEventQueue final : public AbstractEventQueue {
public:
static const bool SupportsPrioritization = true;
explicit PrioritizedEventQueue(already_AddRefed<nsIIdlePeriod>&& aIdlePeriod);
virtual ~PrioritizedEventQueue();
void PutEvent(already_AddRefed<nsIRunnable>&& aEvent,
EventQueuePriority aPriority, const MutexAutoLock& aProofOfLock,
mozilla::TimeDuration* aDelay = nullptr) final;
// See PrioritizedEventQueue.cpp for explanation of
// aHypotheticalInputEventDelay
already_AddRefed<nsIRunnable> GetEvent(
EventQueuePriority* aPriority, const MutexAutoLock& aProofOfLock,
TimeDuration* aHypotheticalInputEventDelay = nullptr) final;
// *aIsIdleEvent will be set to true when we are returning a non-null runnable
// which came from one of our idle queues, and will be false otherwise.
already_AddRefed<nsIRunnable> GetEvent(
EventQueuePriority* aPriority, const MutexAutoLock& aProofOfLock,
TimeDuration* aHypotheticalInputEventDelay, bool* aIsIdleEvent);
void DidRunEvent(const MutexAutoLock& aProofOfLock);
bool IsEmpty(const MutexAutoLock& aProofOfLock) final;
size_t Count(const MutexAutoLock& aProofOfLock) const final;
bool HasReadyEvent(const MutexAutoLock& aProofOfLock) final;
bool HasPendingHighPriorityEvents(const MutexAutoLock& aProofOfLock) final;
// When checking the idle deadline, we need to drop whatever mutex protects
// this queue. This method allows that mutex to be stored so that we can drop
// it and reacquire it when checking the idle deadline. The mutex must live at
// least as long as the queue.
void SetMutexRef(Mutex& aMutex) { mMutex = &aMutex; }
void EnableInputEventPrioritization(const MutexAutoLock& aProofOfLock) final {
mInputTaskManager->EnableInputEventPrioritization();
}
void FlushInputEventPrioritization(const MutexAutoLock& aProofOfLock) final {
mInputTaskManager->FlushInputEventPrioritization();
}
void SuspendInputEventPrioritization(
const MutexAutoLock& aProofOfLock) final {
mInputTaskManager->SuspendInputEventPrioritization();
}
void ResumeInputEventPrioritization(const MutexAutoLock& aProofOfLock) final {
mInputTaskManager->ResumeInputEventPrioritization();
}
IdlePeriodState* GetIdlePeriodState() { return &mIdleTaskManager->State(); }
bool HasIdleRunnables(const MutexAutoLock& aProofOfLock) const;
size_t SizeOfExcludingThis(
mozilla::MallocSizeOf aMallocSizeOf) const override {
size_t n = 0;
n += mHighQueue->SizeOfIncludingThis(aMallocSizeOf);
n += mInputQueue->SizeOfIncludingThis(aMallocSizeOf);
n += mMediumHighQueue->SizeOfIncludingThis(aMallocSizeOf);
n += mNormalQueue->SizeOfIncludingThis(aMallocSizeOf);
n += mDeferredTimersQueue->SizeOfIncludingThis(aMallocSizeOf);
n += mIdleQueue->SizeOfIncludingThis(aMallocSizeOf);
n += mIdleTaskManager->State().SizeOfExcludingThis(aMallocSizeOf);
return n;
}
private:
EventQueuePriority SelectQueue(bool aUpdateState,
const MutexAutoLock& aProofOfLock);
void IndirectlyQueueRunnable(already_AddRefed<nsIRunnable>&& aEvent,
EventQueuePriority aPriority,
const MutexAutoLock& aProofOfLock,
mozilla::TimeDuration* aDelay);
UniquePtr<EventQueue> mHighQueue;
UniquePtr<EventQueueSized<32>> mInputQueue;
UniquePtr<EventQueue> mMediumHighQueue;
UniquePtr<EventQueueSized<64>> mNormalQueue;
UniquePtr<EventQueue> mDeferredTimersQueue;
UniquePtr<EventQueue> mIdleQueue;
// We need to drop the queue mutex when checking the idle deadline, so we keep
// a pointer to it here.
Mutex* mMutex = nullptr;
TimeDuration mLastEventDelay;
TimeStamp mLastEventStart;
RefPtr<InputTaskManager> mInputTaskManager;
RefPtr<IdleTaskManager> mIdleTaskManager;
};
} // namespace mozilla
#endif // mozilla_PrioritizedEventQueue_h