diff --git a/dom/base/nsJSEnvironment.cpp b/dom/base/nsJSEnvironment.cpp index 8ece8028ef..26d7cb2de2 100644 --- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -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(aData); + nsJSContext::KillGCTimer(); + uintptr_t reason = reinterpret_cast(aClosure); nsJSContext::GarbageCollectNow(static_cast(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(JS::gcreason::DOM_WINDOW_UTILS)); + GCTimerFired(nullptr, reinterpret_cast(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"); diff --git a/dom/base/nsJSEnvironment.h b/dom/base/nsJSEnvironment.h index 25cd03ec90..20ed35b53e 100644 --- a/dom/base/nsJSEnvironment.h +++ b/dom/base/nsJSEnvironment.h @@ -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); diff --git a/dom/workers/RuntimeService.cpp b/dom/workers/RuntimeService.cpp index bfdbba58ea..66e379bd08 100644 --- a/dom/workers/RuntimeService.cpp +++ b/dom/workers/RuntimeService.cpp @@ -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 wrapper = - new ExternalRunnableWrapper(mWorkerPrivate, aRunnable); - nsRefPtr 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) { diff --git a/js/xpconnect/src/XPCJSContext.cpp b/js/xpconnect/src/XPCJSContext.cpp index 481a420cd7..5e4479637e 100644 --- a/js/xpconnect/src/XPCJSContext.cpp +++ b/js/xpconnect/src/XPCJSContext.cpp @@ -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 mRunnable; -private: - virtual ~TimerCallbackForRunnable() {} -}; - -NS_IMPL_ISUPPORTS(TimerCallbackForRunnable, nsITimerCallback) - -nsresult -XPCJSContext::ScheduleTimerForThread(nsITimer* aTimer, - nsICancelableRunnable* aRunnable, - uint32_t aDelay) -{ - nsCOMPtr callback = - new TimerCallbackForRunnable(aRunnable); - aTimer->InitWithCallback(callback, aDelay, nsITimer::TYPE_ONE_SHOT); - return NS_OK; -} - void xpc_UnmarkSkippableJSHolders() { diff --git a/js/xpconnect/src/xpcprivate.h b/js/xpconnect/src/xpcprivate.h index 0fd5761128..381cec1646 100644 --- a/js/xpconnect/src/xpcprivate.h +++ b/js/xpconnect/src/xpcprivate.h @@ -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; diff --git a/xpcom/base/CycleCollectedJSContext.h b/xpcom/base/CycleCollectedJSContext.h index 281bd3ecf1..c0d7851568 100644 --- a/xpcom/base/CycleCollectedJSContext.h +++ b/xpcom/base/CycleCollectedJSContext.h @@ -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) { diff --git a/xpcom/base/SlowAsynchronousTaskScheduler.cpp b/xpcom/base/SlowAsynchronousTaskScheduler.cpp deleted file mode 100644 index ff6c06d095..0000000000 --- a/xpcom/base/SlowAsynchronousTaskScheduler.cpp +++ /dev/null @@ -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(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((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 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( - (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(); - } -} \ No newline at end of file diff --git a/xpcom/base/SlowAsynchronousTaskScheduler.h b/xpcom/base/SlowAsynchronousTaskScheduler.h deleted file mode 100644 index 5270e07802..0000000000 --- a/xpcom/base/SlowAsynchronousTaskScheduler.h +++ /dev/null @@ -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 - { - 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 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(aMillis)); - } - - LinkedList mScheduledSlowTasks; - nsCOMPtr mTimer; - mozilla::TimeStamp mTimerTime; - nsCOMPtr mTimerCallback; - mozilla::TimeStamp mExpectedNonTimerTick; - - bool mShuttingDown; -}; -} -#endif \ No newline at end of file