Bug 1465505: Replace PRMJ_Now() by mozilla::TimeStamp r=jonco draft
authorWander Lairson Costa <wcosta@mozilla.com>
Wed, 04 Jul 2018 16:55:11 -0300
changeset 814230 e2b5fe81a4c5c696425583a04395f2ae79aeaccc
parent 814180 6c0fa9a675c91390ca27664ffb626c56e8afea4d
push id115141
push userbmo:wcosta@mozilla.com
push dateWed, 04 Jul 2018 19:55:38 +0000
reviewersjonco
bugs1465505
milestone63.0a1
Bug 1465505: Replace PRMJ_Now() by mozilla::TimeStamp r=jonco Notice as TimeStamp is not an integral type, it can't be wrapped by mozilla::Atomic. However, we wrap it in MainThreadData to assure it only is accessed from the main thread. Another issue is that TimeStamp class does allow some operations on a Null value, with assertions on debug builds. MozReview-Commit-ID: 9GPNDUooQmI
js/public/GCAPI.h
js/public/SliceBudget.h
js/src/gc/GC.cpp
js/src/gc/GCRuntime.h
js/src/gc/Scheduling.h
js/src/jsfriendapi.cpp
js/src/vm/Realm.h
js/src/vm/Runtime.cpp
js/src/vm/Runtime.h
--- a/js/public/GCAPI.h
+++ b/js/public/GCAPI.h
@@ -136,17 +136,17 @@ typedef enum JSGCParamKey {
 
     /**
      * GCs less than this far apart in time will be considered 'high-frequency
      * GCs'.
      *
      * See setGCLastBytes in jsgc.cpp.
      *
      * Pref: javascript.options.mem.gc_high_frequency_time_limit_ms
-     * Default: HighFrequencyThresholdUsec
+     * Default: HighFrequencyThreshold
      */
     JSGC_HIGH_FREQUENCY_TIME_LIMIT = 11,
 
     /**
      * Start of dynamic heap growth.
      *
      * Pref: javascript.options.mem.gc_high_frequency_low_limit_mb
      * Default: HighFrequencyLowLimitBytes
--- a/js/public/SliceBudget.h
+++ b/js/public/SliceBudget.h
@@ -2,16 +2,18 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * 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 js_SliceBudget_h
 #define js_SliceBudget_h
 
+#include "mozilla/TimeStamp.h"
+
 #include <stdint.h>
 
 #include "jstypes.h"
 
 namespace js {
 
 struct JS_PUBLIC_API(TimeBudget)
 {
@@ -30,31 +32,31 @@ struct JS_PUBLIC_API(WorkBudget)
 /*
  * This class records how much work has been done in a given collection slice,
  * so that we can return before pausing for too long. Some slices are allowed
  * to run for unlimited time, and others are bounded. To reduce the number of
  * gettimeofday calls, we only check the time every 1000 operations.
  */
 class JS_PUBLIC_API(SliceBudget)
 {
-    static const int64_t unlimitedDeadline = INT64_MAX;
+    static const mozilla::TimeStamp unlimitedDeadline;
     static const intptr_t unlimitedStartCounter = INTPTR_MAX;
 
     bool checkOverBudget();
 
     SliceBudget();
 
   public:
     // Memory of the originally requested budget. If isUnlimited, neither of
     // these are in use. If deadline==0, then workBudget is valid. Otherwise
     // timeBudget is valid.
     TimeBudget timeBudget;
     WorkBudget workBudget;
 
-    int64_t deadline; /* in microseconds */
+    mozilla::TimeStamp deadline;
     intptr_t counter;
 
     static const intptr_t CounterReset = 1000;
 
     static const int64_t UnlimitedTimeBudget = -1;
     static const int64_t UnlimitedWorkBudget = -1;
 
     /* Use to create an unlimited budget. */
@@ -76,18 +78,18 @@ class JS_PUBLIC_API(SliceBudget)
     }
 
     bool isOverBudget() {
         if (counter > 0)
             return false;
         return checkOverBudget();
     }
 
-    bool isWorkBudget() const { return deadline == 0; }
-    bool isTimeBudget() const { return deadline > 0 && !isUnlimited(); }
+    bool isWorkBudget() const { return deadline.IsNull(); }
+    bool isTimeBudget() const { return !deadline.IsNull() && !isUnlimited(); }
     bool isUnlimited() const { return deadline == unlimitedDeadline; }
 
     int describe(char* buffer, size_t maxlen) const;
 };
 
 } // namespace js
 
 #endif /* js_SliceBudget_h */
