Bug 1672121 - Stop sGCRunner GC slice if the IdleTaskRunnerTask gets a RequestInterrupt() call r=smaug

Differential Revision: https://phabricator.services.mozilla.com/D109631
This commit is contained in:
Steve Fink 2021-12-22 18:06:06 +00:00
Родитель ec62429c88
Коммит 543df99e4e
6 изменённых файлов: 57 добавлений и 11 удалений

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

@ -486,7 +486,8 @@ void CCGCScheduler::EnsureGCRunner(TimeDuration aDelay) {
"CCGCScheduler::EnsureGCRunner", aDelay,
TimeDuration::FromMilliseconds(
StaticPrefs::javascript_options_gc_delay_interslice()),
mActiveIntersliceGCBudget, true, [this] { return mDidShutdown; });
mActiveIntersliceGCBudget, true, [this] { return mDidShutdown; },
[this](uint32_t) { mInterruptRequested = true; });
}
// nsJSEnvironmentObserver observes the user-interaction-inactive notifications

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

@ -109,6 +109,8 @@ struct CCRunnerStep {
class CCGCScheduler {
public:
CCGCScheduler() : mInterruptRequested(false) {}
static bool CCRunnerFired(TimeStamp aDeadline);
// Parameter setting
@ -151,6 +153,11 @@ class CCGCScheduler {
void KillCCRunner();
void KillAllTimersAndRunners();
js::SliceBudget CreateGCSliceBudget(JS::GCReason aReason, int64_t aMillis) {
return js::SliceBudget(mozilla::TimeDuration::FromMilliseconds(aMillis),
&mInterruptRequested);
}
/*
* aDelay is the delay before the first time the idle task runner runs.
* Then it runs every
@ -435,6 +442,10 @@ class CCGCScheduler {
// The parent process is ready for us to do a major GC.
bool mReadyForMajorGC = false;
// Set when the IdleTaskRunner requests the current task be interrupted.
// Cleared when the GC slice budget has detected the interrupt request.
mozilla::Atomic<bool> mInterruptRequested;
// When a shrinking GC has been requested but we back-out, if this is true
// we run a non-shrinking GC.
bool mWantAtLeastRegularGC = false;

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

@ -80,7 +80,6 @@
#include "mozilla/ContentEvents.h"
#include "mozilla/CycleCollectedJSContext.h"
#include "nsCycleCollectionNoteRootCallback.h"
#include "mozilla/IdleTaskRunner.h"
#include "nsViewManager.h"
#include "mozilla/EventStateManager.h"
#include "mozilla/ProfilerLabels.h"
@ -1924,6 +1923,11 @@ static bool ConsumeStream(JSContext* aCx, JS::HandleObject aObj,
nullptr);
}
static js::SliceBudget CreateGCSliceBudget(JS::GCReason aReason,
int64_t aMillis) {
return sScheduler.CreateGCSliceBudget(aReason, aMillis);
}
void nsJSContext::EnsureStatics() {
if (sIsInitialized) {
if (!nsContentUtils::XPConnect()) {
@ -1940,6 +1944,8 @@ void nsJSContext::EnsureStatics() {
sPrevGCSliceCallback = JS::SetGCSliceCallback(jsapi.cx(), DOMGCSliceCallback);
JS::SetCreateGCSliceBudgetCallback(jsapi.cx(), CreateGCSliceBudget);
JS::InitDispatchToEventLoop(jsapi.cx(), DispatchToEventLoop, nullptr);
JS::InitConsumeStreamCallback(jsapi.cx(), ConsumeStream,
FetchUtil::ReportJSStreamError);

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

@ -13,6 +13,7 @@
#include "nsThreadUtils.h"
#include "nsITimer.h"
#include "nsIThread.h"
#include "nsXPCOMPrivate.h" // for gXPCOMThreadsShutDown
namespace mozilla::ipc {
@ -70,7 +71,9 @@ IdleSchedulerParent::IdleSchedulerParent() {
CalculateNumIdleTasks();
});
thread->Dispatch(runnable, NS_DISPATCH_NORMAL);
if (MOZ_LIKELY(!gXPCOMThreadsShutDown)) {
thread->Dispatch(runnable, NS_DISPATCH_NORMAL);
}
}
});
NS_DispatchBackgroundTask(runnable.forget(), NS_DISPATCH_EVENT_MAY_BLOCK);

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

@ -14,14 +14,15 @@ already_AddRefed<IdleTaskRunner> IdleTaskRunner::Create(
const CallbackType& aCallback, const char* aRunnableName,
TimeDuration aStartDelay, TimeDuration aMaxDelay,
TimeDuration aMinimumUsefulBudget, bool aRepeating,
const MayStopProcessingCallbackType& aMayStopProcessing) {
const MayStopProcessingCallbackType& aMayStopProcessing,
const RequestInterruptCallbackType& aRequestInterrupt) {
if (aMayStopProcessing && aMayStopProcessing()) {
return nullptr;
}
RefPtr<IdleTaskRunner> runner =
new IdleTaskRunner(aCallback, aRunnableName, aStartDelay, aMaxDelay,
aMinimumUsefulBudget, aRepeating, aMayStopProcessing);
RefPtr<IdleTaskRunner> runner = new IdleTaskRunner(
aCallback, aRunnableName, aStartDelay, aMaxDelay, aMinimumUsefulBudget,
aRepeating, aMayStopProcessing, aRequestInterrupt);
runner->Schedule(false); // Initial scheduling shouldn't use idle dispatch.
return runner.forget();
}
@ -29,7 +30,9 @@ already_AddRefed<IdleTaskRunner> IdleTaskRunner::Create(
class IdleTaskRunnerTask : public Task {
public:
explicit IdleTaskRunnerTask(IdleTaskRunner* aRunner)
: Task(true, EventQueuePriority::Idle), mRunner(aRunner) {
: Task(true, EventQueuePriority::Idle),
mRunner(aRunner),
mRequestInterrupt(aRunner->mRequestInterrupt) {
SetManager(TaskController::Get()->GetIdleTaskManager());
}
@ -61,15 +64,26 @@ class IdleTaskRunnerTask : public Task {
return true;
}
void RequestInterrupt(uint32_t aInterruptPriority) override {
if (mRequestInterrupt) {
mRequestInterrupt(aInterruptPriority);
}
}
private:
IdleTaskRunner* mRunner;
// Copied here and invoked even if there is no mRunner currently, to avoid
// race conditions checking mRunner when an interrupt is requested.
IdleTaskRunner::RequestInterruptCallbackType mRequestInterrupt;
};
IdleTaskRunner::IdleTaskRunner(
const CallbackType& aCallback, const char* aRunnableName,
TimeDuration aStartDelay, TimeDuration aMaxDelay,
TimeDuration aMinimumUsefulBudget, bool aRepeating,
const MayStopProcessingCallbackType& aMayStopProcessing)
const MayStopProcessingCallbackType& aMayStopProcessing,
const RequestInterruptCallbackType& aRequestInterrupt)
: mCallback(aCallback),
mStartTime(TimeStamp::Now() + aStartDelay),
mMaxDelay(aMaxDelay),
@ -77,6 +91,7 @@ IdleTaskRunner::IdleTaskRunner(
mRepeating(aRepeating),
mTimerActive(false),
mMayStopProcessing(aMayStopProcessing),
mRequestInterrupt(aRequestInterrupt),
mName(aRunnableName) {}
void IdleTaskRunner::Run() {

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

@ -23,6 +23,7 @@ class IdleTaskRunnerTask;
// true to completely remove the runner.
class IdleTaskRunner {
public:
friend class IdleTaskRunnerTask;
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(IdleTaskRunner)
// Return true if some meaningful work was done.
@ -33,6 +34,12 @@ class IdleTaskRunner {
// work together in different way.
using MayStopProcessingCallbackType = std::function<bool()>;
// A callback to be invoked when an interrupt is requested
// (eg during an idle activity when the user presses a key.)
// The callback takes an "interrupt priority" value as its
// sole parameter.
using RequestInterruptCallbackType = std::function<void(uint32_t)>;
public:
// An IdleTaskRunner has (up to) three phases:
//
@ -53,7 +60,8 @@ class IdleTaskRunner {
const CallbackType& aCallback, const char* aRunnableName,
TimeDuration aStartDelay, TimeDuration aMaxDelay,
TimeDuration aMinimumUsefulBudget, bool aRepeating,
const MayStopProcessingCallbackType& aMayStopProcessing);
const MayStopProcessingCallbackType& aMayStopProcessing,
const RequestInterruptCallbackType& aRequestInterrupt = nullptr);
void Run();
@ -77,7 +85,8 @@ class IdleTaskRunner {
const CallbackType& aCallback, const char* aRunnableName,
TimeDuration aStartDelay, TimeDuration aMaxDelay,
TimeDuration aMinimumUsefulBudget, bool aRepeating,
const MayStopProcessingCallbackType& aMayStopProcessing);
const MayStopProcessingCallbackType& aMayStopProcessing,
const RequestInterruptCallbackType& aRequestInterrupt);
~IdleTaskRunner();
void CancelTimer();
void SetTimerInternal(TimeDuration aDelay);
@ -103,6 +112,7 @@ class IdleTaskRunner {
bool mRepeating;
bool mTimerActive;
MayStopProcessingCallbackType mMayStopProcessing;
RequestInterruptCallbackType mRequestInterrupt;
const char* mName;
RefPtr<IdleTaskRunnerTask> mTask;
};