summaryrefslogtreecommitdiffstats
path: root/xpcom/threads/InputTaskManager.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'xpcom/threads/InputTaskManager.cpp')
-rw-r--r--xpcom/threads/InputTaskManager.cpp156
1 files changed, 156 insertions, 0 deletions
diff --git a/xpcom/threads/InputTaskManager.cpp b/xpcom/threads/InputTaskManager.cpp
new file mode 100644
index 0000000000..42b2814290
--- /dev/null
+++ b/xpcom/threads/InputTaskManager.cpp
@@ -0,0 +1,156 @@
+/* -*- 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/. */
+
+#include "InputTaskManager.h"
+#include "VsyncTaskManager.h"
+#include "nsRefreshDriver.h"
+
+namespace mozilla {
+
+StaticRefPtr<InputTaskManager> InputTaskManager::gInputTaskManager;
+
+void InputTaskManager::EnableInputEventPrioritization() {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(mInputQueueState == STATE_DISABLED);
+ mInputQueueState = STATE_ENABLED;
+}
+
+void InputTaskManager::FlushInputEventPrioritization() {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(mInputQueueState == STATE_ENABLED ||
+ mInputQueueState == STATE_SUSPEND);
+ mInputQueueState =
+ mInputQueueState == STATE_ENABLED ? STATE_FLUSHING : STATE_SUSPEND;
+}
+
+void InputTaskManager::SuspendInputEventPrioritization() {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(mInputQueueState == STATE_ENABLED ||
+ mInputQueueState == STATE_FLUSHING);
+ mInputQueueState = STATE_SUSPEND;
+}
+
+void InputTaskManager::ResumeInputEventPrioritization() {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(mInputQueueState == STATE_SUSPEND);
+ mInputQueueState = STATE_ENABLED;
+}
+
+int32_t InputTaskManager::GetPriorityModifierForEventLoopTurn(
+ const MutexAutoLock& aProofOfLock) {
+ // When the state is disabled, the input task that we have is
+ // very likely SuspendInputEventQueue, so here we also use
+ // normal priority as ResumeInputEventQueue, FlushInputEventQueue and
+ // SetInputEventQueueEnabled all uses normal priority, to
+ // ensure the ordering is correct.
+ if (State() == InputTaskManager::STATE_DISABLED) {
+ return static_cast<int32_t>(EventQueuePriority::Normal) -
+ static_cast<int32_t>(EventQueuePriority::InputHigh);
+ }
+
+ return GetPriorityModifierForEventLoopTurnForStrictVsyncAlignment();
+}
+
+void InputTaskManager::WillRunTask() {
+ TaskManager::WillRunTask();
+ mInputPriorityController.WillRunTask();
+}
+
+int32_t
+InputTaskManager::GetPriorityModifierForEventLoopTurnForStrictVsyncAlignment() {
+ MOZ_ASSERT(!IsSuspended());
+
+ size_t inputCount = PendingTaskCount();
+ if (inputCount > 0 &&
+ mInputPriorityController.ShouldUseHighestPriority(this)) {
+ return static_cast<int32_t>(EventQueuePriority::InputHighest) -
+ static_cast<int32_t>(EventQueuePriority::InputHigh);
+ }
+
+ if (State() == STATE_FLUSHING ||
+ nsRefreshDriver::GetNextTickHint().isNothing()) {
+ return 0;
+ }
+
+ return static_cast<int32_t>(EventQueuePriority::InputLow) -
+ static_cast<int32_t>(EventQueuePriority::InputHigh);
+}
+
+InputTaskManager::InputPriorityController::InputPriorityController()
+ : mInputVsyncState(InputVsyncState::NoPendingVsync) {}
+
+bool InputTaskManager::InputPriorityController::ShouldUseHighestPriority(
+ InputTaskManager* aInputTaskManager) {
+ if (mInputVsyncState == InputVsyncState::HasPendingVsync) {
+ return true;
+ }
+
+ if (mInputVsyncState == InputVsyncState::RunVsync) {
+ return false;
+ }
+
+ if (mInputVsyncState == InputVsyncState::NoPendingVsync &&
+ VsyncTaskManager::Get()->PendingTaskCount()) {
+ EnterPendingVsyncState(aInputTaskManager->PendingTaskCount());
+ return true;
+ }
+
+ return false;
+}
+
+void InputTaskManager::InputPriorityController::EnterPendingVsyncState(
+ uint32_t aNumPendingTasks) {
+ MOZ_ASSERT(mInputVsyncState == InputVsyncState::NoPendingVsync);
+
+ mInputVsyncState = InputVsyncState::HasPendingVsync;
+ mMaxInputTasksToRun = aNumPendingTasks;
+ mRunInputStartTime = TimeStamp::Now();
+}
+
+void InputTaskManager::InputPriorityController::WillRunVsync() {
+ if (mInputVsyncState == InputVsyncState::RunVsync ||
+ mInputVsyncState == InputVsyncState::HasPendingVsync) {
+ LeavePendingVsyncState(false);
+ }
+}
+
+void InputTaskManager::InputPriorityController::LeavePendingVsyncState(
+ bool aRunVsync) {
+ if (aRunVsync) {
+ MOZ_ASSERT(mInputVsyncState == InputVsyncState::HasPendingVsync);
+ mInputVsyncState = InputVsyncState::RunVsync;
+ } else {
+ mInputVsyncState = InputVsyncState::NoPendingVsync;
+ }
+
+ mMaxInputTasksToRun = 0;
+}
+
+void InputTaskManager::InputPriorityController::WillRunTask() {
+ switch (mInputVsyncState) {
+ case InputVsyncState::NoPendingVsync:
+ return;
+ case InputVsyncState::HasPendingVsync:
+ MOZ_ASSERT(mMaxInputTasksToRun > 0);
+ --mMaxInputTasksToRun;
+ if (!mMaxInputTasksToRun ||
+ TimeStamp::Now() - mRunInputStartTime >=
+ TimeDuration::FromMilliseconds(
+ StaticPrefs::dom_input_event_queue_duration_max())) {
+ LeavePendingVsyncState(true);
+ }
+ return;
+ default:
+ MOZ_DIAGNOSTIC_ASSERT(
+ false, "Shouldn't run this input task when we suppose to run vsync");
+ return;
+ }
+}
+
+// static
+void InputTaskManager::Init() { gInputTaskManager = new InputTaskManager(); }
+
+} // namespace mozilla