Backed out 3 changesets (bug 1021114, bug 988486) for GC crashes on a CLOSED TREE.

Backed out changeset f56234ba7ec7 (bug 1021114)
Backed out changeset 14a4a9062253 (bug 988486)
Backed out changeset 03eccac81e15 (bug 988486)
This commit is contained in:
Ryan VanderMeulen 2014-06-07 00:03:17 -04:00
parent 2959413a50
commit 9c350ad8b0
27 changed files with 351 additions and 416 deletions

View File

@ -16,9 +16,6 @@
* The purpose of abstracting this as a separate class is to allow it to be
* wrapped in a JS::Heap<T> to correctly handle post-barriering of the JSObject
* pointer, when present.
*
* No implementation of rootKind() is provided, which prevents
* Root<nsXBLMaybeCompiled<UncompiledT>> from being used.
*/
template <class UncompiledT>
class nsXBLMaybeCompiled
@ -91,6 +88,11 @@ struct GCMethods<nsXBLMaybeCompiled<UncompiledT> >
static nsXBLMaybeCompiled<UncompiledT> initial() { return nsXBLMaybeCompiled<UncompiledT>(); }
/*
* No implementation of kind() is provided to prevent
* Root<nsXBLMaybeCompiled<UncompiledT>> from being used.
*/
static bool poisoned(nsXBLMaybeCompiled<UncompiledT> function)
{
return function.IsCompiled() && Base::poisoned(function.GetJSFunction());

View File

@ -375,7 +375,7 @@ ShrinkGCBuffers(JSRuntime *rt);
class JS_PUBLIC_API(AutoAssertOnGC)
{
#ifdef DEBUG
js::gc::GCRuntime *gc;
JSRuntime *runtime;
size_t gcNumber;
public:

View File

@ -171,6 +171,7 @@ IsPoisonedId(jsid iden)
template <> struct GCMethods<jsid>
{
static jsid initial() { return JSID_VOID; }
static ThingRootKind kind() { return THING_ROOT_ID; }
static bool poisoned(jsid id) { return IsPoisonedId(id); }
static bool needsPostBarrier(jsid id) { return false; }
#ifdef JSGC_GENERATIONAL

View File

@ -654,6 +654,7 @@ template <typename T>
struct GCMethods<T *>
{
static T *initial() { return nullptr; }
static ThingRootKind kind() { return RootKind<T *>::rootKind(); }
static bool poisoned(T *v) { return JS::IsPoisonedPtr(v); }
static bool needsPostBarrier(T *v) { return false; }
#ifdef JSGC_GENERATIONAL
@ -666,6 +667,7 @@ template <>
struct GCMethods<JSObject *>
{
static JSObject *initial() { return nullptr; }
static ThingRootKind kind() { return RootKind<JSObject *>::rootKind(); }
static bool poisoned(JSObject *v) { return JS::IsPoisonedPtr(v); }
static bool needsPostBarrier(JSObject *v) {
return v != nullptr && gc::IsInsideNursery(reinterpret_cast<gc::Cell *>(v));
@ -705,7 +707,7 @@ class MOZ_STACK_CLASS Rooted : public js::RootedBase<T>
template <typename CX>
void init(CX *cx) {
#ifdef JSGC_TRACK_EXACT_ROOTS
js::ThingRootKind kind = js::RootKind<T>::rootKind();
js::ThingRootKind kind = js::GCMethods<T>::kind();
this->stack = &cx->thingGCRooters[kind];
this->prev = *stack;
*stack = reinterpret_cast<Rooted<void*>*>(this);

View File

@ -1550,12 +1550,14 @@ namespace js {
template <> struct GCMethods<const JS::Value>
{
static JS::Value initial() { return JS::UndefinedValue(); }
static ThingRootKind kind() { return THING_ROOT_VALUE; }
static bool poisoned(const JS::Value &v) { return JS::IsPoisonedValue(v); }
};
template <> struct GCMethods<JS::Value>
{
static JS::Value initial() { return JS::UndefinedValue(); }
static ThingRootKind kind() { return THING_ROOT_VALUE; }
static bool poisoned(const JS::Value &v) { return JS::IsPoisonedValue(v); }
static bool needsPostBarrier(const JS::Value &v) {
return v.isObject() && gc::IsInsideNursery(reinterpret_cast<gc::Cell*>(&v.toObject()));

View File

@ -520,7 +520,7 @@ SelectForGC(JSContext *cx, unsigned argc, Value *vp)
for (unsigned i = 0; i < args.length(); i++) {
if (args[i].isObject()) {
if (!rt->gc.selectForMarking(&args[i].toObject()))
if (!rt->gc.selectedForMarking.append(&args[i].toObject()))
return false;
}
}
@ -599,7 +599,7 @@ DeterministicGC(JSContext *cx, unsigned argc, jsval *vp)
return false;
}
cx->runtime()->gc.setDeterministic(ToBoolean(args[0]));
gc::SetDeterministicGC(cx, ToBoolean(args[0]));
args.rval().setUndefined();
return true;
}
@ -641,7 +641,7 @@ ValidateGC(JSContext *cx, unsigned argc, jsval *vp)
return false;
}
cx->runtime()->gc.setValidate(ToBoolean(args[0]));
gc::SetValidateGC(cx, ToBoolean(args[0]));
args.rval().setUndefined();
return true;
}
@ -657,7 +657,7 @@ FullCompartmentChecks(JSContext *cx, unsigned argc, jsval *vp)
return false;
}
cx->runtime()->gc.setFullCompartmentChecks(ToBoolean(args[0]));
gc::SetFullCompartmentChecks(cx, ToBoolean(args[0]));
args.rval().setUndefined();
return true;
}

View File

@ -26,6 +26,23 @@
namespace js {
struct ScriptAndCounts
{
/* This structure is stored and marked from the JSRuntime. */
JSScript *script;
ScriptCounts scriptCounts;
PCCounts &getPCCounts(jsbytecode *pc) const {
return scriptCounts.pcCountsVector[script->pcToOffset(pc)];
}
jit::IonScriptCounts *getIonCounts() const {
return scriptCounts.ionCounts;
}
};
typedef Vector<ScriptAndCounts, 0, SystemAllocPolicy> ScriptAndCountsVector;
namespace gc {
typedef Vector<JS::Zone *, 4, SystemAllocPolicy> ZoneVector;
@ -96,10 +113,7 @@ class GCRuntime
bool init(uint32_t maxbytes);
void finish();
inline int zeal();
inline bool upcomingZealousGC();
inline bool needZealousGC();
void setGCZeal(uint8_t zeal, uint32_t frequency);
template <typename T> bool addRoot(T *rp, const char *name, JSGCRootType rootType);
void removeRoot(void *rp);
void setMarkStackLimit(size_t limit);
@ -119,21 +133,14 @@ class GCRuntime
JS::gcreason::Reason reason);
void gcSlice(JSGCInvocationKind gckind, JS::gcreason::Reason reason, int64_t millis);
void runDebugGC();
inline void poke();
void markRuntime(JSTracer *trc, bool useSavedRoots = false);
#ifdef JS_GC_ZEAL
const void *addressOfZealMode() { return &zealMode; }
void setZeal(uint8_t zeal, uint32_t frequency);
void setNextScheduled(uint32_t count);
void verifyPreBarriers();
void verifyPostBarriers();
void maybeVerifyPreBarriers(bool always);
void maybeVerifyPostBarriers(bool always);
bool selectForMarking(JSObject *object);
void clearSelectedForMarking();
void setDeterministic(bool enable);
#endif
public:
@ -194,13 +201,6 @@ class GCRuntime
JS_ASSERT(!isAllocAllowed());
--noGCOrAllocationCheck;
}
bool isInsideUnsafeRegion() { return inUnsafeRegion != 0; }
void enterUnsafeRegion() { ++inUnsafeRegion; }
void leaveUnsafeRegion() {
JS_ASSERT(inUnsafeRegion > 0);
--inUnsafeRegion;
}
#endif
void setAlwaysPreserveCode() { alwaysPreserveCode = true; }
@ -209,24 +209,6 @@ class GCRuntime
void disableGenerationalGC();
void enableGenerationalGC();
void setGrayRootsTracer(JSTraceDataOp traceOp, void *data);
bool addBlackRootsTracer(JSTraceDataOp traceOp, void *data);
void removeBlackRootsTracer(JSTraceDataOp traceOp, void *data);
void setMaxMallocBytes(size_t value);
void resetMallocBytes();
bool isTooMuchMalloc() const { return mallocBytes <= 0; }
void updateMallocCounter(JS::Zone *zone, size_t nbytes);
void onTooMuchMalloc();
void setGCCallback(JSGCCallback callback, void *data);
bool addFinalizeCallback(JSFinalizeCallback callback, void *data);
void removeFinalizeCallback(JSFinalizeCallback func);
JS::GCSliceCallback setSliceCallback(JS::GCSliceCallback callback);
void setValidate(bool enable);
void setFullCompartmentChecks(bool enable);
#ifdef JS_GC_ZEAL
void startVerifyPreBarriers();
bool endVerifyPreBarriers();
@ -242,7 +224,7 @@ class GCRuntime
inline bool wantBackgroundAllocation() const;
bool initZeal();
bool initGCZeal();
void requestInterrupt(JS::gcreason::Reason reason);
bool gcCycle(bool incremental, int64_t budget, JSGCInvocationKind gckind,
JS::gcreason::Reason reason);
@ -475,7 +457,7 @@ class GCRuntime
*/
unsigned objectsMarkedInDeadZones;
bool poked;
bool poke;
volatile js::HeapState heapState;
@ -484,7 +466,6 @@ class GCRuntime
js::gc::StoreBuffer storeBuffer;
#endif
private:
/*
* These options control the zealousness of the GC. The fundamental values
* are nextScheduled and gcDebugCompartmentGC. At every allocation,
@ -522,7 +503,10 @@ class GCRuntime
bool validate;
bool fullCompartmentChecks;
Callback<JSGCCallback> gcCallback;
JSGCCallback gcCallback;
void *gcCallbackData;
JS::GCSliceCallback sliceCallback;
CallbackVector<JSFinalizeCallback> finalizeCallbacks;
/*
@ -532,7 +516,7 @@ class GCRuntime
mozilla::Atomic<ptrdiff_t, mozilla::ReleaseAcquire> mallocBytes;
/*
* Whether a GC has been triggered as a result of mallocBytes falling
* Whether a GC has been triggered as a result of mallocBytes falling
* below zero.
*/
mozilla::Atomic<bool, mozilla::ReleaseAcquire> mallocGCTriggered;
@ -546,6 +530,18 @@ class GCRuntime
CallbackVector<JSTraceDataOp> blackRootTracers;
Callback<JSTraceDataOp> grayRootTracer;
/*
* The GC can only safely decommit memory when the page size of the
* running process matches the compiled arena size.
*/
size_t systemPageSize;
/* The OS allocation granularity may not match the page size. */
size_t systemAllocGranularity;
/* Strong references on scripts held for PCCount profiling API. */
js::ScriptAndCountsVector *scriptAndCountsVector;
#ifdef DEBUG
/*
* Some regions of code are hard for the static rooting hazard analysis to
@ -556,6 +552,7 @@ class GCRuntime
int inUnsafeRegion;
#endif
private:
/* Always preserve JIT code during GCs, for testing. */
bool alwaysPreserveCode;
@ -571,42 +568,11 @@ class GCRuntime
ConservativeGCData conservativeGC;
//friend class js::gc::Chunk; // todo: remove
friend class js::GCHelperState;
friend class js::gc::MarkingValidator;
};
#ifdef JS_GC_ZEAL
inline int
GCRuntime::zeal() {
return zealMode;
}
inline bool
GCRuntime::upcomingZealousGC() {
return nextScheduled == 1;
}
inline bool
GCRuntime::needZealousGC() {
if (nextScheduled > 0 && --nextScheduled == 0) {
if (zealMode == ZealAllocValue ||
zealMode == ZealGenerationalGCValue ||
(zealMode >= ZealIncrementalRootsThenFinish &&
zealMode <= ZealIncrementalMultipleSlices))
{
nextScheduled = zealFrequency;
}
return true;
}
return false;
}
#else
inline int GCRuntime::zeal() { return 0; }
inline bool GCRuntime::upcomingZealousGC() { return false; }
inline bool GCRuntime::needZealousGC() { return false; }
#endif
} /* namespace gc */
} /* namespace js */

View File

@ -107,7 +107,7 @@ js::Nursery::enable()
setCurrentChunk(0);
currentStart_ = position();
#ifdef JS_GC_ZEAL
if (runtime()->gcZeal() == ZealGenerationalGCValue)
if (runtime()->gc.zealMode == ZealGenerationalGCValue)
enterZealMode();
#endif
}
@ -129,7 +129,7 @@ js::Nursery::isEmpty() const
JS_ASSERT(runtime_);
if (!isEnabled())
return true;
JS_ASSERT_IF(runtime_->gcZeal() != ZealGenerationalGCValue, currentStart_ == start());
JS_ASSERT_IF(runtime_->gc.zealMode != ZealGenerationalGCValue, currentStart_ == start());
return position() == currentStart_;
}
@ -930,7 +930,7 @@ js::Nursery::sweep()
for (int i = 0; i < NumNurseryChunks; ++i)
initChunk(i);
if (runtime()->gcZeal() == ZealGenerationalGCValue) {
if (runtime()->gc.zealMode == ZealGenerationalGCValue) {
MOZ_ASSERT(numActiveChunks_ == NumNurseryChunks);
/* Only reset the alloc point when we are close to the end. */
@ -955,7 +955,7 @@ void
js::Nursery::growAllocableSpace()
{
#ifdef JS_GC_ZEAL
MOZ_ASSERT_IF(runtime()->gcZeal() == ZealGenerationalGCValue,
MOZ_ASSERT_IF(runtime()->gc.zealMode == ZealGenerationalGCValue,
numActiveChunks_ == NumNurseryChunks);
#endif
numActiveChunks_ = Min(numActiveChunks_ * 2, NumNurseryChunks);
@ -965,7 +965,7 @@ void
js::Nursery::shrinkAllocableSpace()
{
#ifdef JS_GC_ZEAL
if (runtime()->gcZeal() == ZealGenerationalGCValue)
if (runtime()->gc.zealMode == ZealGenerationalGCValue)
return;
#endif
numActiveChunks_ = Max(numActiveChunks_ - 1, 1);

View File

@ -730,8 +730,8 @@ js::gc::GCRuntime::markRuntime(JSTracer *trc, bool useSavedRoots)
MarkPersistentRootedChains(trc);
if (rt->scriptAndCountsVector) {
ScriptAndCountsVector &vec = *rt->scriptAndCountsVector;
if (scriptAndCountsVector) {
ScriptAndCountsVector &vec = *scriptAndCountsVector;
for (size_t i = 0; i < vec.length(); i++)
MarkScriptRoot(trc, &vec[i].script, "scriptAndCountsVector");
}

View File

@ -446,8 +446,7 @@ Statistics::Statistics(JSRuntime *rt)
compartmentCount(0),
nonincrementalReason(nullptr),
preBytes(0),
phaseNestingDepth(0),
sliceCallback(nullptr)
phaseNestingDepth(0)
{
PodArrayZero(phaseTotals);
PodArrayZero(counts);
@ -490,13 +489,6 @@ Statistics::~Statistics()
}
}
JS::GCSliceCallback
Statistics::setSliceCallback(JS::GCSliceCallback newCallback) {
JS::GCSliceCallback oldCallback = sliceCallback;
sliceCallback = newCallback;
return oldCallback;
}
void
Statistics::printStats()
{
@ -589,9 +581,9 @@ Statistics::beginSlice(int collectedCount, int zoneCount, int compartmentCount,
// Slice callbacks should only fire for the outermost level
if (++gcDepth == 1) {
bool wasFullGC = collectedCount == zoneCount;
if (sliceCallback)
(*sliceCallback)(runtime, first ? JS::GC_CYCLE_BEGIN : JS::GC_SLICE_BEGIN,
JS::GCDescription(!wasFullGC));
if (JS::GCSliceCallback cb = runtime->gc.sliceCallback)
(*cb)(runtime, first ? JS::GC_CYCLE_BEGIN : JS::GC_SLICE_BEGIN,
JS::GCDescription(!wasFullGC));
}
}
@ -613,9 +605,9 @@ Statistics::endSlice()
// Slice callbacks should only fire for the outermost level
if (--gcDepth == 0) {
bool wasFullGC = collectedCount == zoneCount;
if (sliceCallback)
(*sliceCallback)(runtime, last ? JS::GC_CYCLE_END : JS::GC_SLICE_END,
JS::GCDescription(!wasFullGC));
if (JS::GCSliceCallback cb = runtime->gc.sliceCallback)
(*cb)(runtime, last ? JS::GC_CYCLE_END : JS::GC_SLICE_END,
JS::GCDescription(!wasFullGC));
}
/* Do this after the slice callback since it uses these values. */

View File

@ -98,8 +98,6 @@ struct Statistics {
jschar *formatMessage();
jschar *formatJSON(uint64_t timestamp);
JS::GCSliceCallback setSliceCallback(JS::GCSliceCallback callback);
private:
JSRuntime *runtime;
@ -162,8 +160,6 @@ struct Statistics {
/* Sweep times for SCCs of compartments. */
Vector<int64_t, 0, SystemAllocPolicy> sccTimes;
JS::GCSliceCallback sliceCallback;
void beginGC();
void endGC();

View File

@ -68,7 +68,7 @@ CompileRuntime::addressOfLastCachedNativeIterator()
const void *
CompileRuntime::addressOfGCZeal()
{
return runtime()->gc.addressOfZealMode();
return &runtime()->gc.zealMode;
}
#endif

View File

@ -1615,13 +1615,20 @@ JS::RemoveScriptRootRT(JSRuntime *rt, JS::Heap<JSScript *> *rp)
JS_PUBLIC_API(bool)
JS_AddExtraGCRootsTracer(JSRuntime *rt, JSTraceDataOp traceOp, void *data)
{
return rt->gc.addBlackRootsTracer(traceOp, data);
AssertHeapIsIdle(rt);
return !!rt->gc.blackRootTracers.append(Callback<JSTraceDataOp>(traceOp, data));
}
JS_PUBLIC_API(void)
JS_RemoveExtraGCRootsTracer(JSRuntime *rt, JSTraceDataOp traceOp, void *data)
{
return rt->gc.removeBlackRootsTracer(traceOp, data);
for (size_t i = 0; i < rt->gc.blackRootTracers.length(); i++) {
Callback<JSTraceDataOp> *e = &rt->gc.blackRootTracers[i];
if (e->op == traceOp && e->data == data) {
rt->gc.blackRootTracers.erase(e);
break;
}
}
}
#ifdef DEBUG
@ -1892,21 +1899,28 @@ JS_PUBLIC_API(void)
JS_SetGCCallback(JSRuntime *rt, JSGCCallback cb, void *data)
{
AssertHeapIsIdle(rt);
rt->gc.setGCCallback(cb, data);
rt->gc.gcCallback = cb;
rt->gc.gcCallbackData = data;
}
JS_PUBLIC_API(bool)
JS_AddFinalizeCallback(JSRuntime *rt, JSFinalizeCallback cb, void *data)
{
AssertHeapIsIdle(rt);
return rt->gc.addFinalizeCallback(cb, data);
return rt->gc.finalizeCallbacks.append(Callback<JSFinalizeCallback>(cb, data));
}
JS_PUBLIC_API(void)
JS_RemoveFinalizeCallback(JSRuntime *rt, JSFinalizeCallback cb)
{
AssertHeapIsIdle(rt);
rt->gc.removeFinalizeCallback(cb);
for (Callback<JSFinalizeCallback> *p = rt->gc.finalizeCallbacks.begin();
p < rt->gc.finalizeCallbacks.end(); p++)
{
if (p->op == cb) {
rt->gc.finalizeCallbacks.erase(p);
break;
}
}
}
JS_PUBLIC_API(bool)
@ -1931,7 +1945,7 @@ JS_SetGCParameter(JSRuntime *rt, JSGCParamKey key, uint32_t value)
break;
}
case JSGC_MAX_MALLOC_BYTES:
rt->gc.setMaxMallocBytes(value);
rt->setGCMaxMallocBytes(value);
break;
case JSGC_SLICE_TIME_BUDGET:
rt->gc.sliceBudget = SliceBudget::TimeBudget(value);
@ -6206,13 +6220,13 @@ JS_AbortIfWrongThread(JSRuntime *rt)
JS_PUBLIC_API(void)
JS_SetGCZeal(JSContext *cx, uint8_t zeal, uint32_t frequency)
{
cx->runtime()->gc.setZeal(zeal, frequency);
SetGCZeal(cx->runtime(), zeal, frequency);
}
JS_PUBLIC_API(void)
JS_ScheduleGC(JSContext *cx, uint32_t count)
{
cx->runtime()->gc.setNextScheduled(count);
cx->runtime()->gc.nextScheduled = count;
}
#endif

View File

@ -3009,6 +3009,7 @@ namespace js {
template <>
struct GCMethods<JSPropertyDescriptor> {
static JSPropertyDescriptor initial() { return JSPropertyDescriptor(); }
static ThingRootKind kind() { return THING_ROOT_PROPERTY_DESCRIPTOR; }
static bool poisoned(const JSPropertyDescriptor &desc) {
return (desc.obj && JS::IsPoisonedPtr(desc.obj)) ||
(desc.attrs & JSPROP_GETTER && desc.getter && JS::IsPoisonedPtr(desc.getter)) ||

View File

@ -63,7 +63,8 @@ js::ForgetSourceHook(JSRuntime *rt)
JS_FRIEND_API(void)
JS_SetGrayGCRootsTracer(JSRuntime *rt, JSTraceDataOp traceOp, void *data)
{
rt->gc.setGrayRootsTracer(traceOp, data);
rt->gc.grayRootTracer.op = traceOp;
rt->gc.grayRootTracer.data = data;
}
JS_FRIEND_API(JSString *)
@ -863,7 +864,9 @@ js::IsContextRunningJS(JSContext *cx)
JS_FRIEND_API(JS::GCSliceCallback)
JS::SetGCSliceCallback(JSRuntime *rt, GCSliceCallback callback)
{
return rt->gc.setSliceCallback(callback);
JS::GCSliceCallback old = rt->gc.sliceCallback;
rt->gc.sliceCallback = callback;
return old;
}
JS_FRIEND_API(bool)
@ -1023,7 +1026,7 @@ JS::IncrementalValueBarrier(const Value &v)
JS_FRIEND_API(void)
JS::PokeGC(JSRuntime *rt)
{
rt->gc.poke();
rt->gc.poke = true;
}
JS_FRIEND_API(JSCompartment *)

View File

@ -1095,7 +1095,7 @@ GCRuntime::GCRuntime(JSRuntime *rt) :
generationalDisabled(0),
manipulatingDeadZones(false),
objectsMarkedInDeadZones(0),
poked(false),
poke(false),
heapState(Idle),
#ifdef JSGC_GENERATIONAL
nursery(rt),
@ -1110,8 +1110,11 @@ GCRuntime::GCRuntime(JSRuntime *rt) :
#endif
validate(true),
fullCompartmentChecks(false),
gcCallback(nullptr),
sliceCallback(nullptr),
mallocBytes(0),
mallocGCTriggered(false),
scriptAndCountsVector(nullptr),
#ifdef DEBUG
inUnsafeRegion(0),
#endif
@ -1127,8 +1130,14 @@ GCRuntime::GCRuntime(JSRuntime *rt) :
#ifdef JS_GC_ZEAL
extern void
js::SetGCZeal(JSRuntime *rt, uint8_t zeal, uint32_t frequency)
{
rt->gc.setGCZeal(zeal, frequency);
}
void
GCRuntime::setZeal(uint8_t zeal, uint32_t frequency)
GCRuntime::setGCZeal(uint8_t zeal, uint32_t frequency)
{
if (verifyPreData)
VerifyBarriers(rt, PreBarrierVerifier);
@ -1151,14 +1160,8 @@ GCRuntime::setZeal(uint8_t zeal, uint32_t frequency)
nextScheduled = schedule ? frequency : 0;
}
void
GCRuntime::setNextScheduled(uint32_t count)
{
nextScheduled = count;
}
bool
GCRuntime::initZeal()
GCRuntime::initGCZeal()
{
const char *env = getenv("JS_GC_ZEAL");
if (!env)
@ -1194,7 +1197,7 @@ GCRuntime::initZeal()
return false;
}
setZeal(zeal, frequency);
setGCZeal(zeal, frequency);
return true;
}
@ -1226,7 +1229,7 @@ GCRuntime::init(uint32_t maxbytes)
* for default backward API compatibility.
*/
maxBytes = maxbytes;
setMaxMallocBytes(maxbytes);
rt->setGCMaxMallocBytes(maxbytes);
#ifndef JS_MORE_DETERMINISTIC
jitReleaseTime = PRMJ_Now() + JIT_SCRIPT_RELEASE_TYPES_INTERVAL;
@ -1241,7 +1244,7 @@ GCRuntime::init(uint32_t maxbytes)
#endif
#ifdef JS_GC_ZEAL
if (!initZeal())
if (!initGCZeal())
return false;
#endif
@ -1326,63 +1329,6 @@ template <typename T> struct BarrierOwner {};
template <typename T> struct BarrierOwner<T *> { typedef T result; };
template <> struct BarrierOwner<Value> { typedef HeapValue result; };
bool
GCRuntime::addBlackRootsTracer(JSTraceDataOp traceOp, void *data)
{
AssertHeapIsIdle(rt);
return !!blackRootTracers.append(Callback<JSTraceDataOp>(traceOp, data));
}
void
GCRuntime::removeBlackRootsTracer(JSTraceDataOp traceOp, void *data)
{
// Can be called from finalizers
for (size_t i = 0; i < blackRootTracers.length(); i++) {
Callback<JSTraceDataOp> *e = &blackRootTracers[i];
if (e->op == traceOp && e->data == data) {
blackRootTracers.erase(e);
}
}
}
void
GCRuntime::setGrayRootsTracer(JSTraceDataOp traceOp, void *data)
{
AssertHeapIsIdle(rt);
grayRootTracer.op = traceOp;
grayRootTracer.data = data;
}
void
GCRuntime::setGCCallback(JSGCCallback callback, void *data)
{
gcCallback.op = callback;
gcCallback.data = data;
}
bool
GCRuntime::addFinalizeCallback(JSFinalizeCallback callback, void *data)
{
return finalizeCallbacks.append(Callback<JSFinalizeCallback>(callback, data));
}
void
GCRuntime::removeFinalizeCallback(JSFinalizeCallback callback)
{
for (Callback<JSFinalizeCallback> *p = finalizeCallbacks.begin();
p < finalizeCallbacks.end(); p++) {
if (p->op == callback) {
finalizeCallbacks.erase(p);
break;
}
}
}
JS::GCSliceCallback
GCRuntime::setSliceCallback(JS::GCSliceCallback callback) {
return stats.setSliceCallback(callback);
}
template <typename T>
bool
GCRuntime::addRoot(T *rp, const char *name, JSGCRootType rootType)
@ -1403,7 +1349,7 @@ void
GCRuntime::removeRoot(void *rp)
{
rootsHash.remove(rp);
poke();
poke = true;
}
template <typename T>
@ -1477,42 +1423,9 @@ js::RemoveRoot(JSRuntime *rt, void *rp)
rt->gc.removeRoot(rp);
}
void
GCRuntime::setMaxMallocBytes(size_t value)
{
/*
* For compatibility treat any value that exceeds PTRDIFF_T_MAX to
* mean that value.
*/
maxMallocBytes = (ptrdiff_t(value) >= 0) ? value : size_t(-1) >> 1;
resetMallocBytes();
for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next())
zone->setGCMaxMallocBytes(value);
}
void
GCRuntime::resetMallocBytes()
{
mallocBytes = ptrdiff_t(maxMallocBytes);
mallocGCTriggered = false;
}
void
GCRuntime::updateMallocCounter(JS::Zone *zone, size_t nbytes)
{
mallocBytes -= ptrdiff_t(nbytes);
if (MOZ_UNLIKELY(isTooMuchMalloc()))
onTooMuchMalloc();
else if (zone)
zone->updateMallocCounter(nbytes);
}
void
GCRuntime::onTooMuchMalloc()
{
if (!mallocGCTriggered)
mallocGCTriggered = triggerGC(JS::gcreason::TOO_MUCH_MALLOC);
}
typedef RootedValueMap::Range RootRange;
typedef RootedValueMap::Entry RootEntry;
typedef RootedValueMap::Enum RootEnum;
static size_t
ComputeTriggerBytes(Zone *zone, size_t lastBytes, size_t maxBytes, JSGCInvocationKind gckind)
@ -4441,7 +4354,7 @@ AutoGCSession::~AutoGCSession()
#ifdef JS_GC_ZEAL
/* Keeping these around after a GC is dangerous. */
gc->clearSelectedForMarking();
gc->selectedForMarking.clearAndFree();
#endif
/* Clear gcMallocBytes for all compartments */
@ -4450,7 +4363,7 @@ AutoGCSession::~AutoGCSession()
zone->unscheduleGC();
}
gc->resetMallocBytes();
gc->rt->resetGCMallocBytes();
}
AutoCopyFreeListToArenas::AutoCopyFreeListToArenas(JSRuntime *rt, ZoneSelector selector)
@ -4783,7 +4696,7 @@ GCRuntime::budgetIncrementalGC(int64_t *budget)
return;
}
if (isTooMuchMalloc()) {
if (rt->isTooMuchMalloc()) {
*budget = SliceBudget::Unlimited;
stats.nonincremental("malloc bytes trigger");
}
@ -4982,21 +4895,21 @@ GCRuntime::collect(bool incremental, int64_t budget, JSGCInvocationKind gckind,
*/
if (incrementalState == NO_INCREMENTAL) {
gcstats::AutoPhase ap(stats, gcstats::PHASE_GC_BEGIN);
if (gcCallback.op)
gcCallback.op(rt, JSGC_BEGIN, gcCallback.data);
if (gcCallback)
gcCallback(rt, JSGC_BEGIN, gcCallbackData);
}
poked = false;
poke = false;
bool wasReset = gcCycle(incremental, budget, gckind, reason);
if (incrementalState == NO_INCREMENTAL) {
gcstats::AutoPhase ap(stats, gcstats::PHASE_GC_END);
if (gcCallback.op)
gcCallback.op(rt, JSGC_END, gcCallback.data);
if (gcCallback)
gcCallback(rt, JSGC_END, gcCallbackData);
}
/* Need to re-schedule all zones for GC. */
if (poked && shouldCleanUpEverything)
if (poke && shouldCleanUpEverything)
JS::PrepareForFullGC(rt);
/*
@ -5022,7 +4935,7 @@ GCRuntime::collect(bool incremental, int64_t budget, JSGCInvocationKind gckind,
* case) until we can be sure that no additional garbage is created
* (which typically happens if roots are dropped during finalizers).
*/
repeat = (poked && shouldCleanUpEverything) || wasReset || repeatForDeadZone;
repeat = (poke && shouldCleanUpEverything) || wasReset || repeatForDeadZone;
} while (repeat);
if (incrementalState == NO_INCREMENTAL) {
@ -5382,40 +5295,27 @@ GCRuntime::runDebugGC()
}
void
GCRuntime::setValidate(bool enabled)
gc::SetDeterministicGC(JSContext *cx, bool enabled)
{
JS_ASSERT(!isHeapMajorCollecting());
validate = enabled;
}
void
GCRuntime::setFullCompartmentChecks(bool enabled)
{
JS_ASSERT(!isHeapMajorCollecting());
fullCompartmentChecks = enabled;
}
#ifdef JS_GC_ZEAL
bool
GCRuntime::selectForMarking(JSObject *object)
{
JS_ASSERT(!isHeapMajorCollecting());
return selectedForMarking.append(object);
}
void
GCRuntime::clearSelectedForMarking()
{
selectedForMarking.clearAndFree();
}
void
GCRuntime::setDeterministic(bool enabled)
{
JS_ASSERT(!isHeapMajorCollecting());
deterministicOnly = enabled;
}
JSRuntime *rt = cx->runtime();
rt->gc.deterministicOnly = enabled;
#endif
}
void
gc::SetValidateGC(JSContext *cx, bool enabled)
{
JSRuntime *rt = cx->runtime();
rt->gc.validate = enabled;
}
void
gc::SetFullCompartmentChecks(JSContext *cx, bool enabled)
{
JSRuntime *rt = cx->runtime();
rt->gc.fullCompartmentChecks = enabled;
}
#ifdef DEBUG
@ -5474,6 +5374,105 @@ js::ReleaseAllJITCode(FreeOp *fop)
#endif
}
/*
* There are three possible PCCount profiling states:
*
* 1. None: Neither scripts nor the runtime have count information.
* 2. Profile: Active scripts have count information, the runtime does not.
* 3. Query: Scripts do not have count information, the runtime does.
*
* When starting to profile scripts, counting begins immediately, with all JIT
* code discarded and recompiled with counts as necessary. Active interpreter
* frames will not begin profiling until they begin executing another script
* (via a call or return).
*
* The below API functions manage transitions to new states, according
* to the table below.
*
* Old State
* -------------------------
* Function None Profile Query
* --------
* StartPCCountProfiling Profile Profile Profile
* StopPCCountProfiling None Query Query
* PurgePCCounts None None None
*/
static void
ReleaseScriptCounts(FreeOp *fop)
{
JSRuntime *rt = fop->runtime();
JS_ASSERT(rt->gc.scriptAndCountsVector);
ScriptAndCountsVector &vec = *rt->gc.scriptAndCountsVector;
for (size_t i = 0; i < vec.length(); i++)
vec[i].scriptCounts.destroy(fop);
fop->delete_(rt->gc.scriptAndCountsVector);
rt->gc.scriptAndCountsVector = nullptr;
}
JS_FRIEND_API(void)
js::StartPCCountProfiling(JSContext *cx)
{
JSRuntime *rt = cx->runtime();
if (rt->profilingScripts)
return;
if (rt->gc.scriptAndCountsVector)
ReleaseScriptCounts(rt->defaultFreeOp());
ReleaseAllJITCode(rt->defaultFreeOp());
rt->profilingScripts = true;
}
JS_FRIEND_API(void)
js::StopPCCountProfiling(JSContext *cx)
{
JSRuntime *rt = cx->runtime();
if (!rt->profilingScripts)
return;
JS_ASSERT(!rt->gc.scriptAndCountsVector);
ReleaseAllJITCode(rt->defaultFreeOp());
ScriptAndCountsVector *vec = cx->new_<ScriptAndCountsVector>(SystemAllocPolicy());
if (!vec)
return;
for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) {
for (ZoneCellIter i(zone, FINALIZE_SCRIPT); !i.done(); i.next()) {
JSScript *script = i.get<JSScript>();
if (script->hasScriptCounts() && script->types) {
ScriptAndCounts sac;
sac.script = script;
sac.scriptCounts.set(script->releaseScriptCounts());
if (!vec->append(sac))
sac.scriptCounts.destroy(rt->defaultFreeOp());
}
}
}
rt->profilingScripts = false;
rt->gc.scriptAndCountsVector = vec;
}
JS_FRIEND_API(void)
js::PurgePCCounts(JSContext *cx)
{
JSRuntime *rt = cx->runtime();
if (!rt->gc.scriptAndCountsVector)
return;
JS_ASSERT(!rt->profilingScripts);
ReleaseScriptCounts(rt->defaultFreeOp());
}
void
js::PurgeJITCaches(Zone *zone)
{
@ -5602,11 +5601,13 @@ JS::AssertGCThingMustBeTenured(JSObject *obj)
JS_FRIEND_API(void)
js::gc::AssertGCThingHasType(js::gc::Cell *cell, JSGCTraceKind kind)
{
#ifdef DEBUG
JS_ASSERT(cell);
if (IsInsideNursery(cell))
JS_ASSERT(kind == JSTRACE_OBJECT);
else
JS_ASSERT(MapAllocToTraceKind(cell->tenuredGetAllocKind()) == kind);
#endif
}
JS_FRIEND_API(size_t)
@ -5621,7 +5622,7 @@ JS::GetGCNumber()
#ifdef DEBUG
JS::AutoAssertOnGC::AutoAssertOnGC()
: gc(nullptr), gcNumber(0)
: runtime(nullptr), gcNumber(0)
{
js::PerThreadData *data = js::TlsPerThreadData.get();
if (data) {
@ -5631,38 +5632,38 @@ JS::AutoAssertOnGC::AutoAssertOnGC()
* code that works from both threads, however. We also use this to
* annotate the off thread run loops.
*/
JSRuntime *runtime = data->runtimeIfOnOwnerThread();
runtime = data->runtimeIfOnOwnerThread();
if (runtime) {
gc = &runtime->gc;
gcNumber = gc->number;
gc->enterUnsafeRegion();
gcNumber = runtime->gc.number;
++runtime->gc.inUnsafeRegion;
}
}
}
JS::AutoAssertOnGC::AutoAssertOnGC(JSRuntime *rt)
: gc(&rt->gc), gcNumber(rt->gc.number)
: runtime(rt), gcNumber(rt->gc.number)
{
gc->enterUnsafeRegion();
++rt->gc.inUnsafeRegion;
}
JS::AutoAssertOnGC::~AutoAssertOnGC()
{
if (gc) {
gc->leaveUnsafeRegion();
if (runtime) {
--runtime->gc.inUnsafeRegion;
MOZ_ASSERT(runtime->gc.inUnsafeRegion >= 0);
/*
* The following backstop assertion should never fire: if we bumped the
* gcNumber, we should have asserted because inUnsafeRegion was true.
*/
MOZ_ASSERT(gcNumber == gc->number, "GC ran inside an AutoAssertOnGC scope.");
MOZ_ASSERT(gcNumber == runtime->gc.number, "GC ran inside an AutoAssertOnGC scope.");
}
}
/* static */ void
JS::AutoAssertOnGC::VerifyIsSafeToGC(JSRuntime *rt)
{
if (rt->gc.isInsideUnsafeRegion())
if (rt->gc.inUnsafeRegion > 0)
MOZ_CRASH("[AutoAssertOnGC] possible GC in GC-unsafe region");
}
#endif

View File

@ -900,6 +900,11 @@ MinorGC(JSRuntime *rt, JS::gcreason::Reason reason);
extern void
MinorGC(JSContext *cx, JS::gcreason::Reason reason);
#ifdef JS_GC_ZEAL
extern void
SetGCZeal(JSRuntime *rt, uint8_t zeal, uint32_t frequency);
#endif
/* Functions for managing cross compartment gray pointers. */
extern void
@ -1156,6 +1161,15 @@ GCIfNeeded(JSContext *cx);
void
RunDebugGC(JSContext *cx);
void
SetDeterministicGC(JSContext *cx, bool enabled);
void
SetValidateGC(JSContext *cx, bool enabled);
void
SetFullCompartmentChecks(JSContext *cx, bool enabled);
/* Wait for the background thread to finish sweeping if it is running. */
void
FinishBackgroundFinalize(JSRuntime *rt);

View File

@ -76,15 +76,15 @@ GetGCThingTraceKind(const void *thing)
return MapAllocToTraceKind(cell->tenuredGetAllocKind());
}
inline void
GCRuntime::poke()
static inline void
GCPoke(JSRuntime *rt)
{
poked = true;
rt->gc.poke = true;
#ifdef JS_GC_ZEAL
/* Schedule a GC to happen "soon" after a GC poke. */
if (zealMode == ZealPokeValue)
nextScheduled = 1;
if (rt->gcZeal() == js::gc::ZealPokeValue)
rt->gc.nextScheduled = 1;
#endif
}
@ -489,7 +489,7 @@ CheckAllocatorState(ThreadSafeContext *cx, AllocKind kind)
if (allowGC) {
#ifdef JS_GC_ZEAL
if (rt->gc.needZealousGC())
if (rt->needZealousGC())
js::gc::RunDebugGC(ncx);
#endif

View File

@ -71,12 +71,14 @@ struct RootKind<TaggedProto>
template <> struct GCMethods<const TaggedProto>
{
static TaggedProto initial() { return TaggedProto(); }
static ThingRootKind kind() { return THING_ROOT_OBJECT; }
static bool poisoned(const TaggedProto &v) { return IsPoisonedPtr(v.raw()); }
};
template <> struct GCMethods<TaggedProto>
{
static TaggedProto initial() { return TaggedProto(); }
static ThingRootKind kind() { return THING_ROOT_OBJECT; }
static bool poisoned(const TaggedProto &v) { return IsPoisonedPtr(v.raw()); }
};

View File

@ -1271,6 +1271,7 @@ template <>
struct GCMethods<const types::Type>
{
static types::Type initial() { return types::Type::UnknownType(); }
static ThingRootKind kind() { return THING_ROOT_TYPE; }
static bool poisoned(const types::Type &v) {
return (v.isTypeObject() && IsPoisonedPtr(v.typeObject()))
|| (v.isSingleObject() && IsPoisonedPtr(v.singleObject()));
@ -1281,6 +1282,7 @@ template <>
struct GCMethods<types::Type>
{
static types::Type initial() { return types::Type::UnknownType(); }
static ThingRootKind kind() { return THING_ROOT_TYPE; }
static bool poisoned(const types::Type &v) {
return (v.isTypeObject() && IsPoisonedPtr(v.typeObject()))
|| (v.isSingleObject() && IsPoisonedPtr(v.singleObject()));

View File

@ -5274,7 +5274,7 @@ baseops::DeleteGeneric(JSContext *cx, HandleObject obj, HandleId id, bool *succe
return CallJSDeletePropertyOp(cx, obj->getClass()->delProperty, obj, id, succeeded);
}
cx->runtime()->gc.poke();
GCPoke(cx->runtime());
if (IsImplicitDenseOrTypedArrayElement(shape)) {
if (obj->is<TypedArrayObject>()) {

View File

@ -1936,114 +1936,15 @@ js::IsValidBytecodeOffset(JSContext *cx, JSScript *script, size_t offset)
return false;
}
/*
* There are three possible PCCount profiling states:
*
* 1. None: Neither scripts nor the runtime have count information.
* 2. Profile: Active scripts have count information, the runtime does not.
* 3. Query: Scripts do not have count information, the runtime does.
*
* When starting to profile scripts, counting begins immediately, with all JIT
* code discarded and recompiled with counts as necessary. Active interpreter
* frames will not begin profiling until they begin executing another script
* (via a call or return).
*
* The below API functions manage transitions to new states, according
* to the table below.
*
* Old State
* -------------------------
* Function None Profile Query
* --------
* StartPCCountProfiling Profile Profile Profile
* StopPCCountProfiling None Query Query
* PurgePCCounts None None None
*/
static void
ReleaseScriptCounts(FreeOp *fop)
{
JSRuntime *rt = fop->runtime();
JS_ASSERT(rt->scriptAndCountsVector);
ScriptAndCountsVector &vec = *rt->scriptAndCountsVector;
for (size_t i = 0; i < vec.length(); i++)
vec[i].scriptCounts.destroy(fop);
fop->delete_(rt->scriptAndCountsVector);
rt->scriptAndCountsVector = nullptr;
}
JS_FRIEND_API(void)
js::StartPCCountProfiling(JSContext *cx)
{
JSRuntime *rt = cx->runtime();
if (rt->profilingScripts)
return;
if (rt->scriptAndCountsVector)
ReleaseScriptCounts(rt->defaultFreeOp());
ReleaseAllJITCode(rt->defaultFreeOp());
rt->profilingScripts = true;
}
JS_FRIEND_API(void)
js::StopPCCountProfiling(JSContext *cx)
{
JSRuntime *rt = cx->runtime();
if (!rt->profilingScripts)
return;
JS_ASSERT(!rt->scriptAndCountsVector);
ReleaseAllJITCode(rt->defaultFreeOp());
ScriptAndCountsVector *vec = cx->new_<ScriptAndCountsVector>(SystemAllocPolicy());
if (!vec)
return;
for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) {
for (ZoneCellIter i(zone, FINALIZE_SCRIPT); !i.done(); i.next()) {
JSScript *script = i.get<JSScript>();
if (script->hasScriptCounts() && script->types) {
ScriptAndCounts sac;
sac.script = script;
sac.scriptCounts.set(script->releaseScriptCounts());
if (!vec->append(sac))
sac.scriptCounts.destroy(rt->defaultFreeOp());
}
}
}
rt->profilingScripts = false;
rt->scriptAndCountsVector = vec;
}
JS_FRIEND_API(void)
js::PurgePCCounts(JSContext *cx)
{
JSRuntime *rt = cx->runtime();
if (!rt->scriptAndCountsVector)
return;
JS_ASSERT(!rt->profilingScripts);
ReleaseScriptCounts(rt->defaultFreeOp());
}
JS_FRIEND_API(size_t)
js::GetPCCountScriptCount(JSContext *cx)
{
JSRuntime *rt = cx->runtime();
if (!rt->scriptAndCountsVector)
if (!rt->gc.scriptAndCountsVector)
return 0;
return rt->scriptAndCountsVector->length();
return rt->gc.scriptAndCountsVector->length();
}
enum MaybeComma {NO_COMMA, COMMA};
@ -2078,12 +1979,12 @@ js::GetPCCountScriptSummary(JSContext *cx, size_t index)
{
JSRuntime *rt = cx->runtime();
if (!rt->scriptAndCountsVector || index >= rt->scriptAndCountsVector->length()) {
if (!rt->gc.scriptAndCountsVector || index >= rt->gc.scriptAndCountsVector->length()) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BUFFER_TOO_SMALL);
return nullptr;
}
const ScriptAndCounts &sac = (*rt->scriptAndCountsVector)[index];
const ScriptAndCounts &sac = (*rt->gc.scriptAndCountsVector)[index];
RootedScript script(cx, sac.script);
/*
@ -2338,12 +2239,12 @@ js::GetPCCountScriptContents(JSContext *cx, size_t index)
{
JSRuntime *rt = cx->runtime();
if (!rt->scriptAndCountsVector || index >= rt->scriptAndCountsVector->length()) {
if (!rt->gc.scriptAndCountsVector || index >= rt->gc.scriptAndCountsVector->length()) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BUFFER_TOO_SMALL);
return nullptr;
}
const ScriptAndCounts &sac = (*rt->scriptAndCountsVector)[index];
const ScriptAndCounts &sac = (*rt->gc.scriptAndCountsVector)[index];
JSScript *script = sac.script;
StringBuffer buf(cx);

View File

@ -273,6 +273,7 @@ class Bindings
template <>
struct GCMethods<Bindings> {
static Bindings initial();
static ThingRootKind kind() { return THING_ROOT_BINDINGS; }
static bool poisoned(const Bindings &bindings) {
return IsPoisonedPtr(static_cast<Shape *>(bindings.callObjShape()));
}
@ -1927,21 +1928,6 @@ SweepScriptData(JSRuntime *rt);
extern void
FreeScriptData(JSRuntime *rt);
struct ScriptAndCounts
{
/* This structure is stored and marked from the JSRuntime. */
JSScript *script;
ScriptCounts scriptCounts;
PCCounts &getPCCounts(jsbytecode *pc) const {
return scriptCounts.pcCountsVector[script->pcToOffset(pc)];
}
jit::IonScriptCounts *getIonCounts() const {
return scriptCounts.ionCounts;
}
};
struct GSNCache;
jssrcnote *

View File

@ -336,6 +336,7 @@ namespace js {
template <>
struct GCMethods<PropDesc> {
static PropDesc initial() { return PropDesc(); }
static ThingRootKind kind() { return THING_ROOT_PROP_DESC; }
static bool poisoned(const PropDesc &desc) {
return JS::IsPoisonedPtr(desc.descObj_) ||
(desc.value_.isGCThing() &&

View File

@ -56,7 +56,7 @@ NewObjectCache::newObjectFromHit(JSContext *cx, EntryIndex entry_, js::gc::Initi
if (type->shouldPreTenure())
heap = gc::TenuredHeap;
if (cx->runtime()->gc.upcomingZealousGC())
if (cx->runtime()->upcomingZealousGC())
return nullptr;
// Trigger an identical allocation to the one that notified us of OOM

View File

@ -181,7 +181,6 @@ JSRuntime::JSRuntime(JSRuntime *parentRuntime)
#if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR)
simulatorRuntime_(nullptr),
#endif
scriptAndCountsVector(nullptr),
NaNValue(DoubleNaNValue()),
negativeInfinityValue(DoubleValue(NegativeInfinity<double>())),
positiveInfinityValue(DoubleValue(PositiveInfinity<double>())),
@ -695,6 +694,19 @@ JSRuntime::triggerActivityCallback(bool active)
activityCallback(activityCallbackArg, active);
}
void
JSRuntime::setGCMaxMallocBytes(size_t value)
{
/*
* For compatibility treat any value that exceeds PTRDIFF_T_MAX to
* mean that value.
*/
gc.maxMallocBytes = (ptrdiff_t(value) >= 0) ? value : size_t(-1) >> 1;
resetGCMallocBytes();
for (ZonesIter zone(this, WithAtoms); !zone.done(); zone.next())
zone->setGCMaxMallocBytes(value);
}
void
JSRuntime::updateMallocCounter(size_t nbytes)
{
@ -704,7 +716,12 @@ JSRuntime::updateMallocCounter(size_t nbytes)
void
JSRuntime::updateMallocCounter(JS::Zone *zone, size_t nbytes)
{
gc.updateMallocCounter(zone, nbytes);
/* We tolerate any thread races when updating gcMallocBytes. */
gc.mallocBytes -= ptrdiff_t(nbytes);
if (MOZ_UNLIKELY(gc.mallocBytes <= 0))
onTooMuchMalloc();
else if (zone)
zone->updateMallocCounter(nbytes);
}
JS_FRIEND_API(void)
@ -712,7 +729,9 @@ JSRuntime::onTooMuchMalloc()
{
if (!CurrentThreadCanAccessRuntime(this))
return;
gc.onTooMuchMalloc();
if (!gc.mallocGCTriggered)
gc.mallocGCTriggered = TriggerGC(this, JS::gcreason::TOO_MUCH_MALLOC);
}
JS_FRIEND_API(void *)

View File

@ -134,8 +134,6 @@ struct ScopeCoordinateNameCache {
void purge();
};
typedef Vector<ScriptAndCounts, 0, SystemAllocPolicy> ScriptAndCountsVector;
struct EvalCacheEntry
{
JSScript *script;
@ -955,7 +953,31 @@ struct JSRuntime : public JS::shadow::Runtime,
bool isHeapMinorCollecting() { return gc.isHeapMinorCollecting(); }
bool isHeapCollecting() { return gc.isHeapCollecting(); }
int gcZeal() { return gc.zeal(); }
#ifdef JS_GC_ZEAL
int gcZeal() { return gc.zealMode; }
bool upcomingZealousGC() {
return gc.nextScheduled == 1;
}
bool needZealousGC() {
if (gc.nextScheduled > 0 && --gc.nextScheduled == 0) {
if (gcZeal() == js::gc::ZealAllocValue ||
gcZeal() == js::gc::ZealGenerationalGCValue ||
(gcZeal() >= js::gc::ZealIncrementalRootsThenFinish &&
gcZeal() <= js::gc::ZealIncrementalMultipleSlices))
{
gc.nextScheduled = gc.zealFrequency;
}
return true;
}
return false;
}
#else
int gcZeal() { return 0; }
bool upcomingZealousGC() { return false; }
bool needZealousGC() { return false; }
#endif
void lockGC() {
assertCanLock(js::GCLock);
@ -980,9 +1002,6 @@ struct JSRuntime : public JS::shadow::Runtime,
void setSimulatorRuntime(js::jit::SimulatorRuntime *srt);
#endif
/* Strong references on scripts held for PCCount profiling API. */
js::ScriptAndCountsVector *scriptAndCountsVector;
/* Well-known numbers held for use by this runtime's contexts. */
const js::Value NaNValue;
const js::Value negativeInfinityValue;
@ -1257,6 +1276,13 @@ struct JSRuntime : public JS::shadow::Runtime,
JSRuntime *thisFromCtor() { return this; }
void setGCMaxMallocBytes(size_t value);
void resetGCMallocBytes() {
gc.mallocBytes = ptrdiff_t(gc.maxMallocBytes);
gc.mallocGCTriggered = false;
}
/*
* Call this after allocating memory held by GC things, to update memory
* pressure counters or report the OOM error if necessary. If oomError and
@ -1270,6 +1296,10 @@ struct JSRuntime : public JS::shadow::Runtime,
void reportAllocationOverflow() { js_ReportAllocationOverflow(nullptr); }
bool isTooMuchMalloc() const {
return gc.mallocBytes <= 0;
}
/*
* The function must be called outside the GC lock.
*/