зеркало из https://github.com/mozilla/gecko-dev.git
Backed out 2 changesets (bug 1606706) for talos tests timeouts CLOSED TREE
Backed out changeset ab3e1a067a71 (bug 1606706) Backed out changeset c47cf57dab71 (bug 1606706)
This commit is contained in:
Родитель
ffdcf42e89
Коммит
2719f07eb0
|
@ -5,7 +5,6 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/plugins/PluginProcessChild.h"
|
||||
#include "mozilla/TaskController.h"
|
||||
|
||||
#include "ClearOnShutdown.h"
|
||||
#include "base/command_line.h"
|
||||
|
@ -196,10 +195,7 @@ void PluginProcessChild::CleanUp() {
|
|||
#endif
|
||||
|
||||
mozilla::KillClearOnShutdown(ShutdownPhase::ShutdownFinal);
|
||||
|
||||
AbstractThread::ShutdownMainThread();
|
||||
|
||||
mozilla::TaskController::Shutdown();
|
||||
}
|
||||
|
||||
} // namespace plugins
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
#include "mozilla/Poison.h"
|
||||
#include "mozilla/RemoteDecoderManagerChild.h"
|
||||
#include "mozilla/SharedThreadPool.h"
|
||||
#include "mozilla/TaskController.h"
|
||||
#include "mozilla/XPCOM.h"
|
||||
#include "mozJSComponentLoader.h"
|
||||
#include "nsXULAppAPI.h"
|
||||
|
@ -808,8 +807,6 @@ nsresult ShutdownXPCOM(nsIServiceManager* aServMgr) {
|
|||
delete sMessageLoop;
|
||||
sMessageLoop = nullptr;
|
||||
|
||||
mozilla::TaskController::Shutdown();
|
||||
|
||||
if (sCommandLineWasInitialized) {
|
||||
CommandLine::Terminate();
|
||||
sCommandLineWasInitialized = false;
|
||||
|
|
|
@ -17,15 +17,12 @@ class nsIRunnable;
|
|||
namespace mozilla {
|
||||
|
||||
enum class EventQueuePriority {
|
||||
Idle,
|
||||
DeferredTimers,
|
||||
InputLow,
|
||||
// Used to ensure high priority tasks don't starve ~normal priority tasks
|
||||
HighLow,
|
||||
Normal,
|
||||
MediumHigh,
|
||||
InputHigh,
|
||||
High,
|
||||
Input,
|
||||
MediumHigh,
|
||||
Normal,
|
||||
DeferredTimers,
|
||||
Idle,
|
||||
|
||||
Count
|
||||
};
|
||||
|
|
|
@ -25,7 +25,6 @@
|
|||
class nsIIdlePeriod;
|
||||
|
||||
namespace mozilla {
|
||||
class TaskManager;
|
||||
namespace ipc {
|
||||
class IdleSchedulerChild;
|
||||
} // namespace ipc
|
||||
|
|
|
@ -27,7 +27,7 @@ inline already_AddRefed<nsThread> CreateMainThread(
|
|||
MainThreadQueueT* prioritized = queue.get();
|
||||
|
||||
RefPtr<SynchronizedQueueT> synchronizedQueue =
|
||||
new SynchronizedQueueT(std::move(queue), true);
|
||||
new SynchronizedQueueT(std::move(queue));
|
||||
|
||||
prioritized->SetMutexRef(synchronizedQueue->MutexRef());
|
||||
|
||||
|
|
|
@ -12,24 +12,19 @@
|
|||
#include "nsThreadManager.h"
|
||||
#include "nsXPCOMPrivate.h" // for gXPCOMThreadsShutDown
|
||||
#include "InputEventStatistics.h"
|
||||
#include "MainThreadUtils.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
PrioritizedEventQueue::PrioritizedEventQueue(
|
||||
already_AddRefed<nsIIdlePeriod>&& aIdlePeriod)
|
||||
: mHighQueue(MakeUnique<EventQueue>(EventQueuePriority::High)),
|
||||
mInputQueue(
|
||||
MakeUnique<EventQueueSized<32>>(EventQueuePriority::InputHigh)),
|
||||
mInputQueue(MakeUnique<EventQueueSized<32>>(EventQueuePriority::Input)),
|
||||
mMediumHighQueue(MakeUnique<EventQueue>(EventQueuePriority::MediumHigh)),
|
||||
mNormalQueue(MakeUnique<EventQueueSized<64>>(EventQueuePriority::Normal)),
|
||||
mDeferredTimersQueue(
|
||||
MakeUnique<EventQueue>(EventQueuePriority::DeferredTimers)),
|
||||
mIdleQueue(MakeUnique<EventQueue>(EventQueuePriority::Idle)) {
|
||||
mInputTaskManager = new InputTaskManager();
|
||||
mIdleTaskManager = new IdleTaskManager(std::move(aIdlePeriod));
|
||||
TaskController::Get()->SetIdleTaskManager(mIdleTaskManager);
|
||||
}
|
||||
mIdleQueue(MakeUnique<EventQueue>(EventQueuePriority::Idle)),
|
||||
mIdlePeriodState(std::move(aIdlePeriod)) {}
|
||||
|
||||
PrioritizedEventQueue::~PrioritizedEventQueue() = default;
|
||||
|
||||
|
@ -37,33 +32,12 @@ void PrioritizedEventQueue::PutEvent(already_AddRefed<nsIRunnable>&& aEvent,
|
|||
EventQueuePriority aPriority,
|
||||
const MutexAutoLock& aProofOfLock,
|
||||
mozilla::TimeDuration* aDelay) {
|
||||
RefPtr<nsIRunnable> event(aEvent);
|
||||
|
||||
if (UseTaskController()) {
|
||||
TaskController* tc = TaskController::Get();
|
||||
|
||||
TaskManager* manager = nullptr;
|
||||
if (aPriority == EventQueuePriority::InputHigh) {
|
||||
if (mInputTaskManager->State() == InputTaskManager::STATE_DISABLED) {
|
||||
aPriority = EventQueuePriority::Normal;
|
||||
} else {
|
||||
manager = mInputTaskManager;
|
||||
}
|
||||
} else if (aPriority == EventQueuePriority::DeferredTimers ||
|
||||
aPriority == EventQueuePriority::Idle) {
|
||||
manager = mIdleTaskManager;
|
||||
}
|
||||
|
||||
tc->DispatchRunnable(event.forget(), static_cast<uint32_t>(aPriority),
|
||||
manager);
|
||||
return;
|
||||
}
|
||||
|
||||
// Double check the priority with a QI.
|
||||
RefPtr<nsIRunnable> event(aEvent);
|
||||
EventQueuePriority priority = aPriority;
|
||||
|
||||
if (priority == EventQueuePriority::InputHigh &&
|
||||
mInputTaskManager->State() == InputTaskManager::STATE_DISABLED) {
|
||||
if (priority == EventQueuePriority::Input &&
|
||||
mInputQueueState == STATE_DISABLED) {
|
||||
priority = EventQueuePriority::Normal;
|
||||
} else if (priority == EventQueuePriority::MediumHigh &&
|
||||
!StaticPrefs::threads_medium_high_event_queue_enabled()) {
|
||||
|
@ -74,7 +48,7 @@ void PrioritizedEventQueue::PutEvent(already_AddRefed<nsIRunnable>&& aEvent,
|
|||
case EventQueuePriority::High:
|
||||
mHighQueue->PutEvent(event.forget(), priority, aProofOfLock, aDelay);
|
||||
break;
|
||||
case EventQueuePriority::InputHigh:
|
||||
case EventQueuePriority::Input:
|
||||
mInputQueue->PutEvent(event.forget(), priority, aProofOfLock, aDelay);
|
||||
break;
|
||||
case EventQueuePriority::MediumHigh:
|
||||
|
@ -84,12 +58,6 @@ void PrioritizedEventQueue::PutEvent(already_AddRefed<nsIRunnable>&& aEvent,
|
|||
case EventQueuePriority::Normal:
|
||||
mNormalQueue->PutEvent(event.forget(), priority, aProofOfLock, aDelay);
|
||||
break;
|
||||
case EventQueuePriority::HighLow:
|
||||
MOZ_ASSERT(false, "HighLow is a TaskController's internal priority!");
|
||||
break;
|
||||
case EventQueuePriority::InputLow:
|
||||
MOZ_ASSERT(false, "InputLow is a TaskController's internal priority!");
|
||||
break;
|
||||
case EventQueuePriority::DeferredTimers: {
|
||||
if (NS_IsMainThread()) {
|
||||
mDeferredTimersQueue->PutEvent(event.forget(), priority, aProofOfLock,
|
||||
|
@ -121,11 +89,10 @@ EventQueuePriority PrioritizedEventQueue::SelectQueue(
|
|||
bool aUpdateState, const MutexAutoLock& aProofOfLock) {
|
||||
size_t inputCount = mInputQueue->Count(aProofOfLock);
|
||||
|
||||
if (aUpdateState &&
|
||||
mInputTaskManager->State() == InputTaskManager::STATE_ENABLED &&
|
||||
mInputTaskManager->InputHandlingStartTime().IsNull() && inputCount > 0) {
|
||||
mInputTaskManager->SetInputHandlingStartTime(
|
||||
InputEventStatistics::Get().GetInputHandlingStartTime(inputCount));
|
||||
if (aUpdateState && mInputQueueState == STATE_ENABLED &&
|
||||
mInputHandlingStartTime.IsNull() && inputCount > 0) {
|
||||
mInputHandlingStartTime =
|
||||
InputEventStatistics::Get().GetInputHandlingStartTime(inputCount);
|
||||
}
|
||||
|
||||
// We check the different queues in the following order. The conditions we use
|
||||
|
@ -133,10 +100,9 @@ EventQueuePriority PrioritizedEventQueue::SelectQueue(
|
|||
// at the wrong time.
|
||||
//
|
||||
// HIGH:
|
||||
// INPUT: if inputCount > 0 && TimeStamp::Now() >
|
||||
// mInputTaskManager->InputHandlingStartTime(...);
|
||||
// MEDIUMHIGH: if medium high
|
||||
// pending NORMAL: if normal pending
|
||||
// INPUT: if inputCount > 0 && TimeStamp::Now() > mInputHandlingStartTime
|
||||
// MEDIUMHIGH: if medium high pending
|
||||
// NORMAL: if normal pending
|
||||
//
|
||||
// If we still don't have an event, then we take events from the queues
|
||||
// in the following order:
|
||||
|
@ -151,28 +117,25 @@ EventQueuePriority PrioritizedEventQueue::SelectQueue(
|
|||
EventQueuePriority queue;
|
||||
if (!mHighQueue->IsEmpty(aProofOfLock)) {
|
||||
queue = EventQueuePriority::High;
|
||||
} else if (inputCount > 0 &&
|
||||
(mInputTaskManager->State() == InputTaskManager::STATE_FLUSHING ||
|
||||
(mInputTaskManager->State() == InputTaskManager::STATE_ENABLED &&
|
||||
!mInputTaskManager->InputHandlingStartTime().IsNull() &&
|
||||
TimeStamp::Now() >
|
||||
mInputTaskManager->InputHandlingStartTime()))) {
|
||||
queue = EventQueuePriority::InputHigh;
|
||||
} else if (inputCount > 0 && (mInputQueueState == STATE_FLUSHING ||
|
||||
(mInputQueueState == STATE_ENABLED &&
|
||||
!mInputHandlingStartTime.IsNull() &&
|
||||
TimeStamp::Now() > mInputHandlingStartTime))) {
|
||||
queue = EventQueuePriority::Input;
|
||||
} else if (!mMediumHighQueue->IsEmpty(aProofOfLock)) {
|
||||
MOZ_ASSERT(
|
||||
mInputTaskManager->State() != InputTaskManager::STATE_FLUSHING,
|
||||
mInputQueueState != STATE_FLUSHING,
|
||||
"Shouldn't consume medium high event when flushing input events");
|
||||
queue = EventQueuePriority::MediumHigh;
|
||||
} else if (!mNormalQueue->IsEmpty(aProofOfLock)) {
|
||||
MOZ_ASSERT(mInputTaskManager->State() != InputTaskManager::STATE_FLUSHING,
|
||||
MOZ_ASSERT(mInputQueueState != STATE_FLUSHING,
|
||||
"Shouldn't consume normal event when flushing input events");
|
||||
queue = EventQueuePriority::Normal;
|
||||
} else if (inputCount > 0 &&
|
||||
mInputTaskManager->State() != InputTaskManager::STATE_SUSPEND) {
|
||||
} else if (inputCount > 0 && mInputQueueState != STATE_SUSPEND) {
|
||||
MOZ_ASSERT(
|
||||
mInputTaskManager->State() != InputTaskManager::STATE_DISABLED,
|
||||
mInputQueueState != STATE_DISABLED,
|
||||
"Shouldn't consume input events when the input queue is disabled");
|
||||
queue = EventQueuePriority::InputHigh;
|
||||
queue = EventQueuePriority::Input;
|
||||
} else if (!mDeferredTimersQueue->IsEmpty(aProofOfLock)) {
|
||||
// We may not actually return an idle event in this case.
|
||||
queue = EventQueuePriority::DeferredTimers;
|
||||
|
@ -182,10 +145,8 @@ EventQueuePriority PrioritizedEventQueue::SelectQueue(
|
|||
}
|
||||
|
||||
MOZ_ASSERT_IF(
|
||||
queue == EventQueuePriority::InputHigh ||
|
||||
queue == EventQueuePriority::InputLow,
|
||||
mInputTaskManager->State() != InputTaskManager::STATE_DISABLED &&
|
||||
mInputTaskManager->State() != InputTaskManager::STATE_SUSPEND);
|
||||
queue == EventQueuePriority::Input,
|
||||
mInputQueueState != STATE_DISABLED && mInputQueueState != STATE_SUSPEND);
|
||||
|
||||
return queue;
|
||||
}
|
||||
|
@ -245,10 +206,10 @@ already_AddRefed<nsIRunnable> PrioritizedEventQueue::GetEvent(
|
|||
event = mHighQueue->GetEvent(nullptr, aProofOfLock,
|
||||
aHypotheticalInputEventDelay);
|
||||
MOZ_ASSERT(event);
|
||||
mInputTaskManager->SetInputHandlingStartTime(TimeStamp());
|
||||
mInputHandlingStartTime = TimeStamp();
|
||||
break;
|
||||
|
||||
case EventQueuePriority::InputHigh:
|
||||
case EventQueuePriority::Input:
|
||||
event = mInputQueue->GetEvent(nullptr, aProofOfLock,
|
||||
aHypotheticalInputEventDelay);
|
||||
MOZ_ASSERT(event);
|
||||
|
@ -278,8 +239,7 @@ already_AddRefed<nsIRunnable> PrioritizedEventQueue::GetEvent(
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
TimeStamp idleDeadline =
|
||||
mIdleTaskManager->State().GetCachedIdleDeadline();
|
||||
TimeStamp idleDeadline = mIdlePeriodState.GetCachedIdleDeadline();
|
||||
if (!idleDeadline) {
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -313,7 +273,7 @@ void PrioritizedEventQueue::DidRunEvent(const MutexAutoLock& aProofOfLock) {
|
|||
// our caller does nothing after this except return, which unlocks
|
||||
// its lock anyway.
|
||||
MutexAutoUnlock unlock(*mMutex);
|
||||
mIdleTaskManager->State().RanOutOfTasks(unlock);
|
||||
mIdlePeriodState.RanOutOfTasks(unlock);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -329,13 +289,13 @@ bool PrioritizedEventQueue::IsEmpty(const MutexAutoLock& aProofOfLock) {
|
|||
}
|
||||
|
||||
bool PrioritizedEventQueue::HasReadyEvent(const MutexAutoLock& aProofOfLock) {
|
||||
mIdleTaskManager->State().ForgetPendingTaskGuarantee();
|
||||
mIdlePeriodState.ForgetPendingTaskGuarantee();
|
||||
|
||||
EventQueuePriority queue = SelectQueue(false, aProofOfLock);
|
||||
|
||||
if (queue == EventQueuePriority::High) {
|
||||
return mHighQueue->HasReadyEvent(aProofOfLock);
|
||||
} else if (queue == EventQueuePriority::InputHigh) {
|
||||
} else if (queue == EventQueuePriority::Input) {
|
||||
return mInputQueue->HasReadyEvent(aProofOfLock);
|
||||
} else if (queue == EventQueuePriority::MediumHigh) {
|
||||
return mMediumHighQueue->HasReadyEvent(aProofOfLock);
|
||||
|
@ -356,16 +316,16 @@ bool PrioritizedEventQueue::HasReadyEvent(const MutexAutoLock& aProofOfLock) {
|
|||
// Temporarily unlock so we can peek our idle deadline.
|
||||
{
|
||||
MutexAutoUnlock unlock(*mMutex);
|
||||
mIdleTaskManager->State().CachePeekedIdleDeadline(unlock);
|
||||
mIdlePeriodState.CachePeekedIdleDeadline(unlock);
|
||||
}
|
||||
TimeStamp idleDeadline = mIdleTaskManager->State().GetCachedIdleDeadline();
|
||||
mIdleTaskManager->State().ClearCachedIdleDeadline();
|
||||
TimeStamp idleDeadline = mIdlePeriodState.GetCachedIdleDeadline();
|
||||
mIdlePeriodState.ClearCachedIdleDeadline();
|
||||
|
||||
// Re-check the emptiness of the queues, since we had the lock released for a
|
||||
// bit.
|
||||
if (idleDeadline && (mDeferredTimersQueue->HasReadyEvent(aProofOfLock) ||
|
||||
mIdleQueue->HasReadyEvent(aProofOfLock))) {
|
||||
mIdleTaskManager->State().EnforcePendingTaskGuarantee();
|
||||
mIdlePeriodState.EnforcePendingTaskGuarantee();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -377,73 +337,40 @@ bool PrioritizedEventQueue::HasPendingHighPriorityEvents(
|
|||
return !mHighQueue->IsEmpty(aProofOfLock);
|
||||
}
|
||||
|
||||
bool PrioritizedEventQueue::HasIdleRunnables(
|
||||
const MutexAutoLock& aProofOfLock) const {
|
||||
return !mIdleQueue->IsEmpty(aProofOfLock) ||
|
||||
!mDeferredTimersQueue->IsEmpty(aProofOfLock);
|
||||
}
|
||||
|
||||
size_t PrioritizedEventQueue::Count(const MutexAutoLock& aProofOfLock) const {
|
||||
MOZ_CRASH("unimplemented");
|
||||
}
|
||||
|
||||
void InputTaskManager::EnableInputEventPrioritization() {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
void PrioritizedEventQueue::EnableInputEventPrioritization(
|
||||
const MutexAutoLock& aProofOfLock) {
|
||||
MOZ_ASSERT(mInputQueueState == STATE_DISABLED);
|
||||
mInputQueueState = STATE_ENABLED;
|
||||
mInputHandlingStartTime = TimeStamp();
|
||||
}
|
||||
|
||||
void InputTaskManager::FlushInputEventPrioritization() {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
void PrioritizedEventQueue::FlushInputEventPrioritization(
|
||||
const MutexAutoLock& aProofOfLock) {
|
||||
MOZ_ASSERT(mInputQueueState == STATE_ENABLED ||
|
||||
mInputQueueState == STATE_SUSPEND);
|
||||
mInputQueueState =
|
||||
mInputQueueState == STATE_ENABLED ? STATE_FLUSHING : STATE_SUSPEND;
|
||||
}
|
||||
|
||||
void InputTaskManager::SuspendInputEventPrioritization() {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
void PrioritizedEventQueue::SuspendInputEventPrioritization(
|
||||
const MutexAutoLock& aProofOfLock) {
|
||||
MOZ_ASSERT(mInputQueueState == STATE_ENABLED ||
|
||||
mInputQueueState == STATE_FLUSHING);
|
||||
mInputQueueState = STATE_SUSPEND;
|
||||
}
|
||||
|
||||
void InputTaskManager::ResumeInputEventPrioritization() {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
void PrioritizedEventQueue::ResumeInputEventPrioritization(
|
||||
const MutexAutoLock& aProofOfLock) {
|
||||
MOZ_ASSERT(mInputQueueState == STATE_SUSPEND);
|
||||
mInputQueueState = STATE_ENABLED;
|
||||
}
|
||||
|
||||
int32_t InputTaskManager::GetPriorityModifierForEventLoopTurn(
|
||||
const MutexAutoLock& aProofOfLock) {
|
||||
size_t inputCount = PendingTaskCount();
|
||||
if (State() == STATE_ENABLED && InputHandlingStartTime().IsNull() &&
|
||||
inputCount > 0) {
|
||||
SetInputHandlingStartTime(
|
||||
InputEventStatistics::Get().GetInputHandlingStartTime(inputCount));
|
||||
}
|
||||
|
||||
if (inputCount > 0 && (State() == InputTaskManager::STATE_FLUSHING ||
|
||||
(State() == InputTaskManager::STATE_ENABLED &&
|
||||
!InputHandlingStartTime().IsNull() &&
|
||||
TimeStamp::Now() > InputHandlingStartTime()))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t modifier = static_cast<int32_t>(EventQueuePriority::InputLow) -
|
||||
static_cast<int32_t>(EventQueuePriority::MediumHigh);
|
||||
return modifier;
|
||||
}
|
||||
|
||||
void InputTaskManager::WillRunTask() {
|
||||
TaskManager::WillRunTask();
|
||||
mStartTimes.AppendElement(TimeStamp::Now());
|
||||
}
|
||||
|
||||
void InputTaskManager::DidRunTask() {
|
||||
TaskManager::DidRunTask();
|
||||
MOZ_ASSERT(!mStartTimes.IsEmpty());
|
||||
TimeStamp start = mStartTimes.PopLastElement();
|
||||
InputEventStatistics::Get().UpdateInputDuration(TimeStamp::Now() - start);
|
||||
bool PrioritizedEventQueue::HasIdleRunnables(
|
||||
const MutexAutoLock& aProofOfLock) const {
|
||||
return !mIdleQueue->IsEmpty(aProofOfLock) ||
|
||||
!mDeferredTimersQueue->IsEmpty(aProofOfLock);
|
||||
}
|
||||
|
|
|
@ -8,10 +8,8 @@
|
|||
#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"
|
||||
|
@ -24,42 +22,6 @@ 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.
|
||||
|
@ -111,21 +73,12 @@ class PrioritizedEventQueue final : public AbstractEventQueue {
|
|||
// 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();
|
||||
}
|
||||
void EnableInputEventPrioritization(const MutexAutoLock& aProofOfLock) final;
|
||||
void FlushInputEventPrioritization(const MutexAutoLock& aProofOfLock) final;
|
||||
void SuspendInputEventPrioritization(const MutexAutoLock& aProofOfLock) final;
|
||||
void ResumeInputEventPrioritization(const MutexAutoLock& aProofOfLock) final;
|
||||
|
||||
IdlePeriodState* GetIdlePeriodState() { return &mIdleTaskManager->State(); }
|
||||
IdlePeriodState* GetIdlePeriodState() { return &mIdlePeriodState; }
|
||||
|
||||
bool HasIdleRunnables(const MutexAutoLock& aProofOfLock) const;
|
||||
|
||||
|
@ -140,7 +93,7 @@ class PrioritizedEventQueue final : public AbstractEventQueue {
|
|||
n += mDeferredTimersQueue->SizeOfIncludingThis(aMallocSizeOf);
|
||||
n += mIdleQueue->SizeOfIncludingThis(aMallocSizeOf);
|
||||
|
||||
n += mIdleTaskManager->State().SizeOfExcludingThis(aMallocSizeOf);
|
||||
n += mIdlePeriodState.SizeOfExcludingThis(aMallocSizeOf);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
@ -168,8 +121,18 @@ class PrioritizedEventQueue final : public AbstractEventQueue {
|
|||
TimeDuration mLastEventDelay;
|
||||
TimeStamp mLastEventStart;
|
||||
|
||||
RefPtr<InputTaskManager> mInputTaskManager;
|
||||
RefPtr<IdleTaskManager> mIdleTaskManager;
|
||||
TimeStamp mInputHandlingStartTime;
|
||||
|
||||
enum InputEventQueueState {
|
||||
STATE_DISABLED,
|
||||
STATE_FLUSHING,
|
||||
STATE_SUSPEND,
|
||||
STATE_ENABLED
|
||||
};
|
||||
InputEventQueueState mInputQueueState = STATE_DISABLED;
|
||||
|
||||
// Tracking of our idle state of various sorts.
|
||||
IdlePeriodState mIdlePeriodState;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -1,585 +0,0 @@
|
|||
/* -*- 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 "TaskController.h"
|
||||
#include "nsIIdleRunnable.h"
|
||||
#include "nsIRunnable.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include <algorithm>
|
||||
#include <initializer_list>
|
||||
#include "mozilla/AbstractEventQueue.h"
|
||||
#include "mozilla/StaticMutex.h"
|
||||
#include "mozilla/ScopeExit.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "nsIThreadInternal.h"
|
||||
#include "nsThread.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
std::unique_ptr<TaskController> TaskController::sSingleton;
|
||||
uint64_t Task::sCurrentTaskSeqNo = 0;
|
||||
|
||||
bool TaskManager::
|
||||
UpdateCachesForCurrentIterationAndReportPriorityModifierChanged(
|
||||
const MutexAutoLock& aProofOfLock, IterationType aIterationType) {
|
||||
mCurrentSuspended = IsSuspended(aProofOfLock);
|
||||
|
||||
if (aIterationType == IterationType::EVENT_LOOP_TURN) {
|
||||
int32_t oldModifier = mCurrentPriorityModifier;
|
||||
mCurrentPriorityModifier =
|
||||
GetPriorityModifierForEventLoopTurn(aProofOfLock);
|
||||
|
||||
if (mCurrentPriorityModifier != oldModifier) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Task* Task::GetHighestPriorityDependency() {
|
||||
Task* currentTask = this;
|
||||
|
||||
while (!currentTask->mDependencies.empty()) {
|
||||
auto iter = currentTask->mDependencies.begin();
|
||||
|
||||
while (iter != currentTask->mDependencies.end()) {
|
||||
if ((*iter)->mCompleted) {
|
||||
auto oldIter = iter;
|
||||
iter++;
|
||||
// Completed tasks are removed here to prevent needlessly keeping them
|
||||
// alive or iterating over them in the future.
|
||||
currentTask->mDependencies.erase(oldIter);
|
||||
continue;
|
||||
}
|
||||
|
||||
currentTask = iter->get();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return currentTask == this ? nullptr : currentTask;
|
||||
}
|
||||
|
||||
TaskController* TaskController::Get() {
|
||||
MOZ_ASSERT(sSingleton.get());
|
||||
return sSingleton.get();
|
||||
}
|
||||
|
||||
bool TaskController::Initialize() {
|
||||
MOZ_ASSERT(!sSingleton);
|
||||
sSingleton = std::make_unique<TaskController>();
|
||||
return sSingleton->InitializeInternal();
|
||||
}
|
||||
|
||||
bool TaskController::InitializeInternal() {
|
||||
mMTProcessingRunnable = NS_NewRunnableFunction(
|
||||
"TaskController::ExecutePendingMTTasks()",
|
||||
[]() { TaskController::Get()->ProcessPendingMTTask(); });
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void TaskController::SetPerformanceCounterState(
|
||||
PerformanceCounterState* aPerformanceCounterState) {
|
||||
mPerformanceCounterState = aPerformanceCounterState;
|
||||
}
|
||||
|
||||
/* static */
|
||||
void TaskController::Shutdown() {
|
||||
if (sSingleton) {
|
||||
sSingleton->ShutdownInternal();
|
||||
}
|
||||
MOZ_ASSERT(!sSingleton);
|
||||
}
|
||||
|
||||
void TaskController::ShutdownInternal() { sSingleton = nullptr; }
|
||||
|
||||
void TaskController::AddTask(already_AddRefed<Task>&& aTask) {
|
||||
MutexAutoLock lock(mGraphMutex);
|
||||
|
||||
RefPtr<Task> task(aTask);
|
||||
|
||||
if (TaskManager* manager = task->GetManager()) {
|
||||
if (manager->mTaskCount == 0) {
|
||||
mTaskManagers.insert(manager);
|
||||
}
|
||||
manager->DidQueueTask();
|
||||
|
||||
// Set this here since if this manager's priority modifier doesn't change
|
||||
// we will not reprioritize when iterating over the queue.
|
||||
task->mPriorityModifier = manager->mCurrentPriorityModifier;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
task->mIsInGraph = true;
|
||||
|
||||
for (const RefPtr<Task>& otherTask : task->mDependencies) {
|
||||
MOZ_ASSERT(!otherTask->mTaskManager ||
|
||||
otherTask->mTaskManager == task->mTaskManager);
|
||||
}
|
||||
#endif
|
||||
|
||||
auto insertion = mMainThreadTasks.insert(std::move(task));
|
||||
MOZ_ASSERT(insertion.second);
|
||||
(*insertion.first)->mIterator = insertion.first;
|
||||
|
||||
MaybeInterruptTask(*insertion.first);
|
||||
}
|
||||
|
||||
void TaskController::WaitForTaskOrMessage() {
|
||||
MutexAutoLock lock(mGraphMutex);
|
||||
while (!mMayHaveMainThreadTask) {
|
||||
mMainThreadCV.Wait();
|
||||
}
|
||||
}
|
||||
|
||||
void TaskController::ExecuteNextTaskOnlyMainThread() {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MutexAutoLock lock(mGraphMutex);
|
||||
ExecuteNextTaskOnlyMainThreadInternal(lock);
|
||||
}
|
||||
|
||||
void TaskController::ProcessPendingMTTask() {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MutexAutoLock lock(mGraphMutex);
|
||||
mHasScheduledMTRunnable = false;
|
||||
|
||||
ExecuteNextTaskOnlyMainThreadInternal(lock);
|
||||
|
||||
if (mMayHaveMainThreadTask) {
|
||||
EnsureMainThreadTasksScheduled();
|
||||
}
|
||||
}
|
||||
|
||||
void TaskController::ReprioritizeTask(Task* aTask, uint32_t aPriority) {
|
||||
MutexAutoLock lock(mGraphMutex);
|
||||
std::set<RefPtr<Task>, Task::PriorityCompare>* queue = &mMainThreadTasks;
|
||||
|
||||
MOZ_ASSERT(aTask->mIterator != queue->end());
|
||||
queue->erase(aTask->mIterator);
|
||||
|
||||
aTask->mPriority = aPriority;
|
||||
|
||||
auto insertion = queue->insert(aTask);
|
||||
MOZ_ASSERT(insertion.second);
|
||||
aTask->mIterator = insertion.first;
|
||||
|
||||
MaybeInterruptTask(aTask);
|
||||
}
|
||||
|
||||
// Code supporting runnable compatibility.
|
||||
// Task that wraps a runnable.
|
||||
class RunnableTask : public Task {
|
||||
public:
|
||||
RunnableTask(already_AddRefed<nsIRunnable>&& aRunnable, int32_t aPriority,
|
||||
bool aMainThread = true)
|
||||
: Task(aMainThread, aPriority), mRunnable(aRunnable) {}
|
||||
|
||||
virtual bool Run() override {
|
||||
mRunnable->Run();
|
||||
mRunnable = nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
void SetIdleDeadline(TimeStamp aDeadline) override {
|
||||
nsCOMPtr<nsIIdleRunnable> idleRunnable = do_QueryInterface(mRunnable);
|
||||
if (idleRunnable) {
|
||||
idleRunnable->SetDeadline(aDeadline);
|
||||
}
|
||||
}
|
||||
|
||||
PerformanceCounter* GetPerformanceCounter() const override {
|
||||
return nsThread::GetPerformanceCounterBase(mRunnable);
|
||||
}
|
||||
|
||||
private:
|
||||
RefPtr<nsIRunnable> mRunnable;
|
||||
};
|
||||
|
||||
void TaskController::DispatchRunnable(already_AddRefed<nsIRunnable>&& aRunnable,
|
||||
uint32_t aPriority,
|
||||
TaskManager* aManager) {
|
||||
RefPtr<RunnableTask> task = new RunnableTask(std::move(aRunnable), aPriority);
|
||||
|
||||
task->SetManager(aManager);
|
||||
TaskController::Get()->AddTask(task.forget());
|
||||
}
|
||||
|
||||
nsIRunnable* TaskController::GetRunnableForMTTask(bool aReallyWait) {
|
||||
MutexAutoLock lock(mGraphMutex);
|
||||
|
||||
while (mMainThreadTasks.empty()) {
|
||||
if (!aReallyWait) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AUTO_PROFILER_LABEL("TaskController::GetRunnableForMTTask::Wait", IDLE);
|
||||
mMainThreadCV.Wait();
|
||||
}
|
||||
|
||||
return mMTProcessingRunnable;
|
||||
}
|
||||
|
||||
bool TaskController::HasMainThreadPendingTasks() {
|
||||
auto resetIdleState = MakeScopeExit([&idleManager = mIdleTaskManager] {
|
||||
if (idleManager) {
|
||||
idleManager->State().ClearCachedIdleDeadline();
|
||||
}
|
||||
});
|
||||
|
||||
for (bool considerIdle : {false, true}) {
|
||||
if (considerIdle && !mIdleTaskManager) {
|
||||
continue;
|
||||
}
|
||||
|
||||
MutexAutoLock lock(mGraphMutex);
|
||||
|
||||
if (considerIdle) {
|
||||
mIdleTaskManager->State().ForgetPendingTaskGuarantee();
|
||||
// Temporarily unlock so we can peek our idle deadline.
|
||||
// XXX We could do this _before_ we take the lock if the API would let us.
|
||||
// We do want to do this before looking at mMainThreadTasks, in case
|
||||
// someone adds one while we're unlocked.
|
||||
{
|
||||
MutexAutoUnlock unlock(mGraphMutex);
|
||||
mIdleTaskManager->State().CachePeekedIdleDeadline(unlock);
|
||||
}
|
||||
}
|
||||
|
||||
// Return early if there's no tasks at all.
|
||||
if (mMainThreadTasks.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We can cheaply count how many tasks are suspended.
|
||||
uint64_t totalSuspended = 0;
|
||||
for (TaskManager* manager : mTaskManagers) {
|
||||
DebugOnly<bool> modifierChanged =
|
||||
manager
|
||||
->UpdateCachesForCurrentIterationAndReportPriorityModifierChanged(
|
||||
lock, TaskManager::IterationType::NOT_EVENT_LOOP_TURN);
|
||||
MOZ_ASSERT(!modifierChanged);
|
||||
|
||||
// The idle manager should be suspended unless we're doing the idle pass.
|
||||
MOZ_ASSERT(manager != mIdleTaskManager || manager->mCurrentSuspended ||
|
||||
considerIdle,
|
||||
"Why are idle tasks not suspended here?");
|
||||
|
||||
if (manager->mCurrentSuspended) {
|
||||
// XXX - If managers manage off-main-thread tasks this breaks! This
|
||||
// scenario is explicitly not supported.
|
||||
//
|
||||
// This is only incremented inside the lock -or- decremented on the main
|
||||
// thread so this is safe.
|
||||
totalSuspended += manager->mTaskCount;
|
||||
}
|
||||
}
|
||||
|
||||
// Thi would break down if we have a non-suspended task depending on a
|
||||
// suspended task. This is why for the moment we do not allow tasks
|
||||
// to be dependent on tasks managed by another taskmanager.
|
||||
if (mMainThreadTasks.size() > totalSuspended) {
|
||||
// If mIdleTaskManager->mTaskCount is 0, we never updated the suspended
|
||||
// state of mIdleTaskManager above, hence shouldn't even check it here.
|
||||
// But in that case idle tasks are not contributing to our suspended task
|
||||
// count anyway.
|
||||
if (mIdleTaskManager && mIdleTaskManager->mTaskCount &&
|
||||
!mIdleTaskManager->mCurrentSuspended) {
|
||||
MOZ_ASSERT(considerIdle, "Why is mIdleTaskManager not suspended?");
|
||||
// Check whether the idle tasks were really needed to make our "we have
|
||||
// an unsuspended task" decision. If they were, we need to force-enable
|
||||
// idle tasks until we run our next task.
|
||||
if (mMainThreadTasks.size() - mIdleTaskManager->mTaskCount <=
|
||||
totalSuspended) {
|
||||
mIdleTaskManager->State().EnforcePendingTaskGuarantee();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void TaskController::ExecuteNextTaskOnlyMainThreadInternal(
|
||||
const MutexAutoLock& aProofOfLock) {
|
||||
// Block to make it easier to jump to our cleanup.
|
||||
do {
|
||||
bool taskRan = DoExecuteNextTaskOnlyMainThreadInternal(aProofOfLock);
|
||||
if (taskRan) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!mIdleTaskManager) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (mIdleTaskManager->mTaskCount) {
|
||||
// We have idle tasks that we may not have gotten above because
|
||||
// our idle state is not up to date. We need to update the idle state
|
||||
// and try again. We need to temporarily release the lock while we do
|
||||
// that.
|
||||
MutexAutoUnlock unlock(mGraphMutex);
|
||||
mIdleTaskManager->State().UpdateCachedIdleDeadline(unlock);
|
||||
} else {
|
||||
MutexAutoUnlock unlock(mGraphMutex);
|
||||
mIdleTaskManager->State().RanOutOfTasks(unlock);
|
||||
}
|
||||
|
||||
// When we unlocked, someone may have queued a new task on us. So try to
|
||||
// see whether we can run things again.
|
||||
Unused << DoExecuteNextTaskOnlyMainThreadInternal(aProofOfLock);
|
||||
} while (false);
|
||||
|
||||
if (mIdleTaskManager) {
|
||||
// The pending task guarantee is not needed anymore, since we just tried
|
||||
// running a task
|
||||
mIdleTaskManager->State().ForgetPendingTaskGuarantee();
|
||||
|
||||
if (mMainThreadTasks.empty()) {
|
||||
// XXX the IdlePeriodState API demands we have a MutexAutoUnlock for it.
|
||||
// Otherwise we could perhaps just do this after we exit the locked block,
|
||||
// by pushing the lock down into this method. Though it's not clear that
|
||||
// we could check mMainThreadTasks.size() once we unlock, and whether we
|
||||
// could maybe substitute mMayHaveMainThreadTask for that check.
|
||||
MutexAutoUnlock unlock(mGraphMutex);
|
||||
mIdleTaskManager->State().RanOutOfTasks(unlock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool TaskController::DoExecuteNextTaskOnlyMainThreadInternal(
|
||||
const MutexAutoLock& aProofOfLock) {
|
||||
uint32_t totalSuspended = 0;
|
||||
for (TaskManager* manager : mTaskManagers) {
|
||||
bool modifierChanged =
|
||||
manager
|
||||
->UpdateCachesForCurrentIterationAndReportPriorityModifierChanged(
|
||||
aProofOfLock, TaskManager::IterationType::EVENT_LOOP_TURN);
|
||||
if (modifierChanged) {
|
||||
ProcessUpdatedPriorityModifier(manager);
|
||||
}
|
||||
if (manager->mCurrentSuspended) {
|
||||
totalSuspended += manager->mTaskCount;
|
||||
}
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mMainThreadTasks.size() >= totalSuspended);
|
||||
|
||||
// This would break down if we have a non-suspended task depending on a
|
||||
// suspended task. This is why for the moment we do not allow tasks
|
||||
// to be dependent on tasks managed by another taskmanager.
|
||||
if (mMainThreadTasks.size() > totalSuspended) {
|
||||
for (auto iter = mMainThreadTasks.begin(); iter != mMainThreadTasks.end();
|
||||
iter++) {
|
||||
Task* task = iter->get();
|
||||
|
||||
if (task->mTaskManager && task->mTaskManager->mCurrentSuspended) {
|
||||
// Even though we may want to run some dependencies of this task, we
|
||||
// will run them at their own priority level and not the priority
|
||||
// level of their dependents.
|
||||
continue;
|
||||
}
|
||||
|
||||
task = GetFinalDependency(task);
|
||||
|
||||
if (!task->IsMainThreadOnly() || task->mInProgress ||
|
||||
(task->mTaskManager && task->mTaskManager->mCurrentSuspended)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
mCurrentTasksMT.push(task);
|
||||
mMainThreadTasks.erase(task->mIterator);
|
||||
task->mIterator = mMainThreadTasks.end();
|
||||
task->mInProgress = true;
|
||||
TaskManager* manager = task->GetManager();
|
||||
bool result = false;
|
||||
|
||||
{
|
||||
MutexAutoUnlock unlock(mGraphMutex);
|
||||
if (manager) {
|
||||
manager->WillRunTask();
|
||||
if (manager != mIdleTaskManager) {
|
||||
// Notify the idle period state that we're running a non-idle task.
|
||||
// This needs to happen while our mutex is not locked!
|
||||
mIdleTaskManager->State().FlagNotIdle();
|
||||
} else {
|
||||
TimeStamp idleDeadline =
|
||||
mIdleTaskManager->State().GetCachedIdleDeadline();
|
||||
MOZ_ASSERT(
|
||||
idleDeadline,
|
||||
"How can we not have a deadline if our manager is enabled?");
|
||||
task->SetIdleDeadline(idleDeadline);
|
||||
}
|
||||
}
|
||||
if (mIdleTaskManager) {
|
||||
// We found a task to run; we can clear the idle deadline on our idle
|
||||
// task manager. This _must_ be done before we actually run the task,
|
||||
// because running the task could reenter via spinning the event loop
|
||||
// and we want to make sure there's no cached idle deadline at that
|
||||
// point. But we have to make sure we do it after out SetIdleDeadline
|
||||
// call above, in the case when the task is actually an idle task.
|
||||
mIdleTaskManager->State().ClearCachedIdleDeadline();
|
||||
}
|
||||
|
||||
PerformanceCounterState::Snapshot snapshot =
|
||||
mPerformanceCounterState->RunnableWillRun(
|
||||
task->GetPerformanceCounter(), TimeStamp::Now(),
|
||||
manager == mIdleTaskManager);
|
||||
|
||||
result = task->Run();
|
||||
// Task itself should keep manager alive.
|
||||
if (manager) {
|
||||
manager->DidRunTask();
|
||||
}
|
||||
|
||||
mPerformanceCounterState->RunnableDidRun(std::move(snapshot));
|
||||
}
|
||||
|
||||
// Task itself should keep manager alive.
|
||||
if (manager && result && manager->mTaskCount == 0) {
|
||||
mTaskManagers.erase(manager);
|
||||
}
|
||||
|
||||
task->mInProgress = false;
|
||||
|
||||
if (!result) {
|
||||
// Presumably this task was interrupted, leave its dependencies
|
||||
// unresolved and reinsert into the queue.
|
||||
auto insertion =
|
||||
mMainThreadTasks.insert(std::move(mCurrentTasksMT.top()));
|
||||
MOZ_ASSERT(insertion.second);
|
||||
task->mIterator = insertion.first;
|
||||
manager->WillRunTask();
|
||||
} else {
|
||||
task->mCompleted = true;
|
||||
#ifdef DEBUG
|
||||
task->mIsInGraph = false;
|
||||
#endif
|
||||
// Clear dependencies to release references.
|
||||
task->mDependencies.clear();
|
||||
}
|
||||
|
||||
mCurrentTasksMT.pop();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
mMayHaveMainThreadTask = false;
|
||||
if (mIdleTaskManager) {
|
||||
// We did not find a task to run. We still need to clear the cached idle
|
||||
// deadline on our idle state, because that deadline was only relevant to
|
||||
// the execution of this function. Had we found a task, we would have
|
||||
// cleared the deadline before running that task.
|
||||
mIdleTaskManager->State().ClearCachedIdleDeadline();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Task* TaskController::GetFinalDependency(Task* aTask) {
|
||||
Task* nextTask;
|
||||
|
||||
while ((nextTask = aTask->GetHighestPriorityDependency())) {
|
||||
aTask = nextTask;
|
||||
}
|
||||
|
||||
return aTask;
|
||||
}
|
||||
|
||||
void TaskController::MaybeInterruptTask(Task* aTask) {
|
||||
mGraphMutex.AssertCurrentThreadOwns();
|
||||
|
||||
if (!aTask) {
|
||||
return;
|
||||
}
|
||||
|
||||
// This optimization prevents many slow lookups in long chains of similar
|
||||
// priority.
|
||||
if (!aTask->mDependencies.empty()) {
|
||||
Task* firstDependency = aTask->mDependencies.begin()->get();
|
||||
if (aTask->GetPriority() <= firstDependency->GetPriority() &&
|
||||
!firstDependency->mCompleted &&
|
||||
aTask->IsMainThreadOnly() == firstDependency->IsMainThreadOnly()) {
|
||||
// This task has the same or a higher priority as one of its dependencies,
|
||||
// never any need to interrupt.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Task* finalDependency = GetFinalDependency(aTask);
|
||||
|
||||
if (finalDependency->mInProgress) {
|
||||
// No need to wake anything, we can't schedule this task right now anyway.
|
||||
return;
|
||||
}
|
||||
|
||||
EnsureMainThreadTasksScheduled();
|
||||
|
||||
mMayHaveMainThreadTask = true;
|
||||
|
||||
if (mCurrentTasksMT.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We could go through the steps above here and interrupt an off main
|
||||
// thread task in case it has a lower priority.
|
||||
if (!finalDependency->IsMainThreadOnly()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mCurrentTasksMT.top()->GetPriority() < aTask->GetPriority()) {
|
||||
mCurrentTasksMT.top()->RequestInterrupt(aTask->GetPriority());
|
||||
}
|
||||
}
|
||||
|
||||
Task* TaskController::GetHighestPriorityMTTask() {
|
||||
mGraphMutex.AssertCurrentThreadOwns();
|
||||
|
||||
if (!mMainThreadTasks.empty()) {
|
||||
return mMainThreadTasks.begin()->get();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void TaskController::EnsureMainThreadTasksScheduled() {
|
||||
if (mObserver) {
|
||||
mObserver->OnDispatchedEvent();
|
||||
}
|
||||
if (mExternalCondVar) {
|
||||
mExternalCondVar->Notify();
|
||||
}
|
||||
mMainThreadCV.Notify();
|
||||
}
|
||||
|
||||
void TaskController::ProcessUpdatedPriorityModifier(TaskManager* aManager) {
|
||||
mGraphMutex.AssertCurrentThreadOwns();
|
||||
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
int32_t modifier = aManager->mCurrentPriorityModifier;
|
||||
|
||||
std::vector<RefPtr<Task>> storedTasks;
|
||||
// Find all relevant tasks.
|
||||
for (auto iter = mMainThreadTasks.begin(); iter != mMainThreadTasks.end();) {
|
||||
if ((*iter)->mTaskManager == aManager) {
|
||||
storedTasks.push_back(*iter);
|
||||
iter = mMainThreadTasks.erase(iter);
|
||||
} else {
|
||||
iter++;
|
||||
}
|
||||
}
|
||||
|
||||
// Reinsert found tasks with their new priorities.
|
||||
for (RefPtr<Task>& ref : storedTasks) {
|
||||
// Kept alive at first by the vector and then by mMainThreadTasks.
|
||||
Task* task = ref;
|
||||
task->mPriorityModifier = modifier;
|
||||
auto insertion = mMainThreadTasks.insert(std::move(ref));
|
||||
MOZ_ASSERT(insertion.second);
|
||||
task->mIterator = insertion.first;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
|
@ -1,360 +0,0 @@
|
|||
/* -*- 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_TaskController_h
|
||||
#define mozilla_TaskController_h
|
||||
|
||||
#include "mozilla/CondVar.h"
|
||||
#include "mozilla/IdlePeriodState.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "mozilla/StaticMutex.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "mozilla/EventQueue.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
#include "nsIEventTarget.h"
|
||||
|
||||
#include <thread>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <list>
|
||||
#include <stack>
|
||||
|
||||
class nsIRunnable;
|
||||
class nsIThreadObserver;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class Task;
|
||||
class TaskController;
|
||||
class PerformanceCounter;
|
||||
class PerformanceCounterState;
|
||||
|
||||
const uint32_t kDefaultPriorityValue = uint32_t(EventQueuePriority::Normal);
|
||||
|
||||
// This file contains the core classes to access the Gecko scheduler. The
|
||||
// scheduler forms a graph of prioritize tasks, and is responsible for ensuring
|
||||
// the execution of tasks or their dependencies in order of inherited priority.
|
||||
//
|
||||
// The core class is the 'Task' class. The task class describes a single unit of
|
||||
// work. Users scheduling work implement this class and are required to
|
||||
// reimplement the 'Run' function in order to do work.
|
||||
//
|
||||
// The TaskManager class is reimplemented by users that require
|
||||
// the ability to reprioritize or suspend tasks.
|
||||
//
|
||||
// The TaskController is responsible for scheduling the work itself. The AddTask
|
||||
// function is used to schedule work. The ReprioritizeTask function may be used
|
||||
// to change the priority of a task already in the task graph, without
|
||||
// unscheduling it.
|
||||
|
||||
// The TaskManager is the baseclass used to atomically manage a large set of
|
||||
// tasks. API users reimplementing TaskManager may reimplement a number of
|
||||
// functions that they may use to indicate to the scheduler changes in the state
|
||||
// for any tasks they manage. They may be used to reprioritize or suspend tasks
|
||||
// under their control, and will also be notified before and after tasks under
|
||||
// their control are executed. Their methods will only be called once per event
|
||||
// loop turn, however they may still incur some performance overhead. In
|
||||
// addition to this frequent reprioritizations may incur a significant
|
||||
// performance overhead and are discouraged. A TaskManager may currently only be
|
||||
// used to manage tasks that are bound to the Gecko Main Thread.
|
||||
class TaskManager {
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TaskManager)
|
||||
|
||||
TaskManager() : mTaskCount(0) {}
|
||||
|
||||
// Subclasses implementing task manager will have this function called to
|
||||
// determine whether their associated tasks are currently suspended. This
|
||||
// will only be called once per iteration of the task queue, this means that
|
||||
// suspension of tasks managed by a single TaskManager may be assumed to
|
||||
// occur atomically.
|
||||
virtual bool IsSuspended(const MutexAutoLock& aProofOfLock) { return false; }
|
||||
|
||||
// Subclasses may implement this in order to supply a priority adjustment
|
||||
// to their managed tasks. This is called once per iteration of the task
|
||||
// queue, and may be assumed to occur atomically for all managed tasks.
|
||||
virtual int32_t GetPriorityModifierForEventLoopTurn(
|
||||
const MutexAutoLock& aProofOfLock) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void DidQueueTask() { ++mTaskCount; }
|
||||
// This is called when a managed task is about to be executed by the
|
||||
// scheduler. Anyone reimplementing this should ensure to call the parent or
|
||||
// decrement mTaskCount.
|
||||
virtual void WillRunTask() { --mTaskCount; }
|
||||
// This is called when a managed task has finished being executed by the
|
||||
// scheduler.
|
||||
virtual void DidRunTask() {}
|
||||
uint32_t PendingTaskCount() { return mTaskCount; }
|
||||
|
||||
protected:
|
||||
virtual ~TaskManager() {}
|
||||
|
||||
private:
|
||||
friend class TaskController;
|
||||
|
||||
enum class IterationType { NOT_EVENT_LOOP_TURN, EVENT_LOOP_TURN };
|
||||
bool UpdateCachesForCurrentIterationAndReportPriorityModifierChanged(
|
||||
const MutexAutoLock& aProofOfLock, IterationType aIterationType);
|
||||
|
||||
bool mCurrentSuspended = false;
|
||||
int32_t mCurrentPriorityModifier = 0;
|
||||
|
||||
Atomic<uint32_t> mTaskCount;
|
||||
};
|
||||
|
||||
// A Task is the the base class for any unit of work that may be scheduled.
|
||||
// Subclasses may specify their priority and whether they should be bound to
|
||||
// the Gecko Main thread. When not bound to the main thread tasks may be
|
||||
// executed on any available thread (including the main thread), but they may
|
||||
// also be executed in parallel to any other task they do not have a dependency
|
||||
// relationship with. Tasks will be run in order of object creation.
|
||||
class Task {
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Task)
|
||||
|
||||
bool IsMainThreadOnly() { return mMainThreadOnly; }
|
||||
|
||||
// This returns the current task priority with its modifier applied.
|
||||
uint32_t GetPriority() { return mPriority + mPriorityModifier; }
|
||||
uint64_t GetSeqNo() { return mSeqNo; }
|
||||
|
||||
// Callee needs to assume this may be called on any thread.
|
||||
// aInterruptPriority passes the priority of the higher priority task that
|
||||
// is ready to be executed. The task may safely ignore this function, or
|
||||
// interrupt any work being done. It may return 'false' from its run function
|
||||
// in order to be run automatically in the future, or true if it will
|
||||
// reschedule incomplete work manually.
|
||||
virtual void RequestInterrupt(uint32_t aInterruptPriority) {}
|
||||
|
||||
// At the moment this -must- be called before the task is added to the
|
||||
// controller. Calling this after tasks have been added to the controller
|
||||
// results in undefined behavior!
|
||||
// At submission, tasks must depend only on tasks managed by the same, or
|
||||
// no idle manager.
|
||||
void AddDependency(Task* aTask) {
|
||||
MOZ_ASSERT(aTask);
|
||||
MOZ_ASSERT(!mIsInGraph);
|
||||
mDependencies.insert(aTask);
|
||||
}
|
||||
|
||||
// This sets the TaskManager for the current task. Calling this after the
|
||||
// task has been added to the TaskController results in undefined behavior.
|
||||
void SetManager(TaskManager* aManager) {
|
||||
MOZ_ASSERT(mMainThreadOnly);
|
||||
MOZ_ASSERT(!mIsInGraph);
|
||||
mTaskManager = aManager;
|
||||
}
|
||||
TaskManager* GetManager() { return mTaskManager; }
|
||||
|
||||
struct PriorityCompare {
|
||||
bool operator()(const RefPtr<Task>& aTaskA,
|
||||
const RefPtr<Task>& aTaskB) const {
|
||||
uint32_t prioA = aTaskA->GetPriority();
|
||||
uint32_t prioB = aTaskB->GetPriority();
|
||||
return (prioA > prioB) ||
|
||||
(prioA == prioB && (aTaskA->GetSeqNo() < aTaskB->GetSeqNo()));
|
||||
}
|
||||
};
|
||||
|
||||
// Tell the task about its idle deadline. Will only be called for
|
||||
// tasks managed by an IdleTaskManager, right before the task runs.
|
||||
virtual void SetIdleDeadline(TimeStamp aDeadline) {}
|
||||
|
||||
virtual PerformanceCounter* GetPerformanceCounter() const { return nullptr; }
|
||||
|
||||
protected:
|
||||
Task(bool aMainThreadOnly, uint32_t aPriority = kDefaultPriorityValue)
|
||||
: mMainThreadOnly(aMainThreadOnly),
|
||||
mSeqNo(sCurrentTaskSeqNo++),
|
||||
mPriority(aPriority) {}
|
||||
|
||||
virtual ~Task() {}
|
||||
|
||||
friend class TaskController;
|
||||
|
||||
// When this returns false, the task is considered incomplete and will be
|
||||
// rescheduled at the current 'mPriority' level.
|
||||
virtual bool Run() = 0;
|
||||
|
||||
private:
|
||||
Task* GetHighestPriorityDependency();
|
||||
|
||||
// Iterator pointing to this task's position in
|
||||
// mThreadableTasks/mMainThreadTasks if, and only if this task is currently
|
||||
// scheduled to be executed. This allows fast access to the task's position
|
||||
// in the set, allowing for fast removal.
|
||||
// This is safe, and remains valid unless the task is removed from the set.
|
||||
// See also iterator invalidation in:
|
||||
// https://en.cppreference.com/w/cpp/container
|
||||
//
|
||||
// Or the spec:
|
||||
// "All Associative Containers: The insert and emplace members shall not
|
||||
// affect the validity of iterators and references to the container
|
||||
// [26.2.6/9]" "All Associative Containers: The erase members shall invalidate
|
||||
// only iterators and references to the erased elements [26.2.6/9]"
|
||||
std::set<RefPtr<Task>, PriorityCompare>::iterator mIterator;
|
||||
std::set<RefPtr<Task>, PriorityCompare> mDependencies;
|
||||
|
||||
RefPtr<TaskManager> mTaskManager;
|
||||
|
||||
// Access to these variables is protected by the GraphMutex.
|
||||
bool mMainThreadOnly;
|
||||
bool mCompleted = false;
|
||||
bool mInProgress = false;
|
||||
#ifdef DEBUG
|
||||
bool mIsInGraph = false;
|
||||
#endif
|
||||
|
||||
static uint64_t sCurrentTaskSeqNo;
|
||||
int64_t mSeqNo;
|
||||
uint32_t mPriority;
|
||||
// Modifier currently being applied to this task by its taskmanager.
|
||||
int32_t mPriorityModifier = 0;
|
||||
};
|
||||
|
||||
// A task manager implementation for priority levels that should only
|
||||
// run during idle periods.
|
||||
class IdleTaskManager : public TaskManager {
|
||||
public:
|
||||
explicit IdleTaskManager(already_AddRefed<nsIIdlePeriod>&& aIdlePeriod)
|
||||
: mIdlePeriodState(std::move(aIdlePeriod)) {}
|
||||
|
||||
IdlePeriodState& State() { return mIdlePeriodState; }
|
||||
|
||||
bool IsSuspended(const MutexAutoLock& aProofOfLock) override {
|
||||
TimeStamp idleDeadline = State().GetCachedIdleDeadline();
|
||||
return !idleDeadline;
|
||||
}
|
||||
|
||||
private:
|
||||
// Tracking of our idle state of various sorts.
|
||||
IdlePeriodState mIdlePeriodState;
|
||||
};
|
||||
|
||||
// The TaskController is the core class of the scheduler. It is used to
|
||||
// schedule tasks to be executed, as well as to reprioritize tasks that have
|
||||
// already been scheduled. The core functions to do this are AddTask and
|
||||
// ReprioritizeTask.
|
||||
class TaskController {
|
||||
public:
|
||||
TaskController()
|
||||
: mGraphMutex("TaskController::mGraphMutex"),
|
||||
mMainThreadCV(mGraphMutex, "TaskController::mMainThreadCV") {}
|
||||
|
||||
static TaskController* Get();
|
||||
|
||||
static bool Initialize();
|
||||
|
||||
void SetThreadObserver(nsIThreadObserver* aObserver) {
|
||||
mObserver = aObserver;
|
||||
}
|
||||
void SetConditionVariable(CondVar* aExternalCondVar) {
|
||||
mExternalCondVar = aExternalCondVar;
|
||||
}
|
||||
|
||||
void SetIdleTaskManager(IdleTaskManager* aIdleTaskManager) {
|
||||
mIdleTaskManager = aIdleTaskManager;
|
||||
}
|
||||
|
||||
// Initialization and shutdown code.
|
||||
bool InitializeInternal();
|
||||
void SetPerformanceCounterState(
|
||||
PerformanceCounterState* aPerformanceCounterState);
|
||||
|
||||
static void Shutdown();
|
||||
|
||||
// This adds a task to the TaskController graph.
|
||||
// This may be called on any thread.
|
||||
void AddTask(already_AddRefed<Task>&& aTask);
|
||||
|
||||
// This wait function is the theoretical function you would need if our main
|
||||
// thread needs to also process OS messages or something along those lines.
|
||||
void WaitForTaskOrMessage();
|
||||
|
||||
// This gets the next (highest priority) task that is only allowed to execute
|
||||
// on the main thread.
|
||||
void ExecuteNextTaskOnlyMainThread();
|
||||
|
||||
// Process all pending main thread tasks.
|
||||
void ProcessPendingMTTask();
|
||||
|
||||
// This allows reprioritization of a task already in the task graph.
|
||||
// This may be called on any thread.
|
||||
void ReprioritizeTask(Task* aTask, uint32_t aPriority);
|
||||
|
||||
void DispatchRunnable(already_AddRefed<nsIRunnable>&& aRunnable,
|
||||
uint32_t aPriority, TaskManager* aManager = nullptr);
|
||||
|
||||
nsIRunnable* GetRunnableForMTTask(bool aReallyWait);
|
||||
|
||||
bool HasMainThreadPendingTasks();
|
||||
|
||||
private:
|
||||
// This gets the next (highest priority) task that is only allowed to execute
|
||||
// on the main thread, if any, and executes it.
|
||||
void ExecuteNextTaskOnlyMainThreadInternal(const MutexAutoLock& aProofOfLock);
|
||||
|
||||
// The guts of ExecuteNextTaskOnlyMainThreadInternal, which get idle handling
|
||||
// wrapped around them. Returns whether a task actually ran.
|
||||
bool DoExecuteNextTaskOnlyMainThreadInternal(
|
||||
const MutexAutoLock& aProofOfLock);
|
||||
|
||||
Task* GetFinalDependency(Task* aTask);
|
||||
void MaybeInterruptTask(Task* aTask);
|
||||
Task* GetHighestPriorityMTTask();
|
||||
|
||||
void EnsureMainThreadTasksScheduled();
|
||||
|
||||
void ProcessUpdatedPriorityModifier(TaskManager* aManager);
|
||||
|
||||
void ShutdownInternal();
|
||||
|
||||
static std::unique_ptr<TaskController> sSingleton;
|
||||
static StaticMutex sSingletonMutex;
|
||||
|
||||
// This protects access to the task graph.
|
||||
Mutex mGraphMutex;
|
||||
|
||||
CondVar mMainThreadCV;
|
||||
|
||||
// Variables below are protected by mGraphMutex.
|
||||
|
||||
std::stack<RefPtr<Task>> mCurrentTasksMT;
|
||||
|
||||
// A list of all tasks ordered by priority.
|
||||
std::set<RefPtr<Task>, Task::PriorityCompare> mMainThreadTasks;
|
||||
|
||||
// TaskManagers currently active.
|
||||
// We can use a raw pointer since tasks always hold on to their TaskManager.
|
||||
std::set<TaskManager*> mTaskManagers;
|
||||
|
||||
// This ensures we keep running the main thread if we processed a task there.
|
||||
bool mMayHaveMainThreadTask = true;
|
||||
|
||||
// Whether we have scheduled a runnable on the main thread event loop.
|
||||
// This is used for nsIRunnable compatibility.
|
||||
bool mHasScheduledMTRunnable = false;
|
||||
RefPtr<nsIRunnable> mMTProcessingRunnable;
|
||||
|
||||
// XXX - Thread observer to notify when a new event has been dispatched
|
||||
nsIThreadObserver* mObserver = nullptr;
|
||||
// XXX - External condvar to notify when we have received an event
|
||||
CondVar* mExternalCondVar = nullptr;
|
||||
// Idle task manager so we can properly do idle state stuff.
|
||||
RefPtr<IdleTaskManager> mIdleTaskManager;
|
||||
|
||||
// Our tracking of our performance counter and long task state,
|
||||
// shared with nsThread.
|
||||
PerformanceCounterState* mPerformanceCounterState = nullptr;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_TaskController_h
|
|
@ -13,7 +13,6 @@
|
|||
#include "nsThreadUtils.h"
|
||||
#include "PrioritizedEventQueue.h"
|
||||
#include "ThreadEventTarget.h"
|
||||
#include "mozilla/TaskController.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
|
@ -47,14 +46,10 @@ class ThreadEventQueue<InnerQueueT>::NestedSink : public ThreadTargetSink {
|
|||
};
|
||||
|
||||
template <class InnerQueueT>
|
||||
ThreadEventQueue<InnerQueueT>::ThreadEventQueue(UniquePtr<InnerQueueT> aQueue,
|
||||
bool aIsMainThread)
|
||||
ThreadEventQueue<InnerQueueT>::ThreadEventQueue(UniquePtr<InnerQueueT> aQueue)
|
||||
: mBaseQueue(std::move(aQueue)),
|
||||
mLock("ThreadEventQueue"),
|
||||
mEventsAvailable(mLock, "EventsAvail") {
|
||||
if (aIsMainThread) {
|
||||
TaskController::Get()->SetConditionVariable(&mEventsAvailable);
|
||||
}
|
||||
static_assert(std::is_base_of<AbstractEventQueue, InnerQueueT>::value,
|
||||
"InnerQueueT must be an AbstractEventQueue subclass");
|
||||
}
|
||||
|
@ -91,7 +86,7 @@ bool ThreadEventQueue<InnerQueueT>::PutEventInternal(
|
|||
if (prio == nsIRunnablePriority::PRIORITY_HIGH) {
|
||||
aPriority = EventQueuePriority::High;
|
||||
} else if (prio == nsIRunnablePriority::PRIORITY_INPUT_HIGH) {
|
||||
aPriority = EventQueuePriority::InputHigh;
|
||||
aPriority = EventQueuePriority::Input;
|
||||
} else if (prio == nsIRunnablePriority::PRIORITY_MEDIUMHIGH) {
|
||||
aPriority = EventQueuePriority::MediumHigh;
|
||||
} else if (prio == nsIRunnablePriority::PRIORITY_DEFERRED_TIMERS) {
|
||||
|
@ -377,9 +372,6 @@ template <class InnerQueueT>
|
|||
void ThreadEventQueue<InnerQueueT>::SetObserver(nsIThreadObserver* aObserver) {
|
||||
MutexAutoLock lock(mLock);
|
||||
mObserver = aObserver;
|
||||
if (NS_IsMainThread()) {
|
||||
TaskController::Get()->SetThreadObserver(aObserver);
|
||||
}
|
||||
}
|
||||
|
||||
namespace mozilla {
|
||||
|
|
|
@ -31,8 +31,7 @@ class ThreadEventTarget;
|
|||
template <class InnerQueueT>
|
||||
class ThreadEventQueue final : public SynchronizedEventQueue {
|
||||
public:
|
||||
explicit ThreadEventQueue(UniquePtr<InnerQueueT> aQueue,
|
||||
bool aIsMainThread = false);
|
||||
explicit ThreadEventQueue(UniquePtr<InnerQueueT> aQueue);
|
||||
|
||||
bool PutEvent(already_AddRefed<nsIRunnable>&& aEvent,
|
||||
EventQueuePriority aPriority) final;
|
||||
|
|
|
@ -69,7 +69,6 @@ EXPORTS.mozilla += [
|
|||
'SynchronizedEventQueue.h',
|
||||
'SyncRunnable.h',
|
||||
'TaskCategory.h',
|
||||
'TaskController.h',
|
||||
'TaskDispatcher.h',
|
||||
'TaskQueue.h',
|
||||
'ThreadBound.h',
|
||||
|
@ -107,7 +106,6 @@ UNIFIED_SOURCES += [
|
|||
'SchedulerGroup.cpp',
|
||||
'SharedThreadPool.cpp',
|
||||
'SynchronizedEventQueue.cpp',
|
||||
'TaskController.cpp',
|
||||
'TaskQueue.cpp',
|
||||
'ThreadEventQueue.cpp',
|
||||
'ThreadEventTarget.cpp',
|
||||
|
|
|
@ -31,10 +31,9 @@
|
|||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/SchedulerGroup.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "mozilla/StaticPrefs_threads.h"
|
||||
#include "mozilla/TaskController.h"
|
||||
#include "nsXPCOMPrivate.h"
|
||||
#include "mozilla/ChaosMode.h"
|
||||
#include "mozilla/SchedulerGroup.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "mozilla/Unused.h"
|
||||
|
@ -596,10 +595,6 @@ nsThread::nsThread(NotNull<SynchronizedEventQueue*> aQueue,
|
|||
mLastWakeupCheckTime(TimeStamp::Now()),
|
||||
#endif
|
||||
mPerformanceCounterState(mNestedEventLoopDepth, mIsMainThread) {
|
||||
if (mIsMainThread) {
|
||||
mozilla::TaskController::Get()->SetPerformanceCounterState(
|
||||
&mPerformanceCounterState);
|
||||
}
|
||||
}
|
||||
|
||||
nsThread::nsThread()
|
||||
|
@ -907,11 +902,7 @@ nsThread::HasPendingEvents(bool* aResult) {
|
|||
return NS_ERROR_NOT_SAME_THREAD;
|
||||
}
|
||||
|
||||
if (NS_IsMainThread() && UseTaskController() && !mIsInLocalExecutionMode) {
|
||||
*aResult = TaskController::Get()->HasMainThreadPendingTasks();
|
||||
} else {
|
||||
*aResult = mEvents->HasPendingEvent();
|
||||
}
|
||||
*aResult = mEvents->HasPendingEvent();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -1007,7 +998,7 @@ static bool GetLabeledRunnableName(nsIRunnable* aEvent, nsACString& aName,
|
|||
aName.AssignLiteral("anonymous runnable");
|
||||
}
|
||||
|
||||
if (!labeled && aPriority > EventQueuePriority::InputHigh) {
|
||||
if (!labeled && aPriority > EventQueuePriority::Input) {
|
||||
aName.AppendLiteral("(unlabeled)");
|
||||
}
|
||||
|
||||
|
@ -1017,12 +1008,6 @@ static bool GetLabeledRunnableName(nsIRunnable* aEvent, nsACString& aName,
|
|||
|
||||
mozilla::PerformanceCounter* nsThread::GetPerformanceCounter(
|
||||
nsIRunnable* aEvent) const {
|
||||
return GetPerformanceCounterBase(aEvent);
|
||||
}
|
||||
|
||||
// static
|
||||
mozilla::PerformanceCounter* nsThread::GetPerformanceCounterBase(
|
||||
nsIRunnable* aEvent) {
|
||||
RefPtr<SchedulerGroup::Runnable> docRunnable = do_QueryObject(aEvent);
|
||||
if (docRunnable) {
|
||||
mozilla::dom::DocGroup* docGroup = docRunnable->DocGroup();
|
||||
|
@ -1071,13 +1056,14 @@ nsThread::ProcessNextEvent(bool aMayWait, bool* aResult) {
|
|||
}
|
||||
|
||||
// The toplevel event loop normally blocks waiting for the next event, but
|
||||
// if we're trying to shut this thread down, we must exit the event loop
|
||||
// when the event queue is empty. This only applys to the toplevel event
|
||||
// loop! Nested event loops (e.g. during sync dispatch) are waiting for
|
||||
// some state change and must be able to block even if something has
|
||||
// requested shutdown of the thread. Otherwise we'll just busywait as we
|
||||
// endlessly look for an event, fail to find one, and repeat the nested
|
||||
// event loop since its state change hasn't happened yet.
|
||||
// if we're trying to shut this thread down, we must exit the event loop when
|
||||
// the event queue is empty.
|
||||
// This only applys to the toplevel event loop! Nested event loops (e.g.
|
||||
// during sync dispatch) are waiting for some state change and must be able
|
||||
// to block even if something has requested shutdown of the thread. Otherwise
|
||||
// we'll just busywait as we endlessly look for an event, fail to find one,
|
||||
// and repeat the nested event loop since its state change hasn't happened
|
||||
// yet.
|
||||
bool reallyWait = aMayWait && (mNestedEventLoopDepth > 0 || !ShuttingDown());
|
||||
|
||||
if (mIsInLocalExecutionMode) {
|
||||
|
@ -1106,8 +1092,8 @@ nsThread::ProcessNextEvent(bool aMayWait, bool* aResult) {
|
|||
|
||||
++mNestedEventLoopDepth;
|
||||
|
||||
// We only want to create an AutoNoJSAPI on threads that actually do DOM
|
||||
// stuff (including workers). Those are exactly the threads that have an
|
||||
// We only want to create an AutoNoJSAPI on threads that actually do DOM stuff
|
||||
// (including workers). Those are exactly the threads that have an
|
||||
// mScriptObserver.
|
||||
bool callScriptObserver = !!mScriptObserver;
|
||||
if (callScriptObserver) {
|
||||
|
@ -1139,17 +1125,9 @@ nsThread::ProcessNextEvent(bool aMayWait, bool* aResult) {
|
|||
// Scope for |event| to make sure that its destructor fires while
|
||||
// mNestedEventLoopDepth has been incremented, since that destructor can
|
||||
// also do work.
|
||||
EventQueuePriority priority = EventQueuePriority::Normal;
|
||||
nsCOMPtr<nsIRunnable> event;
|
||||
bool usingTaskController = mIsMainThread && UseTaskController();
|
||||
if (usingTaskController) {
|
||||
// XXX should set priority? Maybe we can just grab the "last task"
|
||||
// priority from the TaskController where we currently grab the
|
||||
// "ranIdleTask" state?
|
||||
event = TaskController::Get()->GetRunnableForMTTask(reallyWait);
|
||||
} else {
|
||||
event = mEvents->GetEvent(reallyWait, &priority, &mLastEventDelay);
|
||||
}
|
||||
EventQueuePriority priority;
|
||||
nsCOMPtr<nsIRunnable> event =
|
||||
mEvents->GetEvent(reallyWait, &priority, &mLastEventDelay);
|
||||
|
||||
*aResult = (event.get() != nullptr);
|
||||
|
||||
|
@ -1193,8 +1171,8 @@ nsThread::ProcessNextEvent(bool aMayWait, bool* aResult) {
|
|||
}
|
||||
|
||||
#ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY
|
||||
// If we're on the main thread, we want to record our current
|
||||
// runnable's name in a static so that BHR can record it.
|
||||
// If we're on the main thread, we want to record our current runnable's
|
||||
// name in a static so that BHR can record it.
|
||||
Array<char, kRunnableNameBufSize> restoreRunnableName;
|
||||
restoreRunnableName[0] = '\0';
|
||||
auto clear = MakeScopeExit([&] {
|
||||
|
@ -1218,27 +1196,23 @@ nsThread::ProcessNextEvent(bool aMayWait, bool* aResult) {
|
|||
sMainThreadRunnableName[length] = '\0';
|
||||
}
|
||||
#endif
|
||||
// Note, with TaskController InputTaskManager handles these updates.
|
||||
Maybe<AutoTimeDurationHelper> timeDurationHelper;
|
||||
if (priority == EventQueuePriority::InputHigh) {
|
||||
if (priority == EventQueuePriority::Input) {
|
||||
timeDurationHelper.emplace();
|
||||
}
|
||||
|
||||
Maybe<PerformanceCounterState::Snapshot> snapshot;
|
||||
if (!usingTaskController) {
|
||||
snapshot.emplace(mPerformanceCounterState.RunnableWillRun(
|
||||
GetPerformanceCounter(event), now,
|
||||
priority == EventQueuePriority::Idle));
|
||||
}
|
||||
PerformanceCounterState::Snapshot snapshot =
|
||||
mPerformanceCounterState.RunnableWillRun(
|
||||
GetPerformanceCounter(event), now,
|
||||
priority == EventQueuePriority::Idle);
|
||||
|
||||
mLastEventStart = now;
|
||||
|
||||
event->Run();
|
||||
|
||||
if (!usingTaskController) {
|
||||
mEvents->DidRunEvent();
|
||||
mPerformanceCounterState.RunnableDidRun(std::move(snapshot.ref()));
|
||||
}
|
||||
mEvents->DidRunEvent();
|
||||
|
||||
mPerformanceCounterState.RunnableDidRun(std::move(snapshot));
|
||||
|
||||
// To cover the event's destructor code inside the LogRunnable span.
|
||||
event = nullptr;
|
||||
|
@ -1292,8 +1266,7 @@ nsThread::SetPriority(int32_t aPriority) {
|
|||
// PR_PRIORITY_NORMAL
|
||||
// PR_PRIORITY_HIGH
|
||||
// PR_PRIORITY_URGENT
|
||||
// We map the priority values defined on nsISupportsPriority to these
|
||||
// values.
|
||||
// We map the priority values defined on nsISupportsPriority to these values.
|
||||
|
||||
mPriority = aPriority;
|
||||
|
||||
|
@ -1493,8 +1466,8 @@ void PerformanceCounterState::RunnableDidRun(Snapshot&& aSnapshot) {
|
|||
mCurrentPerformanceCounter = std::move(aSnapshot.mOldPerformanceCounter);
|
||||
mCurrentRunnableIsIdleRunnable = aSnapshot.mOldIsIdleRunnable;
|
||||
if (IsNestedRunnable()) {
|
||||
// Reset mCurrentTimeSliceStart to right now, so our parent runnable's
|
||||
// next slice can be properly accounted for.
|
||||
// Reset mCurrentTimeSliceStart to right now, so our parent runnable's next
|
||||
// slice can be properly accounted for.
|
||||
mCurrentTimeSliceStart = now;
|
||||
} else {
|
||||
// We are done at the outermost level; we are no longer in a timeslice.
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
#define nsThread_h__
|
||||
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "prenv.h"
|
||||
#include "nsIThreadInternal.h"
|
||||
#include "nsISupportsPriority.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
@ -43,13 +42,6 @@ class nsThreadEnumerator;
|
|||
// See https://www.w3.org/TR/longtasks
|
||||
#define LONGTASK_BUSY_WINDOW_MS 50
|
||||
|
||||
static inline bool UseTaskController() {
|
||||
if (PR_GetEnv("MOZ_USE_TASKCONTROLLER")) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// A class for managing performance counter state.
|
||||
namespace mozilla {
|
||||
class PerformanceCounterState {
|
||||
|
@ -238,9 +230,6 @@ class nsThread : public nsIThreadInternal,
|
|||
virtual mozilla::PerformanceCounter* GetPerformanceCounter(
|
||||
nsIRunnable* aEvent) const;
|
||||
|
||||
static mozilla::PerformanceCounter* GetPerformanceCounterBase(
|
||||
nsIRunnable* aEvent);
|
||||
|
||||
size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
|
||||
|
||||
// Returns the size of this object, its PRThread, and its shutdown contexts,
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
#include "mozilla/ThreadEventQueue.h"
|
||||
#include "mozilla/ThreadLocal.h"
|
||||
#include "PrioritizedEventQueue.h"
|
||||
#include "TaskController.h"
|
||||
#ifdef MOZ_CANARY
|
||||
# include <fcntl.h>
|
||||
# include <unistd.h>
|
||||
|
@ -367,8 +366,6 @@ nsresult nsThreadManager::Init() {
|
|||
: 0;
|
||||
#endif
|
||||
|
||||
TaskController::Initialize();
|
||||
|
||||
nsCOMPtr<nsIIdlePeriod> idlePeriod = new MainThreadIdlePeriod();
|
||||
|
||||
mMainThread =
|
||||
|
|
Загрузка…
Ссылка в новой задаче