--- a/js/src/gc/GC.cpp
+++ b/js/src/gc/GC.cpp
@@ -300,17 +300,17 @@ namespace TuningDefaults {
 
     /* no parameter */
     static const size_t ZoneAllocDelayBytes = 1024 * 1024;
 
     /* JSGC_DYNAMIC_HEAP_GROWTH */
     static const bool DynamicHeapGrowthEnabled = false;
 
     /* JSGC_HIGH_FREQUENCY_TIME_LIMIT */
-    static const uint64_t HighFrequencyThresholdUsec = 1000000;
+    static const auto HighFrequencyThreshold = mozilla::TimeDuration::FromSeconds(1);
 
     /* JSGC_HIGH_FREQUENCY_LOW_LIMIT */
     static const uint64_t HighFrequencyLowLimitBytes = 100 * 1024 * 1024;
 
     /* JSGC_HIGH_FREQUENCY_HIGH_LIMIT */
     static const uint64_t HighFrequencyHighLimitBytes = 500 * 1024 * 1024;
 
     /* JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MAX */
@@ -341,16 +341,18 @@ namespace TuningDefaults {
     static const bool CompactingEnabled = true;
 
     /* JSGC_NURSERY_FREE_THRESHOLD_FOR_IDLE_COLLECTION */
     static const uint32_t NurseryFreeThresholdForIdleCollection =
         Nursery::NurseryChunkUsableSize / 4;
 
 }}} // namespace js::gc::TuningDefaults
 
+static const auto ONE_SECOND = mozilla::TimeDuration::FromSeconds(1);
+
 /*
  * We start to incremental collection for a zone when a proportion of its
  * threshold is reached. This is configured by the
  * JSGC_ALLOCATION_THRESHOLD_FACTOR and
  * JSGC_ALLOCATION_THRESHOLD_FACTOR_AVOID_INTERRUPT parameters.
  */
 static const double MinAllocationThresholdFactor = 0.9;
 
@@ -948,17 +950,17 @@ GCRuntime::GCRuntime(JSRuntime* rt) :
     atomsZone(nullptr),
     stats_(rt),
     marker(rt),
     usage(nullptr),
     nextCellUniqueId_(LargestTaggedNullCellPointer + 1), // Ensure disjoint from null tagged pointers.
     numArenasFreeCommitted(0),
     verifyPreData(nullptr),
     chunkAllocationSinceLastGC(false),
-    lastGCTime(PRMJ_Now()),
+    lastGCTime(mozilla::TimeStamp::Now()),
     mode(TuningDefaults::Mode),
     numActiveZoneIters(0),
     cleanUpEverything(false),
     grayBufferState(GCRuntime::GrayBufferState::Unused),
     grayBitsValid(false),
     majorGCTriggerReason(JS::gcreason::NO_REASON),
     fullGCForAtomsRequested_(false),
     minorGCNumber(0),
