зеркало из https://github.com/mozilla/gecko-dev.git
210 строки
5.7 KiB
C++
210 строки
5.7 KiB
C++
/* -*- 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 "IdleTaskRunner.h"
|
|
#include "nsRefreshDriver.h"
|
|
#include "mozilla/SystemGroup.h"
|
|
#include "nsComponentManagerUtils.h"
|
|
|
|
namespace mozilla {
|
|
|
|
already_AddRefed<IdleTaskRunner>
|
|
IdleTaskRunner::Create(const CallbackType& aCallback,
|
|
const char* aRunnableName, uint32_t aDelay,
|
|
int64_t aBudget, bool aRepeating,
|
|
const MayStopProcessingCallbackType& aMayStopProcessing,
|
|
TaskCategory aTaskCategory)
|
|
{
|
|
if (aMayStopProcessing && aMayStopProcessing()) {
|
|
return nullptr;
|
|
}
|
|
|
|
RefPtr<IdleTaskRunner> runner =
|
|
new IdleTaskRunner(aCallback, aRunnableName, aDelay,
|
|
aBudget, aRepeating, aMayStopProcessing,
|
|
aTaskCategory);
|
|
runner->Schedule(false); // Initial scheduling shouldn't use idle dispatch.
|
|
return runner.forget();
|
|
}
|
|
|
|
IdleTaskRunner::IdleTaskRunner(const CallbackType& aCallback,
|
|
const char* aRunnableName,
|
|
uint32_t aDelay, int64_t aBudget,
|
|
bool aRepeating,
|
|
const MayStopProcessingCallbackType& aMayStopProcessing,
|
|
TaskCategory aTaskCategory)
|
|
: IdleRunnable(aRunnableName)
|
|
, mCallback(aCallback), mDelay(aDelay)
|
|
, mBudget(TimeDuration::FromMilliseconds(aBudget))
|
|
, mRepeating(aRepeating), mTimerActive(false)
|
|
, mMayStopProcessing(aMayStopProcessing)
|
|
, mTaskCategory(aTaskCategory)
|
|
{
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
IdleTaskRunner::Run()
|
|
{
|
|
if (!mCallback) {
|
|
return NS_OK;
|
|
}
|
|
|
|
// Deadline is null when called from timer.
|
|
TimeStamp now = TimeStamp::Now();
|
|
bool deadLineWasNull = mDeadline.IsNull();
|
|
bool didRun = false;
|
|
bool allowIdleDispatch = false;
|
|
if (deadLineWasNull || ((now + mBudget) < mDeadline)) {
|
|
CancelTimer();
|
|
didRun = mCallback(mDeadline);
|
|
// If we didn't do meaningful work, don't schedule using immediate
|
|
// idle dispatch, since that could lead to a loop until the idle
|
|
// period ends.
|
|
allowIdleDispatch = didRun;
|
|
} else if (now >= mDeadline) {
|
|
allowIdleDispatch = true;
|
|
}
|
|
|
|
if (mCallback && (mRepeating || !didRun)) {
|
|
Schedule(allowIdleDispatch);
|
|
} else {
|
|
mCallback = nullptr;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
static void
|
|
TimedOut(nsITimer* aTimer, void* aClosure)
|
|
{
|
|
RefPtr<IdleTaskRunner> runnable = static_cast<IdleTaskRunner*>(aClosure);
|
|
runnable->Run();
|
|
}
|
|
|
|
void
|
|
IdleTaskRunner::SetDeadline(mozilla::TimeStamp aDeadline)
|
|
{
|
|
mDeadline = aDeadline;
|
|
};
|
|
|
|
void IdleTaskRunner::SetTimer(uint32_t aDelay, nsIEventTarget* aTarget)
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
// aTarget is always the main thread event target provided from
|
|
// NS_IdleDispatchToCurrentThread(). We ignore aTarget here to ensure that
|
|
// CollectorRunner always run specifically on SystemGroup::EventTargetFor(
|
|
// TaskCategory::GarbageCollection) of the main thread.
|
|
SetTimerInternal(aDelay);
|
|
}
|
|
|
|
nsresult
|
|
IdleTaskRunner::Cancel()
|
|
{
|
|
CancelTimer();
|
|
mTimer = nullptr;
|
|
mScheduleTimer = nullptr;
|
|
mCallback = nullptr;
|
|
return NS_OK;
|
|
}
|
|
|
|
static void
|
|
ScheduleTimedOut(nsITimer* aTimer, void* aClosure)
|
|
{
|
|
RefPtr<IdleTaskRunner> runnable = static_cast<IdleTaskRunner*>(aClosure);
|
|
runnable->Schedule(true);
|
|
}
|
|
|
|
void
|
|
IdleTaskRunner::Schedule(bool aAllowIdleDispatch)
|
|
{
|
|
if (!mCallback) {
|
|
return;
|
|
}
|
|
|
|
if (mMayStopProcessing && mMayStopProcessing()) {
|
|
Cancel();
|
|
return;
|
|
}
|
|
|
|
mDeadline = TimeStamp();
|
|
TimeStamp now = TimeStamp::Now();
|
|
TimeStamp hint = nsRefreshDriver::GetIdleDeadlineHint(now);
|
|
if (hint != now) {
|
|
// RefreshDriver is ticking, let it schedule the idle dispatch.
|
|
nsRefreshDriver::DispatchIdleRunnableAfterTick(this, mDelay);
|
|
// Ensure we get called at some point, even if RefreshDriver is stopped.
|
|
SetTimerInternal(mDelay);
|
|
} else {
|
|
// RefreshDriver doesn't seem to be running.
|
|
if (aAllowIdleDispatch) {
|
|
nsCOMPtr<nsIRunnable> runnable = this;
|
|
SetTimerInternal(mDelay);
|
|
NS_IdleDispatchToCurrentThread(runnable.forget());
|
|
} else {
|
|
if (!mScheduleTimer) {
|
|
mScheduleTimer = NS_NewTimer();
|
|
if (!mScheduleTimer) {
|
|
return;
|
|
}
|
|
} else {
|
|
mScheduleTimer->Cancel();
|
|
}
|
|
if (TaskCategory::Count != mTaskCategory) {
|
|
mScheduleTimer->SetTarget(SystemGroup::EventTargetFor(mTaskCategory));
|
|
}
|
|
// We weren't allowed to do idle dispatch immediately, do it after a
|
|
// short timeout.
|
|
mScheduleTimer->InitWithNamedFuncCallback(ScheduleTimedOut, this, 16,
|
|
nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY,
|
|
"IdleTaskRunner");
|
|
}
|
|
}
|
|
}
|
|
|
|
IdleTaskRunner::~IdleTaskRunner()
|
|
{
|
|
CancelTimer();
|
|
}
|
|
|
|
void
|
|
IdleTaskRunner::CancelTimer()
|
|
{
|
|
nsRefreshDriver::CancelIdleRunnable(this);
|
|
if (mTimer) {
|
|
mTimer->Cancel();
|
|
}
|
|
if (mScheduleTimer) {
|
|
mScheduleTimer->Cancel();
|
|
}
|
|
mTimerActive = false;
|
|
}
|
|
|
|
void
|
|
IdleTaskRunner::SetTimerInternal(uint32_t aDelay)
|
|
{
|
|
if (mTimerActive) {
|
|
return;
|
|
}
|
|
|
|
if (!mTimer) {
|
|
mTimer = NS_NewTimer();
|
|
} else {
|
|
mTimer->Cancel();
|
|
}
|
|
|
|
if (mTimer) {
|
|
if (TaskCategory::Count != mTaskCategory) {
|
|
mTimer->SetTarget(SystemGroup::EventTargetFor(mTaskCategory));
|
|
}
|
|
mTimer->InitWithNamedFuncCallback(TimedOut, this, aDelay,
|
|
nsITimer::TYPE_ONE_SHOT,
|
|
"IdleTaskRunner");
|
|
mTimerActive = true;
|
|
}
|
|
}
|
|
|
|
} // end of namespace mozilla
|