mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-06 15:58:39 +09:00
Revert SATS fully, idle runnables are working use them, faster.
Revert SATS fully, idle runnables are working use them, faster.
This commit is contained in:
parent
df3bcbbb38
commit
5a78e79eb9
8 changed files with 91 additions and 774 deletions
|
|
@ -93,6 +93,14 @@ const size_t gStackSize = 8192;
|
|||
#undef CompareString
|
||||
#endif
|
||||
|
||||
#define NS_SHRINK_GC_BUFFERS_DELAY 4000 // ms
|
||||
|
||||
// The amount of time we wait from the first request to GC to actually
|
||||
// doing the first GC.
|
||||
#define NS_FIRST_GC_DELAY 10000 // ms
|
||||
|
||||
#define NS_FULL_GC_DELAY 60000 // ms
|
||||
|
||||
// The default amount of time to wait from the user being idle to starting a
|
||||
// shrinking GC.
|
||||
#define NS_DEAULT_INACTIVE_GC_DELAY 300000 // ms
|
||||
|
|
@ -100,19 +108,17 @@ const size_t gStackSize = 8192;
|
|||
// Maximum amount of time that should elapse between incremental GC slices
|
||||
#define NS_INTERSLICE_GC_DELAY 100 // ms
|
||||
|
||||
// Maximum amount of time that should elapse between incremental GC slices
|
||||
#define NS_INTERSLICE_GC_DELAY 100 // ms
|
||||
|
||||
// The amount of time we wait between a request to CC (after GC ran)
|
||||
// and doing the actual CC.
|
||||
#define NS_CC_DELAY 50000 // ms
|
||||
|
||||
#define NS_CC_SKIPPABLE_DELAY 400 // ms
|
||||
|
||||
// ForgetSkippable is usually fast, so we can use small budgets.
|
||||
// This isn't a real budget but a hint to CollectorRunner whether there
|
||||
// is enough time to call ForgetSkippable.
|
||||
static const int64_t kForgetSkippableSliceDuration = 2;
|
||||
|
||||
// Maximum amount of time that should elapse between incremental CC slices
|
||||
static const int64_t kICCIntersliceDelay = 64; // ms
|
||||
static const int64_t kICCIntersliceDelay = 32; // ms
|
||||
|
||||
// Time budget for an incremental CC slice when using timer to run it.
|
||||
static const int64_t kICCSliceBudget = 3; // ms
|
||||
|
|
@ -140,89 +146,12 @@ class CollectorRunner;
|
|||
|
||||
// if you add statics here, add them to the list in StartupJSEnvironment
|
||||
|
||||
static SlowAsynchronousTaskScheduler* sScheduler;
|
||||
|
||||
static SATSState
|
||||
TriggerGCOrGCSlice(uint32_t aCurrentID, void* aData);
|
||||
|
||||
static SATSState
|
||||
TriggerGCSlice(uint32_t aCurrentID, void* aData);
|
||||
|
||||
static SATSState
|
||||
TriggerFullGC(uint32_t aCurrentID, void* aData);
|
||||
|
||||
static SATSState
|
||||
ShrinkGCBuffers(uint32_t aCurrentID, void* aData);
|
||||
|
||||
static SATSState
|
||||
TriggerShrinkingGC(uint32_t aCurrentID, void* aData);
|
||||
|
||||
static SATSState
|
||||
TriggerForgetSkippable(uint32_t aCurrentID, void* aData);
|
||||
|
||||
static SATSState
|
||||
TriggerICCSlice(uint32_t aCurrentID, void* aData);
|
||||
|
||||
class CollectorSchedule
|
||||
{
|
||||
public:
|
||||
enum {
|
||||
eInitialGC,
|
||||
eGC,
|
||||
eVariableScheduledGC,
|
||||
eGCSlice,
|
||||
eFullGC,
|
||||
eShrinkingGC,
|
||||
eForgetSkippable,
|
||||
eCCSlice,
|
||||
eNone
|
||||
};
|
||||
};
|
||||
|
||||
static DependentSlowTask sMainThreadCollectorScheduling[]
|
||||
{
|
||||
{ CollectorSchedule::eInitialGC, 10000, TriggerGCOrGCSlice },
|
||||
{ CollectorSchedule::eGC, 4000, TriggerGCOrGCSlice },
|
||||
{ CollectorSchedule::eVariableScheduledGC, 0, TriggerGCOrGCSlice },
|
||||
{ CollectorSchedule::eGCSlice, 100, TriggerGCSlice },
|
||||
{ CollectorSchedule::eFullGC, 60000, TriggerFullGC },
|
||||
{ CollectorSchedule::eShrinkingGC, 300000, TriggerShrinkingGC },
|
||||
{ CollectorSchedule::eForgetSkippable, 250, TriggerForgetSkippable },
|
||||
{ CollectorSchedule::eCCSlice, 32, TriggerICCSlice },
|
||||
{ CollectorSchedule::eNone, 0, nullptr }
|
||||
};
|
||||
|
||||
static bool
|
||||
IsGCScheduled()
|
||||
{
|
||||
return sScheduler->IsScheduled(sMainThreadCollectorScheduling,
|
||||
CollectorSchedule::eInitialGC) ||
|
||||
sScheduler->IsScheduled(sMainThreadCollectorScheduling,
|
||||
CollectorSchedule::eGC) ||
|
||||
sScheduler->IsScheduled(sMainThreadCollectorScheduling,
|
||||
CollectorSchedule::eVariableScheduledGC);
|
||||
}
|
||||
|
||||
static bool
|
||||
IsGCSliceScheduled()
|
||||
{
|
||||
return sScheduler->IsScheduled(sMainThreadCollectorScheduling,
|
||||
CollectorSchedule::eGCSlice);
|
||||
}
|
||||
|
||||
static bool IsForgetSkippableScheduled()
|
||||
{
|
||||
return sScheduler->IsScheduled(sMainThreadCollectorScheduling,
|
||||
CollectorSchedule::eForgetSkippable);
|
||||
}
|
||||
|
||||
static bool
|
||||
IsCCSliceScheduled()
|
||||
{
|
||||
return sScheduler->IsScheduled(sMainThreadCollectorScheduling,
|
||||
CollectorSchedule::eCCSlice);
|
||||
}
|
||||
|
||||
static nsITimer *sGCTimer;
|
||||
static nsITimer *sShrinkingGCTimer;
|
||||
static nsITimer *sCCTimer;
|
||||
static nsITimer *sICCTimer;
|
||||
static nsITimer *sFullGCTimer;
|
||||
static nsITimer *sInterSliceGCTimer;
|
||||
|
||||
static TimeStamp sLastCCEndTime;
|
||||
|
||||
|
|
@ -1324,7 +1253,6 @@ FullGCTimerFired(nsITimer* aTimer, void* aClosure)
|
|||
MOZ_ASSERT(!aClosure, "Don't pass a closure to FullGCTimerFired");
|
||||
nsJSContext::GarbageCollectNow(JS::gcreason::FULL_GC_TIMER,
|
||||
nsJSContext::IncrementalGC);
|
||||
return CollectorSchedule::eNone;
|
||||
}
|
||||
|
||||
//static
|
||||
|
|
@ -1609,10 +1537,10 @@ nsJSContext::CycleCollectNow(nsICycleCollectorListener *aListener,
|
|||
|
||||
//static
|
||||
void
|
||||
nsJSContext::RunCycleCollectorSlice(TimeStamp aDeadline)
|
||||
nsJSContext::RunCycleCollectorSlice()
|
||||
{
|
||||
if (!NS_IsMainThread()) {
|
||||
return CollectorSchedule::eNone;
|
||||
return;
|
||||
}
|
||||
|
||||
PROFILER_LABEL("nsJSContext", "RunCycleCollectorSlice",
|
||||
|
|
@ -1657,10 +1585,8 @@ nsJSContext::RunCycleCollectorSlice(TimeStamp aDeadline)
|
|||
}
|
||||
}
|
||||
|
||||
nsCycleCollector_collectSlice(budget,
|
||||
aDeadline.IsNull() ||
|
||||
(aDeadline - TimeStamp::Now()).ToMilliseconds() <
|
||||
kICCSliceBudget);
|
||||
nsCycleCollector_collectSlice(budget, sDidPaintAfterPreviousICCSlice);
|
||||
sDidPaintAfterPreviousICCSlice = false;
|
||||
|
||||
gCCStats.FinishCycleCollectionSlice();
|
||||
}
|
||||
|
|
@ -1696,11 +1622,11 @@ nsJSContext::GetMaxCCSliceTimeSinceClear()
|
|||
return gCCStats.mMaxSliceTimeSinceClear;
|
||||
}
|
||||
|
||||
static bool
|
||||
ICCRunnerFired(TimeStamp aDeadline, void* aData)
|
||||
static void
|
||||
ICCTimerFired(nsITimer* aTimer, void* aClosure)
|
||||
{
|
||||
if (sDidShutdown) {
|
||||
return CollectorSchedule::eNone;
|
||||
return;
|
||||
}
|
||||
|
||||
// Ignore ICC timer fires during IGC. Running ICC during an IGC will cause us
|
||||
|
|
@ -1710,15 +1636,14 @@ ICCRunnerFired(TimeStamp aDeadline, void* aData)
|
|||
PRTime now = PR_Now();
|
||||
if (sCCLockedOutTime == 0) {
|
||||
sCCLockedOutTime = now;
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
if (now - sCCLockedOutTime < NS_MAX_CC_LOCKEDOUT_TIME) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
nsJSContext::RunCycleCollectorSlice(aDeadline);
|
||||
return true;
|
||||
nsJSContext::RunCycleCollectorSlice();
|
||||
}
|
||||
|
||||
//static
|
||||
|
|
@ -1734,12 +1659,17 @@ nsJSContext::BeginCycleCollectionCallback()
|
|||
|
||||
gCCStats.RunForgetSkippable();
|
||||
|
||||
MOZ_ASSERT(!sICCRunner, "Tried to create a new ICC timer when one already existed.");
|
||||
MOZ_ASSERT(!sICCTimer, "Tried to create a new ICC timer when one already existed.");
|
||||
|
||||
// Create an ICC timer even if ICC is globally disabled, because we could be manually triggering
|
||||
// an incremental collection, and we want to be sure to finish it.
|
||||
sICCRunner = CollectorRunner::Create(ICCRunnerFired, kICCIntersliceDelay,
|
||||
kIdleICCSliceBudget, true);
|
||||
CallCreateInstance("@mozilla.org/timer;1", &sICCTimer);
|
||||
if (sICCTimer) {
|
||||
sICCTimer->InitWithNamedFuncCallback(ICCTimerFired, nullptr,
|
||||
kICCIntersliceDelay,
|
||||
nsITimer::TYPE_REPEATING_SLACK,
|
||||
"ICCTimerFired");
|
||||
}
|
||||
}
|
||||
|
||||
static_assert(NS_GC_DELAY > kMaxICCDuration, "A max duration ICC shouldn't reduce GC delay to 0");
|
||||
|
|
@ -1884,8 +1814,8 @@ nsJSContext::EndCycleCollectionCallback(CycleCollectorResults &aResults)
|
|||
}
|
||||
|
||||
// static
|
||||
bool
|
||||
InterSliceGCRunnerFired(TimeStamp aDeadline, void* aData)
|
||||
void
|
||||
InterSliceGCTimerFired(nsITimer *aTimer, void *aClosure)
|
||||
{
|
||||
nsJSContext::KillInterSliceGCRunner();
|
||||
MOZ_ASSERT(sActiveIntersliceGCBudget > 0);
|
||||
|
|
@ -1912,17 +1842,16 @@ InterSliceGCRunnerFired(TimeStamp aDeadline, void* aData)
|
|||
nsJSContext::IncrementalGC,
|
||||
nsJSContext::NonShrinkingGC,
|
||||
NS_INTERSLICE_GC_BUDGET);
|
||||
return CollectorSchedule::eNone;
|
||||
}
|
||||
|
||||
// static
|
||||
void
|
||||
GCTimerFired(nsITimer *aTimer, void *aClosure)
|
||||
{
|
||||
uintptr_t reason = reinterpret_cast<uintptr_t>(aData);
|
||||
nsJSContext::KillGCTimer();
|
||||
uintptr_t reason = reinterpret_cast<uintptr_t>(aClosure);
|
||||
nsJSContext::GarbageCollectNow(static_cast<JS::gcreason::Reason>(reason),
|
||||
nsJSContext::IncrementalGC);
|
||||
return CollectorSchedule::eNone;
|
||||
}
|
||||
|
||||
// static
|
||||
|
|
@ -1934,7 +1863,6 @@ ShrinkingGCTimerFired(nsITimer* aTimer, void* aClosure)
|
|||
nsJSContext::GarbageCollectNow(JS::gcreason::USER_INACTIVE,
|
||||
nsJSContext::IncrementalGC,
|
||||
nsJSContext::ShrinkingGC);
|
||||
return CollectorSchedule::eNone;
|
||||
}
|
||||
|
||||
static bool
|
||||
|
|
@ -1946,11 +1874,11 @@ ShouldTriggerCC(uint32_t aSuspected)
|
|||
TimeUntilNow(sLastCCEndTime) > NS_CC_FORCED);
|
||||
}
|
||||
|
||||
static bool
|
||||
CCRunnerFired(TimeStamp aDeadline, void* aData)
|
||||
static void
|
||||
CCTimerFired(nsITimer *aTimer, void *aClosure)
|
||||
{
|
||||
if (sDidShutdown) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
static uint32_t ccDelay = NS_CC_DELAY;
|
||||
|
|
@ -1966,10 +1894,10 @@ CCRunnerFired(TimeStamp aDeadline, void* aData)
|
|||
// forgetSkippable and CycleCollectNow eventually.
|
||||
sCCRunnerFireCount = 0;
|
||||
sCCLockedOutTime = now;
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
if (now - sCCLockedOutTime < NS_MAX_CC_LOCKEDOUT_TIME) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1991,12 +1919,7 @@ CCRunnerFired(TimeStamp aDeadline, void* aData)
|
|||
if (ShouldTriggerCC(nsCycleCollector_suspectedCount())) {
|
||||
// Our efforts to avoid a CC have failed, so we return to let the
|
||||
// timer fire once more to trigger a CC.
|
||||
|
||||
// Clear content unbinder before the first CC slice.
|
||||
Element::ClearContentUnbinder();
|
||||
// And trigger deferred deletion too.
|
||||
nsCycleCollector_doDeferredDeletion();
|
||||
return didDoWork;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// We are in the final timer fire and still meet the conditions for
|
||||
|
|
@ -2019,9 +1942,8 @@ CCRunnerFired(TimeStamp aDeadline, void* aData)
|
|||
// We have either just run the CC or decided we don't want to run the CC
|
||||
// next time, so kill the timer.
|
||||
sPreviousSuspectedCount = 0;
|
||||
return CollectorSchedule::eNone;
|
||||
nsJSContext::KillCCTimer();
|
||||
}
|
||||
return didDoWork;
|
||||
}
|
||||
|
||||
// static
|
||||
|
|
@ -2089,14 +2011,13 @@ nsJSContext::RunNextCollectorTimer()
|
|||
|
||||
if (sGCTimer) {
|
||||
if (ReadyToTriggerExpensiveCollectorTimer()) {
|
||||
TriggerGCOrGCSlice(CollectorSchedule::eNone,
|
||||
reinterpret_cast<void *>(JS::gcreason::DOM_WINDOW_UTILS));
|
||||
GCTimerFired(nullptr, reinterpret_cast<void *>(JS::gcreason::DOM_WINDOW_UTILS));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsGCSliceScheduled()) {
|
||||
TriggerGCSlice(CollectorSchedule::eNone, nullptr);
|
||||
if (sInterSliceGCTimer) {
|
||||
InterSliceGCTimerFired(nullptr, nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -2104,15 +2025,15 @@ nsJSContext::RunNextCollectorTimer()
|
|||
// anything if a GC is in progress.
|
||||
MOZ_ASSERT(!sCCLockedOut, "Don't check the CC timers if the CC is locked out.");
|
||||
|
||||
if (sCCRunner) {
|
||||
if (sCCTimer) {
|
||||
if (ReadyToTriggerExpensiveCollectorTimer()) {
|
||||
TriggerForgetSkippable(CollectorSchedule::eNone, nullptr);
|
||||
CCTimerFired(nullptr, nullptr);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsCCSliceScheduled()) {
|
||||
TriggerICCSlice(CollectorSchedule::eNone, nullptr);
|
||||
if (sICCTimer) {
|
||||
ICCTimerFired(nullptr, nullptr);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -2130,12 +2051,12 @@ nsJSContext::PokeGC(JS::gcreason::Reason aReason, int aDelay)
|
|||
sNeedsFullGC = true;
|
||||
}
|
||||
|
||||
if (sGCTimer || sInterSliceGCRunner) {
|
||||
if (sGCTimer || sInterSliceGCTimer) {
|
||||
// There's already a timer for GC'ing, just return
|
||||
return;
|
||||
}
|
||||
|
||||
if (sCCRunner) {
|
||||
if (sCCTimer) {
|
||||
// Make sure CC is called...
|
||||
sNeedsFullCC = true;
|
||||
// and GC after it.
|
||||
|
|
@ -2143,7 +2064,7 @@ nsJSContext::PokeGC(JS::gcreason::Reason aReason, int aDelay)
|
|||
return;
|
||||
}
|
||||
|
||||
if (sICCRunner) {
|
||||
if (sICCTimer) {
|
||||
// Make sure GC is called after the current CC completes.
|
||||
// No need to set sNeedsFullCC because we are currently running a CC.
|
||||
sNeedsGCAfterCC = true;
|
||||
|
|
@ -2196,7 +2117,7 @@ nsJSContext::PokeShrinkingGC()
|
|||
void
|
||||
nsJSContext::MaybePokeCC()
|
||||
{
|
||||
if (sCCRunner || sICCRunner || sShuttingDown || !sHasRunGC) {
|
||||
if (sCCTimer || sICCTimer || sShuttingDown || !sHasRunGC) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -2207,14 +2128,18 @@ nsJSContext::MaybePokeCC()
|
|||
|
||||
|
||||
if (ShouldTriggerCC(nsCycleCollector_suspectedCount())) {
|
||||
sCCRunnerFireCount = 0;
|
||||
|
||||
sCCTimerFireCount = 0;
|
||||
CallCreateInstance("@mozilla.org/timer;1", &sCCTimer);
|
||||
if (!sCCTimer) {
|
||||
return;
|
||||
}
|
||||
// We can kill some objects before running forgetSkippable.
|
||||
nsCycleCollector_dispatchDeferredDeletion();
|
||||
|
||||
sCCRunner =
|
||||
CollectorRunner::Create(CCRunnerFired, NS_CC_SKIPPABLE_DELAY,
|
||||
kForgetSkippableSliceDuration, true);
|
||||
sCCTimer->InitWithNamedFuncCallback(CCTimerFired, nullptr,
|
||||
NS_CC_SKIPPABLE_DELAY,
|
||||
nsITimer::TYPE_REPEATING_SLACK,
|
||||
"CCTimerFired");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2222,12 +2147,9 @@ nsJSContext::MaybePokeCC()
|
|||
void
|
||||
nsJSContext::KillGCTimer()
|
||||
{
|
||||
sScheduler->CancelScheduledTask(sMainThreadCollectorScheduling,
|
||||
CollectorSchedule::eInitialGC);
|
||||
sScheduler->CancelScheduledTask(sMainThreadCollectorScheduling,
|
||||
CollectorSchedule::eGC);
|
||||
sScheduler->CancelScheduledTask(sMainThreadCollectorScheduling,
|
||||
CollectorSchedule::eVariableScheduledGC);
|
||||
if (sGCTimer) {
|
||||
sGCTimer->Cancel();
|
||||
NS_RELEASE(sGCTimer);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2243,9 +2165,9 @@ nsJSContext::KillFullGCTimer()
|
|||
void
|
||||
nsJSContext::KillInterSliceGCRunner()
|
||||
{
|
||||
if (sInterSliceGCRunner) {
|
||||
sInterSliceGCRunner->Cancel();
|
||||
sInterSliceGCRunner = nullptr;
|
||||
if (sInterSliceGCTimer) {
|
||||
sInterSliceGCTimer->Cancel();
|
||||
NS_RELEASE(sInterSliceGCTimer);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2264,9 +2186,9 @@ void
|
|||
nsJSContext::KillCCRunner()
|
||||
{
|
||||
sCCLockedOutTime = 0;
|
||||
if (sCCRunner) {
|
||||
sCCRunner->Cancel();
|
||||
sCCRunner = nullptr;
|
||||
if (sCCTimer) {
|
||||
sCCTimer->Cancel();
|
||||
NS_RELEASE(sCCTimer);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2276,9 +2198,9 @@ nsJSContext::KillICCRunner()
|
|||
{
|
||||
sCCLockedOutTime = 0;
|
||||
|
||||
if (sICCRunner) {
|
||||
sICCRunner->Cancel();
|
||||
sICCRunner = nullptr;
|
||||
if (sICCTimer) {
|
||||
sICCTimer->Cancel();
|
||||
NS_RELEASE(sICCTimer);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2389,9 +2311,12 @@ DOMGCSliceCallback(JSContext* aCx, JS::GCProgress aProgress, const JS::GCDescrip
|
|||
// The GC has more work to do, so schedule another GC slice.
|
||||
nsJSContext::KillInterSliceGCRunner();
|
||||
if (!sShuttingDown) {
|
||||
sInterSliceGCRunner =
|
||||
CollectorRunner::Create(InterSliceGCRunnerFired, NS_INTERSLICE_GC_DELAY,
|
||||
sActiveIntersliceGCBudget, false);
|
||||
CallCreateInstance("@mozilla.org/timer;1", &sInterSliceGCTimer);
|
||||
sInterSliceGCTimer->InitWithNamedFuncCallback(InterSliceGCTimerFired,
|
||||
nullptr,
|
||||
NS_INTERSLICE_GC_DELAY,
|
||||
nsITimer::TYPE_ONE_SHOT,
|
||||
"InterSliceGCTimerFired");
|
||||
}
|
||||
|
||||
if (ShouldTriggerCC(nsCycleCollector_suspectedCount())) {
|
||||
|
|
@ -2444,19 +2369,8 @@ nsJSContext::LikelyShortLivingObjectCreated()
|
|||
void
|
||||
mozilla::dom::StartupJSEnvironment()
|
||||
{
|
||||
sScheduler = CycleCollectedJSRuntime::GetScheduler();
|
||||
MOZ_ASSERT(sScheduler);
|
||||
MOZ_ASSERT(sMainThreadCollectorScheduling[CollectorSchedule::eGC].mDelayMillis ==
|
||||
NS_GC_DELAY);
|
||||
MOZ_ASSERT(sMainThreadCollectorScheduling[CollectorSchedule::eForgetSkippable].mDelayMillis ==
|
||||
NS_CC_SKIPPABLE_DELAY);
|
||||
MOZ_ASSERT(sMainThreadCollectorScheduling[CollectorSchedule::eCCSlice].mDelayMillis ==
|
||||
NS_ICC_DELAY);
|
||||
MOZ_ASSERT(sMainThreadCollectorScheduling[CollectorSchedule::eShrinkingGC].mDelayMillis ==
|
||||
NS_DEFAULT_INACTIVE_GC_DELAY);
|
||||
|
||||
// initialize all our statics, so that we can restart XPCOM
|
||||
sGCTimer = sShrinkingGCTimer = sFullGCTimer = nullptr;
|
||||
sGCTimer = sShrinkingGCTimer = sFullGCTimer = sCCTimer = sICCTimer = nullptr;
|
||||
sCCLockedOut = false;
|
||||
sCCLockedOutTime = 0;
|
||||
sLastCCEndTime = TimeStamp();
|
||||
|
|
@ -2774,9 +2688,9 @@ nsJSContext::EnsureStatics()
|
|||
"javascript.options.compact_on_user_inactive",
|
||||
true);
|
||||
|
||||
sMainThreadCollectorScheduling[CollectorSchedule::eShrinkGCBuffers].mDelayMillis =
|
||||
Preferences::GetUint("javascript.options.compact_on_user_inactive_delay",
|
||||
NS_DEFAULT_INACTIVE_GC_DELAY);
|
||||
Preferences::AddUintVarCache(&sCompactOnUserInactiveDelay,
|
||||
"javascript.options.compact_on_user_inactive_delay",
|
||||
NS_DEAULT_INACTIVE_GC_DELAY);
|
||||
|
||||
Preferences::AddBoolVarCache(&sPostGCEventsToConsole,
|
||||
JS_OPTIONS_DOT_STR "mem.log");
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ public:
|
|||
int32_t aExtraForgetSkippableCalls = 0);
|
||||
|
||||
// Run a cycle collector slice, using a heuristic to decide how long to run it.
|
||||
static void RunCycleCollectorSlice(mozilla::TimeStamp aDeadline);
|
||||
static void RunCycleCollectorSlice();
|
||||
|
||||
// Run a cycle collector slice, using the given work budget.
|
||||
static void RunCycleCollectorWorkSlice(int64_t aWorkBudget);
|
||||
|
|
|
|||
|
|
@ -1035,6 +1035,7 @@ public:
|
|||
|
||||
~WorkerJSContext()
|
||||
{
|
||||
MOZ_COUNT_DTOR_INHERITED(WorkerJSContext, CycleCollectedJSContext);
|
||||
JSContext* cx = MaybeContext();
|
||||
if (!cx) {
|
||||
return; // Initialize() must have failed
|
||||
|
|
@ -1102,20 +1103,6 @@ public:
|
|||
nsCycleCollector_doDeferredDeletion();
|
||||
}
|
||||
|
||||
nsresult ScheduleTimerForThread(nsITimer* aTimer,
|
||||
nsICancelableRunnable* aRunnable,
|
||||
uint32_t aDelay) override
|
||||
{
|
||||
NS_ENSURE_STATE(mWorkerPrivate);
|
||||
nsRefPtr<ExternalRunnableWrapper> wrapper =
|
||||
new ExternalRunnableWrapper(mWorkerPrivate, aRunnable);
|
||||
nsRefPtr<TimerThreadEventTarget> target =
|
||||
new TimerThreadEventTarget(mWorkerPrivate, wrapper);
|
||||
aTimer->SetTarget(target);
|
||||
return aTimer->InitWithFuncCallback(DummyCallback, nullptr, aDelay,
|
||||
nsITimer::TYPE_ONE_SHOT);
|
||||
}
|
||||
|
||||
virtual void CustomGCCallback(JSGCStatus aStatus) override
|
||||
{
|
||||
if (!mWorkerPrivate) {
|
||||
|
|
|
|||
|
|
@ -682,38 +682,6 @@ XPCJSContext::DispatchDeferredDeletion(bool aContinuation, bool aPurge)
|
|||
mAsyncSnowWhiteFreer->Dispatch(aContinuation, aPurge);
|
||||
}
|
||||
|
||||
class TimerCallbackForRunnable : public nsITimerCallback
|
||||
{
|
||||
public:
|
||||
TimerCallbackForRunnable(nsIRunnable* aRunnable)
|
||||
: mRunnable(aRunnable)
|
||||
{}
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_IMETHOD Notify(nsITimer* aTimer) final
|
||||
{
|
||||
mRunnable->Run();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIRunnable> mRunnable;
|
||||
private:
|
||||
virtual ~TimerCallbackForRunnable() {}
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(TimerCallbackForRunnable, nsITimerCallback)
|
||||
|
||||
nsresult
|
||||
XPCJSContext::ScheduleTimerForThread(nsITimer* aTimer,
|
||||
nsICancelableRunnable* aRunnable,
|
||||
uint32_t aDelay)
|
||||
{
|
||||
nsCOMPtr<nsITimerCallback> callback =
|
||||
new TimerCallbackForRunnable(aRunnable);
|
||||
aTimer->InitWithCallback(callback, aDelay, nsITimer::TYPE_ONE_SHOT);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
xpc_UnmarkSkippableJSHolders()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -528,9 +528,6 @@ public:
|
|||
void BeginCycleCollectionCallback() override;
|
||||
void EndCycleCollectionCallback(mozilla::CycleCollectorResults& aResults) override;
|
||||
void DispatchDeferredDeletion(bool aContinuation, bool aPurge = false) override;
|
||||
nsresult ScheduleTimerForThread(nsITimer* aTimer,
|
||||
nsICancelableRunnable* aRunnable,
|
||||
uint32_t aDelay) override;
|
||||
|
||||
void CustomGCCallback(JSGCStatus status) override;
|
||||
void CustomOutOfMemoryCallback() override;
|
||||
|
|
|
|||
|
|
@ -368,27 +368,6 @@ public:
|
|||
// isn't one.
|
||||
static CycleCollectedJSContext* Get();
|
||||
|
||||
static SlowAsynchronousTaskScheduler* GetScheduler()
|
||||
{
|
||||
CycleCollectedJSContext* context = Get();
|
||||
return context ? &context->mSATS : nullptr;
|
||||
}
|
||||
|
||||
// Because of our Web Worker setup being broken, we need to specially initiate
|
||||
// timers for Worker threads.
|
||||
static nsresult ScheduleTimer(nsITimer* aTimer,
|
||||
nsICancelableRunnable* aRunnable,
|
||||
uint32_t aDelay)
|
||||
{
|
||||
CycleCollectedJSContext* context = Get();
|
||||
NS_ENSURE_STATE(context);
|
||||
return context->ScheduleTimerForThread(aTimer, aRunnable, aDelay);
|
||||
}
|
||||
|
||||
virtual nsresult ScheduleTimerForThread(nsITimer* aTimer,
|
||||
nsICancelableRunnable* aRunnable,
|
||||
uint32_t aDelay) = 0;
|
||||
|
||||
// Add aZone to the set of zones waiting for a GC.
|
||||
void AddZoneWaitingForGC(JS::Zone* aZone)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,371 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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 "SlowAsynchronousTaskScheduler.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "nsComponentManagerUtils.h"
|
||||
|
||||
//#define DEBUG_SATS 1
|
||||
using namespace mozilla;
|
||||
|
||||
// If we're expecting a tick to happen soon, don't use timer based tick.
|
||||
static const uint32_t kExpectedTimeOverlapLimit = 10;
|
||||
|
||||
SlowAsynchronousTaskScheduler::SlowAsynchronousTaskScheduler()
|
||||
|
||||
: mShuttingDown(false)
|
||||
{
|
||||
}
|
||||
|
||||
SlowAsynchronousTaskScheduler::~SlowAsynchronousTaskScheduler()
|
||||
{
|
||||
MOZ_ASSERT(!mTimer);
|
||||
while (ScheduledSlowTask* task = mScheduledSlowTasks.popFirst()) {
|
||||
delete task;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SlowAsynchronousTaskScheduler::ExpectedTick(uint32_t aMillisecondsFromNow)
|
||||
{
|
||||
TimeStamp newExpectedTick = FromNow(aMillisecondsFromNow);
|
||||
if (mExpectedNonTimerTick.IsNull() ||
|
||||
newExpectedTick < mExpectedNonTimerTick) {
|
||||
mExpectedNonTimerTick = newExpectedTick;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SlowAsynchronousTaskScheduler::Tick(bool aFromTimer)
|
||||
{
|
||||
CancelTimer();
|
||||
if (mScheduledSlowTasks.isEmpty()) {
|
||||
if (!aFromTimer) {
|
||||
mExpectedNonTimerTick = TimeStamp();
|
||||
}
|
||||
return;
|
||||
}
|
||||
TimeStamp now = TimeStamp::Now();
|
||||
TimeDuration limit =
|
||||
TimeDuration::FromMilliseconds(
|
||||
static_cast<double>(kExpectedTimeOverlapLimit));
|
||||
|
||||
// We have a tick from timer, but we're possibly waiting for a non-timer
|
||||
// tick happening real soon. If so, don't handle this tick.
|
||||
|
||||
if (aFromTimer && !mExpectedNonTimerTick.IsNull() &&
|
||||
now < mExpectedNonTimerTick &&
|
||||
now + limit > mExpectedNonTimerTick) {
|
||||
|
||||
// Try to run the timer right after the expected tick, since
|
||||
// the expected tick might get lost.
|
||||
uint32_t newTimerValue =
|
||||
static_cast<uint32_t>((mExpectedNonTimerTick - now).ToMilliseconds()) + 1;
|
||||
EnsureTimer(newTimerValue);
|
||||
return;
|
||||
}
|
||||
|
||||
mExpectedNonTimerTick = TimeStamp();
|
||||
ScheduledSlowTask* current = mScheduledSlowTasks.popFirst();
|
||||
|
||||
if (!current) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (current->mRunnable) {
|
||||
#ifdef DEBUG_SATS
|
||||
printf("SlowAsynchronousTaskScheduler::Tick[timer=%s], runnable \n",
|
||||
aFromTimer ? "true" : "false");
|
||||
#endif
|
||||
nsCOMPtr<nsIRunnable> runnable;
|
||||
runnable.swap(current->mRunnable);
|
||||
delete current;
|
||||
current = nullptr;
|
||||
runnable->Run();
|
||||
} else {
|
||||
#ifdef DEBUG_SATS
|
||||
printf("SlowAsynchronousTaskScheduler::Tick[timer=%s] task=%p ",
|
||||
aFromTimer ? "true" : "false", current->mSlowTasks);
|
||||
for (uint32_t i = 0; current->mSlowTasks[i].mCallback; ++i) {
|
||||
printf("[%u]", current->mSlowTasks[i].mID);
|
||||
}
|
||||
#endif
|
||||
DependentSlowTask& task =
|
||||
current->mSlowTasks[current->mIndexOfNextTask];
|
||||
#ifdef DEBUG_SATS
|
||||
printf(", will run id=%u\n", task.mID);
|
||||
#endif
|
||||
// Run the currently scheduled task, and store state of the next task.
|
||||
void* data = current->mDataForNextTask;
|
||||
current->mDataForNextTask = nullptr;
|
||||
|
||||
SATSState nextState = task.mCallback(task.mID, data);
|
||||
uint32_t nextID = nextState.mState;
|
||||
uint32_t i = 0;
|
||||
for (; current->mSlowTasks[i].mCallback; ++i) {
|
||||
if (current->mSlowTasks[i].mID == nextID) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// The nextID did point to another task which has a callback to run.
|
||||
// Schedule that.
|
||||
if (current->mSlowTasks[i].mCallback) {
|
||||
current->mIndexOfNextTask = i;
|
||||
current->mDataForNextTask = nextState.mData;
|
||||
// Reschedule, since it now has new mIndexOfNextTask.
|
||||
AddScheduledSlowTask(current, current->mSlowTasks[i].mDelayMillis);
|
||||
} else {
|
||||
// Couldn't find anything to schedule.
|
||||
delete current;
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure we have a timer running if we have something scheduled.
|
||||
ScheduledSlowTask* first = mScheduledSlowTasks.getFirst();
|
||||
|
||||
if (first) {
|
||||
#ifdef DEBUG_SATS
|
||||
printf("Ensuring timer for the next task\n");
|
||||
#endif
|
||||
uint32_t millis = first->mExpectedTimeToRun > now ?
|
||||
static_cast<uint32_t>(
|
||||
(first->mExpectedTimeToRun - now).ToMilliseconds()) :
|
||||
0;
|
||||
EnsureTimer(millis);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SlowAsynchronousTaskScheduler::Schedule(nsIRunnable* aRunnable,
|
||||
uint32_t aMillis)
|
||||
{
|
||||
#ifdef DEBUG_SATS
|
||||
printf("SlowAsynchronousTaskScheduler::Schedule aRunnable\n");
|
||||
#endif
|
||||
|
||||
AddScheduledSlowTask(new ScheduledSlowTask(aRunnable), aMillis);
|
||||
EnsureTimer(aMillis);
|
||||
}
|
||||
|
||||
void
|
||||
SlowAsynchronousTaskScheduler::Schedule(DependentSlowTask* aTasks,
|
||||
void* aData)
|
||||
|
||||
{
|
||||
#ifdef DEBUG_SATS
|
||||
printf("SlowAsynchronousTaskScheduler::Schedule aTasks ");
|
||||
for (uint32_t i = 0; aTasks[i].mCallback; ++i) {
|
||||
printf("[%u]", aTasks[i].mID);
|
||||
}
|
||||
printf("\n");
|
||||
#endif
|
||||
|
||||
MOZ_ASSERT(aTasks && aTasks[0].mCallback);
|
||||
AddScheduledSlowTask(new ScheduledSlowTask(aTasks, 0, aData),
|
||||
aTasks[0].mDelayMillis);
|
||||
EnsureTimer(aTasks[0].mDelayMillis);
|
||||
}
|
||||
|
||||
void
|
||||
SlowAsynchronousTaskScheduler::Schedule(DependentSlowTask* aTasks,
|
||||
uint32_t aID, void* aData)
|
||||
{
|
||||
#ifdef DEBUG_SATS
|
||||
printf("SlowAsynchronousTaskScheduler::Schedule aTask ");
|
||||
for (uint32_t i = 0; aTasks[i].mCallback; ++i) {
|
||||
printf("[%u]", aTasks[i].mID);
|
||||
}
|
||||
printf(", scheduling=%u\n", aID);
|
||||
#endif
|
||||
|
||||
for (uint32_t i = 0; aTasks[i].mCallback; ++i) {
|
||||
if (aTasks[i].mID == aID) {
|
||||
MOZ_ASSERT(aTasks[i].mCallback);
|
||||
AddScheduledSlowTask(new ScheduledSlowTask(aTasks, i, aData),
|
||||
aTasks[i].mDelayMillis);
|
||||
EnsureTimer(aTasks[i].mDelayMillis);
|
||||
return;
|
||||
}
|
||||
}
|
||||
MOZ_ASSERT(false, "Unknown ID?");
|
||||
}
|
||||
|
||||
void
|
||||
SlowAsynchronousTaskScheduler::CancelScheduledTask(nsIRunnable* aRunnable)
|
||||
{
|
||||
if (aRunnable) {
|
||||
ScheduledSlowTask* task = mScheduledSlowTasks.getFirst();
|
||||
while(task) {
|
||||
ScheduledSlowTask* next = task->getNext();
|
||||
if (task->mRunnable == aRunnable) {
|
||||
task->remove();
|
||||
delete task;
|
||||
}
|
||||
task = next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SlowAsynchronousTaskScheduler::CancelScheduledTask(DependentSlowTask* aTasks)
|
||||
{
|
||||
if (aTasks) {
|
||||
ScheduledSlowTask* task = mScheduledSlowTasks.getFirst();
|
||||
while(task) {
|
||||
ScheduledSlowTask* next = task->getNext();
|
||||
if (task->mSlowTasks == aTasks) {
|
||||
task->remove();
|
||||
delete task;
|
||||
}
|
||||
task = next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SlowAsynchronousTaskScheduler::CancelScheduledTask(DependentSlowTask* aTasks,
|
||||
uint32_t aID)
|
||||
{
|
||||
if (aTasks) {
|
||||
ScheduledSlowTask* task = mScheduledSlowTasks.getFirst();
|
||||
while(task) {
|
||||
ScheduledSlowTask* next = task->getNext();
|
||||
if (task->mSlowTasks == aTasks &&
|
||||
task->mSlowTasks[task->mIndexOfNextTask].mID == aID) {
|
||||
task->remove();
|
||||
delete task;
|
||||
}
|
||||
task = next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
SlowAsynchronousTaskScheduler::IsScheduled(nsIRunnable* aRunnable)
|
||||
{
|
||||
if (aRunnable) {
|
||||
ScheduledSlowTask* task = mScheduledSlowTasks.getFirst();
|
||||
while(task) {
|
||||
if (task->mRunnable == aRunnable) {
|
||||
return true;
|
||||
}
|
||||
task = task->getNext();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
SlowAsynchronousTaskScheduler::IsScheduled(DependentSlowTask* aTasks)
|
||||
{
|
||||
if (aTasks) {
|
||||
ScheduledSlowTask* task = mScheduledSlowTasks.getFirst();
|
||||
while(task) {
|
||||
if (task->mSlowTasks == aTasks) {
|
||||
return true;
|
||||
}
|
||||
task = task->getNext();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
SlowAsynchronousTaskScheduler::IsScheduled(DependentSlowTask* aTasks,
|
||||
uint32_t aID)
|
||||
{
|
||||
if (aTasks) {
|
||||
ScheduledSlowTask* task = mScheduledSlowTasks.getFirst();
|
||||
while(task) {
|
||||
if (task->mSlowTasks == aTasks &&
|
||||
task->mSlowTasks[task->mIndexOfNextTask].mID == aID) {
|
||||
return true;
|
||||
}
|
||||
task = task->getNext();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
class SATSTimerTick : public nsCancelableRunnable
|
||||
{
|
||||
public:
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
if (!mCancelled) {
|
||||
SlowAsynchronousTaskScheduler* sats =
|
||||
CycleCollectedJSContext::GetScheduler();
|
||||
if (sats) {
|
||||
sats->TickFromTimer();
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
NS_IMETHOD Cancel()
|
||||
{
|
||||
mCancelled = true;
|
||||
return NS_OK;
|
||||
}
|
||||
bool mCancelled = false;
|
||||
};
|
||||
|
||||
void
|
||||
SlowAsynchronousTaskScheduler::EnsureTimer(uint32_t aMillis)
|
||||
{
|
||||
#ifdef DEBUG_SATS
|
||||
printf("SlowAsynchronousTaskScheduler::EnsureTimer %ums\n", aMillis);
|
||||
#endif
|
||||
TimeStamp fromNow = FromNow(aMillis);
|
||||
if (mTimer) {
|
||||
if (!mTimerTime.IsNull() && mTimerTime < fromNow) {
|
||||
// We will run the timer sooner than aMillis from now.
|
||||
return;
|
||||
}
|
||||
mTimer->Cancel();
|
||||
} else {
|
||||
if (mShuttingDown) {
|
||||
return;
|
||||
}
|
||||
mTimer = do_CreateInstance("@mozilla.org/timer;1");
|
||||
if (!mTimer) {
|
||||
NS_WARNING("No timer!");
|
||||
return;
|
||||
}
|
||||
mTimerCallback = new SATSTimerTick();
|
||||
}
|
||||
mTimerTime = fromNow;
|
||||
CycleCollectedJSContext::ScheduleTimer(mTimer, mTimerCallback, aMillis);
|
||||
}
|
||||
|
||||
void
|
||||
SlowAsynchronousTaskScheduler::AddScheduledSlowTask(ScheduledSlowTask* aTask,
|
||||
uint32_t aMillis)
|
||||
{
|
||||
TimeStamp expectedTime = FromNow(aMillis);
|
||||
ScheduledSlowTask* task = mScheduledSlowTasks.getFirst();
|
||||
while(task) {
|
||||
if (task->mExpectedTimeToRun > expectedTime) {
|
||||
task->setPrevious(aTask);
|
||||
break;
|
||||
|
||||
}
|
||||
task = task->getNext();
|
||||
|
||||
}
|
||||
if (!task) {
|
||||
mScheduledSlowTasks.insertBack(aTask);
|
||||
}
|
||||
aTask->mExpectedTimeToRun = expectedTime;
|
||||
}
|
||||
|
||||
void
|
||||
SlowAsynchronousTaskScheduler::CancelTimer()
|
||||
{
|
||||
if (mTimer) {
|
||||
mTimer->Cancel();
|
||||
mTimerTime = TimeStamp();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,157 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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_SlowAsynchronousTaskScheduler_h__
|
||||
#define mozilla_SlowAsynchronousTaskScheduler_h__
|
||||
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsITimer.h"
|
||||
#include "mozilla/LinkedList.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
struct SATSState
|
||||
{
|
||||
MOZ_IMPLICIT SATSState(uint32_t aState)
|
||||
: mState(aState), mData(nullptr)
|
||||
{}
|
||||
|
||||
MOZ_IMPLICIT SATSState(uint32_t aState, void* aData)
|
||||
: mState(aState), mData(aData)
|
||||
{}
|
||||
|
||||
MOZ_IMPLICIT SATSState(const SATSState& aOther)
|
||||
: mState(aOther.mState), mData(aOther.mData)
|
||||
{}
|
||||
|
||||
uint32_t mState;
|
||||
void* mData;
|
||||
|
||||
private:
|
||||
SATSState() = delete;
|
||||
};
|
||||
|
||||
// Returns the ID of the next DependentSlowTask.
|
||||
typedef SATSState (*SlowTaskCallback)(uint32_t aCurrentID, void* aData);
|
||||
|
||||
struct DependentSlowTask
|
||||
{
|
||||
uint32_t mID;
|
||||
uint32_t mDelayMillis;
|
||||
SlowTaskCallback mCallback;
|
||||
};
|
||||
|
||||
// If one has several tasks which need to run occasionally and those tasks
|
||||
// depend on the each others, one can pass pointer to an array of
|
||||
// DependentSlowTasks objects. Each callback returns the ID of the next state.
|
||||
//
|
||||
// enum {
|
||||
// eIDOfState1,
|
||||
// eIdOfState2,
|
||||
// eIdOfFinishState
|
||||
// };
|
||||
//
|
||||
// static DependentSlowTask sMySlowTasks[]
|
||||
// {
|
||||
// { eIDOfState1, 10000, TriggerState1 },
|
||||
// { eIdOfState2, 5000, TriggerState2 },
|
||||
// { eIdOfFinishState, 0, nullptr }
|
||||
// };
|
||||
//
|
||||
// scheduler->Schedule(sMySlowTasks);
|
||||
class SlowAsynchronousTaskScheduler
|
||||
{
|
||||
public:
|
||||
SlowAsynchronousTaskScheduler();
|
||||
~SlowAsynchronousTaskScheduler();
|
||||
|
||||
// Call ExpectedTick() to give SATS hint when the next non-timer based Tick
|
||||
// might be called. For example refresh driver could call this.
|
||||
void ExpectedTick(uint32_t aMillisecondsFromNow);
|
||||
|
||||
// Call Tick() to run possible pending scheduled tasks.
|
||||
void Tick()
|
||||
|
||||
{
|
||||
Tick(false);
|
||||
}
|
||||
|
||||
void TickFromTimer()
|
||||
{
|
||||
Tick(true);
|
||||
}
|
||||
|
||||
void Schedule(nsIRunnable* aRunnable, uint32_t aMillis);
|
||||
void Schedule(DependentSlowTask* aTasks, void* aData = nullptr);
|
||||
void Schedule(DependentSlowTask* aTasks, uint32_t aID, void* aData = nullptr);
|
||||
|
||||
void CancelScheduledTask(nsIRunnable* aRunnable);
|
||||
void CancelScheduledTask(DependentSlowTask* aTasks);
|
||||
void CancelScheduledTask(DependentSlowTask* aTasks, uint32_t aID);
|
||||
|
||||
bool IsScheduled(nsIRunnable* aRunnable);
|
||||
bool IsScheduled(DependentSlowTask* aTasks);
|
||||
bool IsScheduled(DependentSlowTask* aTasks, uint32_t aID);
|
||||
void Shutdown()
|
||||
{
|
||||
CancelTimer();
|
||||
mTimer = nullptr;
|
||||
mShuttingDown = true;
|
||||
}
|
||||
private:
|
||||
class ScheduledSlowTask : public LinkedListElement<ScheduledSlowTask>
|
||||
{
|
||||
public:
|
||||
ScheduledSlowTask(DependentSlowTask* aSlowTasks, uint32_t aIndex = 0,
|
||||
void* aData = nullptr)
|
||||
: mSlowTasks(aSlowTasks)
|
||||
, mIndexOfNextTask(aIndex)
|
||||
, mDataForNextTask(aData)
|
||||
{
|
||||
MOZ_COUNT_CTOR(ScheduledSlowTask);
|
||||
}
|
||||
|
||||
ScheduledSlowTask(nsIRunnable* aSlowTask)
|
||||
: mRunnable(aSlowTask)
|
||||
, mSlowTasks(nullptr)
|
||||
, mIndexOfNextTask(0)
|
||||
, mDataForNextTask(nullptr)
|
||||
{
|
||||
MOZ_COUNT_CTOR(ScheduledSlowTask);
|
||||
}
|
||||
|
||||
~ScheduledSlowTask()
|
||||
{
|
||||
MOZ_COUNT_DTOR(ScheduledSlowTask);
|
||||
}
|
||||
|
||||
mozilla::TimeStamp mExpectedTimeToRun;
|
||||
nsCOMPtr<nsIRunnable> mRunnable;
|
||||
DependentSlowTask* mSlowTasks;
|
||||
uint32_t mIndexOfNextTask;
|
||||
void* mDataForNextTask;
|
||||
};
|
||||
|
||||
void Tick(bool aFromTimer);
|
||||
void AddScheduledSlowTask(ScheduledSlowTask* aTask, uint32_t aMillis);
|
||||
void EnsureTimer(uint32_t aMillis);
|
||||
void CancelTimer();
|
||||
static TimeStamp FromNow(uint32_t aMillis)
|
||||
{
|
||||
return TimeStamp::Now() +
|
||||
TimeDuration::FromMilliseconds(static_cast<double>(aMillis));
|
||||
}
|
||||
|
||||
LinkedList<ScheduledSlowTask> mScheduledSlowTasks;
|
||||
nsCOMPtr<nsITimer> mTimer;
|
||||
mozilla::TimeStamp mTimerTime;
|
||||
nsCOMPtr<nsICancelableRunnable> mTimerCallback;
|
||||
mozilla::TimeStamp mExpectedNonTimerTick;
|
||||
|
||||
bool mShuttingDown;
|
||||
};
|
||||
}
|
||||
#endif
|
||||
Loading…
Add table
Add a link
Reference in a new issue