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. There two uses of it, which seems to not harm the code,
but I am not entirily sure.
Another issue is that TimeStamp class does allow some operations on a
Null value, with assertions on debug builds.
MozReview-Commit-ID: CGZY7OL3pjs
--- 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 HighFrequencyThresholdUsec = mozilla::TimeDuration::FromMicroseconds(1000000);
/* 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;
+ highFrequencyThresholdUsec_ = 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;
}
@@ -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.highFrequencyThresholdUsec().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 ¤tTime,
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 {
@@ -6788,17 +6800,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 ¤tTime,
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> highFrequencyThresholdUsec_;
/*
* 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 &highFrequencyThresholdUsec() const { return highFrequencyThresholdUsec_; }
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,20 +490,20 @@ 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 ¤tTime,
const GCSchedulingTunables& tunables) {
inHighFrequencyGCMode_ =
- tunables.isDynamicHeapGrowthEnabled() && lastGCTime &&
+ tunables.isDynamicHeapGrowthEnabled() && !lastGCTime.IsNull() &&
lastGCTime + tunables.highFrequencyThresholdUsec() > currentTime;
}
};
class MemoryCounter
{
// Bytes counter to measure memory pressure for GC scheduling. It counts
// upwards from zero.
--- 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