@@ -1409,17 +1411,17 @@ GCSchedulingTunables::setParameter(JSGCP
     switch(key) {
       case JSGC_MAX_BYTES:
         gcMaxBytes_ = value;
         break;
       case JSGC_MAX_NURSERY_BYTES:
         gcMaxNurseryBytes_ = value;
         break;
       case JSGC_HIGH_FREQUENCY_TIME_LIMIT:
-        highFrequencyThresholdUsec_ = value * PRMJ_USEC_PER_MSEC;
+        highFrequencyThreshold_ = mozilla::TimeDuration::FromMilliseconds(value);
         break;
       case JSGC_HIGH_FREQUENCY_LOW_LIMIT: {
         uint64_t newLimit = (uint64_t)value * 1024 * 1024;
         if (newLimit == UINT64_MAX)
             return false;
         setHighFrequencyLowLimit(newLimit);
         break;
       }
@@ -1565,17 +1567,17 @@ GCSchedulingTunables::GCSchedulingTunabl
   : gcMaxBytes_(0),
     maxMallocBytes_(TuningDefaults::MaxMallocBytes),
     gcMaxNurseryBytes_(0),
     gcZoneAllocThresholdBase_(TuningDefaults::GCZoneAllocThresholdBase),
     allocThresholdFactor_(TuningDefaults::AllocThresholdFactor),
     allocThresholdFactorAvoidInterrupt_(TuningDefaults::AllocThresholdFactorAvoidInterrupt),
     zoneAllocDelayBytes_(TuningDefaults::ZoneAllocDelayBytes),
     dynamicHeapGrowthEnabled_(TuningDefaults::DynamicHeapGrowthEnabled),
-    highFrequencyThresholdUsec_(TuningDefaults::HighFrequencyThresholdUsec),
+    highFrequencyThreshold_(TuningDefaults::HighFrequencyThreshold),
     highFrequencyLowLimitBytes_(TuningDefaults::HighFrequencyLowLimitBytes),
     highFrequencyHighLimitBytes_(TuningDefaults::HighFrequencyHighLimitBytes),
     highFrequencyHeapGrowthMax_(TuningDefaults::HighFrequencyHeapGrowthMax),
     highFrequencyHeapGrowthMin_(TuningDefaults::HighFrequencyHeapGrowthMin),
     lowFrequencyHeapGrowth_(TuningDefaults::LowFrequencyHeapGrowth),
     dynamicMarkSliceEnabled_(TuningDefaults::DynamicMarkSliceEnabled),
     minEmptyChunkCount_(TuningDefaults::MinEmptyChunkCount),
     maxEmptyChunkCount_(TuningDefaults::MaxEmptyChunkCount),
@@ -1616,18 +1618,18 @@ GCSchedulingTunables::resetParameter(JSG
     switch(key) {
       case JSGC_MAX_BYTES:
         gcMaxBytes_ = 0xffffffff;
         break;
       case JSGC_MAX_NURSERY_BYTES:
         gcMaxNurseryBytes_ = JS::DefaultNurseryBytes;
         break;
       case JSGC_HIGH_FREQUENCY_TIME_LIMIT:
-        highFrequencyThresholdUsec_ =
-            TuningDefaults::HighFrequencyThresholdUsec;
+        highFrequencyThreshold_ =
+            TuningDefaults::HighFrequencyThreshold;
         break;
       case JSGC_HIGH_FREQUENCY_LOW_LIMIT:
         setHighFrequencyLowLimit(TuningDefaults::HighFrequencyLowLimitBytes);
         break;
       case JSGC_HIGH_FREQUENCY_HIGH_LIMIT:
         setHighFrequencyHighLimit(TuningDefaults::HighFrequencyHighLimitBytes);
         break;
       case JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MAX:
@@ -1693,17 +1695,17 @@ GCRuntime::getParameter(JSGCParamKey key
         } else {
             MOZ_RELEASE_ASSERT(defaultTimeBudget_ >= 0);
             MOZ_RELEASE_ASSERT(defaultTimeBudget_ <= UINT32_MAX);
             return uint32_t(defaultTimeBudget_);
         }
       case JSGC_MARK_STACK_LIMIT:
         return marker.maxCapacity();
       case JSGC_HIGH_FREQUENCY_TIME_LIMIT:
-        return tunables.highFrequencyThresholdUsec() / PRMJ_USEC_PER_MSEC;
+        return tunables.highFrequencyThreshold().ToMilliseconds();
       case JSGC_HIGH_FREQUENCY_LOW_LIMIT:
         return tunables.highFrequencyLowLimitBytes() / 1024 / 1024;
       case JSGC_HIGH_FREQUENCY_HIGH_LIMIT:
         return tunables.highFrequencyHighLimitBytes() / 1024 / 1024;
       case JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MAX:
         return uint32_t(tunables.highFrequencyHeapGrowthMax() * 100);
       case JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MIN:
         return uint32_t(tunables.highFrequencyHeapGrowthMin() * 100);
@@ -2137,17 +2139,18 @@ GCRuntime::shouldCompact()
         return false;
 
     if (initialReason == JS::gcreason::USER_INACTIVE ||
         initialReason == JS::gcreason::MEM_PRESSURE)
     {
         return true;
     }
 
-    return !isIncremental || rt->lastAnimationTime + PRMJ_USEC_PER_SEC < PRMJ_Now();
+    const auto &lastAnimationTime = rt->lastAnimationTime.ref();
+    return !isIncremental || lastAnimationTime.IsNull() || lastAnimationTime + ONE_SECOND < mozilla::TimeStamp::Now();
 }
 
 bool
 GCRuntime::isCompactingGCEnabled() const
 {
     return compactingEnabled && rt->mainContextFromOwnThread()->compactingDisabledCount == 0;
 }
 
@@ -3243,41 +3246,43 @@ void
 ArenaLists::queueForegroundThingsForSweep()
 {
     gcShapeArenasToUpdate = arenaListsToSweep(AllocKind::SHAPE);
     gcAccessorShapeArenasToUpdate = arenaListsToSweep(AllocKind::ACCESSOR_SHAPE);
     gcObjectGroupArenasToUpdate = arenaListsToSweep(AllocKind::OBJECT_GROUP);
     gcScriptArenasToUpdate = arenaListsToSweep(AllocKind::SCRIPT);
 }
 
+const mozilla::TimeStamp SliceBudget::unlimitedDeadline = mozilla::TimeStamp::Now() + mozilla::TimeDuration::Forever();
+
 SliceBudget::SliceBudget()
   : timeBudget(UnlimitedTimeBudget), workBudget(UnlimitedWorkBudget)
 {
     makeUnlimited();
 }
 
 SliceBudget::SliceBudget(TimeBudget time)
   : timeBudget(time), workBudget(UnlimitedWorkBudget)
 {
     if (time.budget < 0) {
         makeUnlimited();
     } else {
         // Note: TimeBudget(0) is equivalent to WorkBudget(CounterReset).
-        deadline = PRMJ_Now() + time.budget * PRMJ_USEC_PER_MSEC;
+        deadline = mozilla::TimeStamp::Now() + mozilla::TimeDuration::FromMilliseconds(time.budget);
         counter = CounterReset;
     }
 }
 
 SliceBudget::SliceBudget(WorkBudget work)
   : timeBudget(UnlimitedTimeBudget), workBudget(work)
 {
     if (work.budget < 0) {
         makeUnlimited();
     } else {
-        deadline = 0;
+        deadline = mozilla::TimeStamp();
         counter = work.budget;
     }
 }
 
 int
 SliceBudget::describe(char* buffer, size_t maxlen) const
 {
     if (isUnlimited())
@@ -3286,17 +3291,20 @@ SliceBudget::describe(char* buffer, size
         return snprintf(buffer, maxlen, "work(%" PRId64 ")", workBudget.budget);
     else
         return snprintf(buffer, maxlen, "%" PRId64 "ms", timeBudget.budget);
 }
 
 bool
 SliceBudget::checkOverBudget()
 {
-    bool over = PRMJ_Now() >= deadline;
+    if (deadline.IsNull())
+        return true;
+
+    bool over = mozilla::TimeStamp::Now() >= deadline;
     if (!over)
         counter = CounterReset;
     return over;
 }
 
 void
 GCRuntime::requestMajorGC(JS::gcreason::Reason reason)
 {
@@ -4048,30 +4056,34 @@ GCRuntime::purgeRuntime()
     if (auto cache = rt->maybeThisRuntimeSharedImmutableStrings())
         cache->purge();
 
     MOZ_ASSERT(unmarkGrayStack.empty());
     unmarkGrayStack.clearAndFree();
 }
 
 bool
-GCRuntime::shouldPreserveJITCode(Realm* realm, int64_t currentTime,
+GCRuntime::shouldPreserveJITCode(Realm* realm, const mozilla::TimeStamp &currentTime,
                                  JS::gcreason::Reason reason, bool canAllocateMoreCode)
 {
+
     if (cleanUpEverything)
         return false;
     if (!canAllocateMoreCode)
         return false;
 
     if (alwaysPreserveCode)
         return true;
     if (realm->preserveJitCode())
         return true;
-    if (realm->lastAnimationTime + PRMJ_USEC_PER_SEC >= currentTime)
+
+    const auto &lastAnimationTime = realm->lastAnimationTime.ref();
+    if (!lastAnimationTime.IsNull() && lastAnimationTime + ONE_SECOND >= currentTime)
         return true;
+
     if (reason == JS::gcreason::DEBUG_GC)
         return true;
 
     return false;
 }
 
 #ifdef DEBUG
 class CompartmentCheckTracer : public JS::CallbackTracer
@@ -4235,17 +4247,17 @@ GCRuntime::prepareZonesForCollection(JS:
         for (auto i : AllAllocKinds())
             MOZ_ASSERT(!zone->arenas.arenaListsToSweep(i));
     }
 #endif
 
     *isFullOut = true;
     bool any = false;
 
-    int64_t currentTime = PRMJ_Now();
+    auto currentTime = mozilla::TimeStamp::Now();
 
     for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
         /* Set up which zones will be collected. */
         if (ShouldCollectZone(zone, reason)) {
             MOZ_ASSERT(zone->canCollect());
             any = true;
             zone->changeGCState(Zone::NoGC, Zone::Mark);
         } else {
@@ -6779,17 +6791,17 @@ GCRuntime::endCompactPhase()
 void
 GCRuntime::finishCollection()
 {
     assertBackgroundSweepingFinished();
     MOZ_ASSERT(marker.isDrained());
     marker.stop();
     clearBufferedGrayRoots();
 
-    uint64_t currentTime = PRMJ_Now();
+    auto currentTime = mozilla::TimeStamp::Now();
     schedulingState.updateHighFrequencyMode(lastGCTime, currentTime, tunables);
 
     for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
         if (zone->isCollecting()) {
             zone->changeGCState(Zone::Finished, Zone::NoGC);
             zone->notifyObservingDebuggers();
         }
 
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef gc_GCRuntime_h
 #define gc_GCRuntime_h
 
 #include "mozilla/Atomics.h"
 #include "mozilla/EnumSet.h"
 #include "mozilla/Maybe.h"
+#include "mozilla/TimeStamp.h"
 
 #include "gc/ArenaList.h"
 #include "gc/AtomMarking.h"
 #include "gc/GCMarker.h"
 #include "gc/GCParallelTask.h"
 #include "gc/Nursery.h"
 #include "gc/Scheduling.h"
 #include "gc/Statistics.h"
@@ -595,17 +596,17 @@ class GCRuntime
 
     friend class AutoCallGCCallbacks;
     void maybeCallGCCallback(JSGCStatus status);
 
     void pushZealSelectedObjects();
     void purgeRuntime();
     MOZ_MUST_USE bool beginMarkPhase(JS::gcreason::Reason reason, AutoGCSession& session);
     bool prepareZonesForCollection(JS::gcreason::Reason reason, bool* isFullOut);
-    bool shouldPreserveJITCode(JS::Realm* realm, int64_t currentTime,
+    bool shouldPreserveJITCode(JS::Realm* realm, const mozilla::TimeStamp &currentTime,
                                JS::gcreason::Reason reason, bool canAllocateMoreCode);
     void traceRuntimeForMajorGC(JSTracer* trc, AutoGCSession& session);
     void traceRuntimeAtoms(JSTracer* trc, const AutoAccessAtomsZone& atomsAccess);
     void traceKeptAtoms(JSTracer* trc);
     void traceRuntimeCommon(JSTracer* trc, TraceOrMarkRuntime traceOrMark);
     void maybeDoCycleCollection();
     void markCompartments();
     IncrementalProgress drainMarkStack(SliceBudget& sliceBudget, gcstats::PhaseKind phase);
@@ -743,17 +744,17 @@ class GCRuntime
     /*
      * Number of the committed arenas in all GC chunks including empty chunks.
      */
     mozilla::Atomic<uint32_t, mozilla::ReleaseAcquire> numArenasFreeCommitted;
     MainThreadData<VerifyPreTracer*> verifyPreData;
 
   private:
     UnprotectedData<bool> chunkAllocationSinceLastGC;
-    MainThreadData<int64_t> lastGCTime;
+    MainThreadData<mozilla::TimeStamp> lastGCTime;
 
     /*
      * JSGC_MODE
      * prefs: javascript.options.mem.gc_per_zone and
      *   javascript.options.mem.gc_incremental.
      */
     MainThreadData<JSGCMode> mode;
 
--- a/js/src/gc/Scheduling.h
+++ b/js/src/gc/Scheduling.h
@@ -384,19 +384,19 @@ class GCSchedulingTunables
      * tunables that make GC non-deterministic.
      */
     MainThreadData<bool> dynamicHeapGrowthEnabled_;
 
     /*
      * JSGC_HIGH_FREQUENCY_TIME_LIMIT
      *
      * We enter high-frequency mode if we GC a twice within this many
-     * microseconds. This value is stored directly in microseconds.
+     * microseconds.
      */
-    MainThreadData<uint64_t> highFrequencyThresholdUsec_;
+    MainThreadData<mozilla::TimeDuration> highFrequencyThreshold_;
 
     /*
      * JSGC_HIGH_FREQUENCY_LOW_LIMIT
      * JSGC_HIGH_FREQUENCY_HIGH_LIMIT
      * JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MAX
      * JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MIN
      *
      * When in the |highFrequencyGC| mode, these parameterize the per-zone
@@ -445,17 +445,17 @@ class GCSchedulingTunables
     size_t gcMaxBytes() const { return gcMaxBytes_; }
     size_t maxMallocBytes() const { return maxMallocBytes_; }
     size_t gcMaxNurseryBytes() const { return gcMaxNurseryBytes_; }
     size_t gcZoneAllocThresholdBase() const { return gcZoneAllocThresholdBase_; }
     double allocThresholdFactor() const { return allocThresholdFactor_; }
     double allocThresholdFactorAvoidInterrupt() const { return allocThresholdFactorAvoidInterrupt_; }
     size_t zoneAllocDelayBytes() const { return zoneAllocDelayBytes_; }
     bool isDynamicHeapGrowthEnabled() const { return dynamicHeapGrowthEnabled_; }
-    uint64_t highFrequencyThresholdUsec() const { return highFrequencyThresholdUsec_; }
+    const mozilla::TimeDuration &highFrequencyThreshold() const { return highFrequencyThreshold_; }
     uint64_t highFrequencyLowLimitBytes() const { return highFrequencyLowLimitBytes_; }
     uint64_t highFrequencyHighLimitBytes() const { return highFrequencyHighLimitBytes_; }
     double highFrequencyHeapGrowthMax() const { return highFrequencyHeapGrowthMax_; }
     double highFrequencyHeapGrowthMin() const { return highFrequencyHeapGrowthMin_; }
     double lowFrequencyHeapGrowth() const { return lowFrequencyHeapGrowth_; }
     bool isDynamicMarkSliceEnabled() const { return dynamicMarkSliceEnabled_; }
     unsigned minEmptyChunkCount(const AutoLockGC&) const { return minEmptyChunkCount_; }
     unsigned maxEmptyChunkCount() const { return maxEmptyChunkCount_; }
@@ -490,21 +490,21 @@ class GCSchedulingState
 
   public:
     GCSchedulingState()
       : inHighFrequencyGCMode_(false)
     {}
 
     bool inHighFrequencyGCMode() const { return inHighFrequencyGCMode_; }
 
-    void updateHighFrequencyMode(uint64_t lastGCTime, uint64_t currentTime,
+    void updateHighFrequencyMode(const mozilla::TimeStamp &lastGCTime, const mozilla::TimeStamp &currentTime,
                                  const GCSchedulingTunables& tunables) {
         inHighFrequencyGCMode_ =
-            tunables.isDynamicHeapGrowthEnabled() && lastGCTime &&
-            lastGCTime + tunables.highFrequencyThresholdUsec() > currentTime;
+            tunables.isDynamicHeapGrowthEnabled() && !lastGCTime.IsNull() &&
+            lastGCTime + tunables.highFrequencyThreshold() > currentTime;
     }
 };
 
 class MemoryCounter
 {
     // Bytes counter to measure memory pressure for GC scheduling. It counts
     // upwards from zero.
     mozilla::Atomic<size_t, mozilla::ReleaseAcquire> bytes_;
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -3,16 +3,17 @@
  * 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 "jsfriendapi.h"
 
 #include "mozilla/Atomics.h"
 #include "mozilla/PodOperations.h"
+#include "mozilla/TimeStamp.h"
 
 #include <stdint.h>
 
 #ifdef ENABLE_BIGINT
 #include "builtin/BigInt.h"
 #endif
 #include "builtin/Promise.h"
 #include "builtin/TestingFunctions.h"
@@ -415,17 +416,17 @@ js::AssertSameCompartment(JSObject* objA
 }
 #endif
 
 JS_FRIEND_API(void)
 js::NotifyAnimationActivity(JSObject* obj)
 {
     MOZ_ASSERT(obj->is<GlobalObject>());
 
-    int64_t timeNow = PRMJ_Now();
+    auto timeNow = mozilla::TimeStamp::Now();
     obj->as<GlobalObject>().realm()->lastAnimationTime = timeNow;
     obj->runtimeFromMainThread()->lastAnimationTime = timeNow;
 }
 
 JS_FRIEND_API(uint32_t)
 js::GetObjectSlotSpan(JSObject* obj)
 {
     return obj->as<NativeObject>().slotSpan();
--- a/js/src/vm/Realm.h
+++ b/js/src/vm/Realm.h
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef vm_Realm_h
 #define vm_Realm_h
 
 #include "mozilla/LinkedList.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/MemoryReporting.h"
+#include "mozilla/TimeStamp.h"
 #include "mozilla/Tuple.h"
 #include "mozilla/Variant.h"
 #include "mozilla/XorShift128PlusRNG.h"
 
 #include <stddef.h>
 
 #include "gc/Barrier.h"
 #include "gc/Zone.h"
@@ -417,17 +418,17 @@ class JS::Realm : public JS::shadow::Rea
 
     /*
      * Lazily initialized script source object to use for scripts cloned
      * from the self-hosting global.
      */
     js::ReadBarrieredScriptSourceObject selfHostingScriptSource { nullptr };
 
     // Last time at which an animation was played for this realm.
-    int64_t lastAnimationTime = 0;
+    js::MainThreadData<mozilla::TimeStamp> lastAnimationTime;
 
     /*
      * For generational GC, record whether a write barrier has added this
      * realm's global to the store buffer since the last minor GC.
      *
      * This is used to avoid calling into the VM every time a nursery object is
      * written to a property of the global.
      */
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -168,17 +168,16 @@ JSRuntime::JSRuntime(JSRuntime* parentRu
     parallelParsingEnabled_(true),
 #ifdef DEBUG
     offThreadParsesRunning_(0),
     offThreadParsingBlocked_(false),
 #endif
     autoWritableJitCodeActive_(false),
     oomCallback(nullptr),
     debuggerMallocSizeOf(ReturnZeroSize),
-    lastAnimationTime(0),
     performanceMonitoring_(),
     stackFormat_(parentRuntime ? js::StackFormat::Default
                                : js::StackFormat::SpiderMonkey),
     wasmInstances(mutexid::WasmRuntimeInstances),
     moduleResolveHook(),
     moduleMetadataHook()
 {
     JS_COUNT_CTOR(JSRuntime);
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -11,16 +11,17 @@
 #include "mozilla/Attributes.h"
 #include "mozilla/DoublyLinkedList.h"
 #include "mozilla/LinkedList.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/MaybeOneOf.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Scoped.h"
 #include "mozilla/ThreadLocal.h"
+#include "mozilla/TimeStamp.h"
 #include "mozilla/Vector.h"
 
 #include <algorithm>
 #include <setjmp.h>
 
 #include "builtin/AtomicsObject.h"
 #include "builtin/intl/SharedIntlData.h"
 #include "builtin/Promise.h"
@@ -929,17 +930,17 @@ struct JSRuntime : public js::MallocProv
 
     /*
      * Debugger.Memory functions like takeCensus use this embedding-provided
      * function to assess the size of malloc'd blocks of memory.
      */
     js::MainThreadData<mozilla::MallocSizeOf> debuggerMallocSizeOf;
 
     /* Last time at which an animation was played for this runtime. */
-    mozilla::Atomic<int64_t> lastAnimationTime;
+    js::MainThreadData<mozilla::TimeStamp> lastAnimationTime;
 
   private:
     js::MainThreadData<js::PerformanceMonitoring> performanceMonitoring_;
   public:
     js::PerformanceMonitoring& performanceMonitoring() { return performanceMonitoring_.ref(); }
 
   private:
     /* The stack format for the current runtime.  Only valid on non-child