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 * 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 * wrapped in a JS::Heap<T> to correctly handle post-barriering of the JSObject
* pointer, when present. * pointer, when present.
*
* No implementation of rootKind() is provided, which prevents
* Root<nsXBLMaybeCompiled<UncompiledT>> from being used.
*/ */
template <class UncompiledT> template <class UncompiledT>
class nsXBLMaybeCompiled class nsXBLMaybeCompiled
@ -91,6 +88,11 @@ struct GCMethods<nsXBLMaybeCompiled<UncompiledT> >
static nsXBLMaybeCompiled<UncompiledT> initial() { return 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) static bool poisoned(nsXBLMaybeCompiled<UncompiledT> function)
{ {
return function.IsCompiled() && Base::poisoned(function.GetJSFunction()); return function.IsCompiled() && Base::poisoned(function.GetJSFunction());

View File

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

View File

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

View File

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

View File

@ -1550,12 +1550,14 @@ namespace js {
template <> struct GCMethods<const JS::Value> template <> struct GCMethods<const JS::Value>
{ {
static JS::Value initial() { return JS::UndefinedValue(); } 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 poisoned(const JS::Value &v) { return JS::IsPoisonedValue(v); }
}; };
template <> struct GCMethods<JS::Value> template <> struct GCMethods<JS::Value>
{ {
static JS::Value initial() { return JS::UndefinedValue(); } 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 poisoned(const JS::Value &v) { return JS::IsPoisonedValue(v); }
static bool needsPostBarrier(const JS::Value &v) { static bool needsPostBarrier(const JS::Value &v) {
return v.isObject() && gc::IsInsideNursery(reinterpret_cast<gc::Cell*>(&v.toObject())); 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++) { for (unsigned i = 0; i < args.length(); i++) {
if (args[i].isObject()) { if (args[i].isObject()) {
if (!rt->gc.selectForMarking(&args[i].toObject())) if (!rt->gc.selectedForMarking.append(&args[i].toObject()))
return false; return false;
} }
} }
@ -599,7 +599,7 @@ DeterministicGC(JSContext *cx, unsigned argc, jsval *vp)
return false; return false;
} }
cx->runtime()->gc.setDeterministic(ToBoolean(args[0])); gc::SetDeterministicGC(cx, ToBoolean(args[0]));
args.rval().setUndefined(); args.rval().setUndefined();
return true; return true;
} }
@ -641,7 +641,7 @@ ValidateGC(JSContext *cx, unsigned argc, jsval *vp)
return false; return false;
} }
cx->runtime()->gc.setValidate(ToBoolean(args[0])); gc::SetValidateGC(cx, ToBoolean(args[0]));
args.rval().setUndefined(); args.rval().setUndefined();
return true; return true;
} }
@ -657,7 +657,7 @@ FullCompartmentChecks(JSContext *cx, unsigned argc, jsval *vp)
return false; return false;
} }
cx->runtime()->gc.setFullCompartmentChecks(ToBoolean(args[0])); gc::SetFullCompartmentChecks(cx, ToBoolean(args[0]));
args.rval().setUndefined(); args.rval().setUndefined();
return true; return true;
} }

View File

@ -26,6 +26,23 @@
namespace js { 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 { namespace gc {
typedef Vector<JS::Zone *, 4, SystemAllocPolicy> ZoneVector; typedef Vector<JS::Zone *, 4, SystemAllocPolicy> ZoneVector;
@ -96,10 +113,7 @@ class GCRuntime
bool init(uint32_t maxbytes); bool init(uint32_t maxbytes);
void finish(); void finish();
inline int zeal(); void setGCZeal(uint8_t zeal, uint32_t frequency);
inline bool upcomingZealousGC();
inline bool needZealousGC();
template <typename T> bool addRoot(T *rp, const char *name, JSGCRootType rootType); template <typename T> bool addRoot(T *rp, const char *name, JSGCRootType rootType);
void removeRoot(void *rp); void removeRoot(void *rp);
void setMarkStackLimit(size_t limit); void setMarkStackLimit(size_t limit);
@ -119,21 +133,14 @@ class GCRuntime
JS::gcreason::Reason reason); JS::gcreason::Reason reason);
void gcSlice(JSGCInvocationKind gckind, JS::gcreason::Reason reason, int64_t millis); void gcSlice(JSGCInvocationKind gckind, JS::gcreason::Reason reason, int64_t millis);
void runDebugGC(); void runDebugGC();
inline void poke();
void markRuntime(JSTracer *trc, bool useSavedRoots = false); void markRuntime(JSTracer *trc, bool useSavedRoots = false);
#ifdef JS_GC_ZEAL #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 verifyPreBarriers();
void verifyPostBarriers(); void verifyPostBarriers();
void maybeVerifyPreBarriers(bool always); void maybeVerifyPreBarriers(bool always);
void maybeVerifyPostBarriers(bool always); void maybeVerifyPostBarriers(bool always);
bool selectForMarking(JSObject *object);
void clearSelectedForMarking();
void setDeterministic(bool enable);
#endif #endif
public: public:
@ -194,13 +201,6 @@ class GCRuntime
JS_ASSERT(!isAllocAllowed()); JS_ASSERT(!isAllocAllowed());
--noGCOrAllocationCheck; --noGCOrAllocationCheck;
} }
bool isInsideUnsafeRegion() { return inUnsafeRegion != 0; }
void enterUnsafeRegion() { ++inUnsafeRegion; }
void leaveUnsafeRegion() {
JS_ASSERT(inUnsafeRegion > 0);
--inUnsafeRegion;
}
#endif #endif
void setAlwaysPreserveCode() { alwaysPreserveCode = true; } void setAlwaysPreserveCode() { alwaysPreserveCode = true; }
@ -209,24 +209,6 @@ class GCRuntime
void disableGenerationalGC(); void disableGenerationalGC();
void enableGenerationalGC(); 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 #ifdef JS_GC_ZEAL
void startVerifyPreBarriers(); void startVerifyPreBarriers();
bool endVerifyPreBarriers(); bool endVerifyPreBarriers();
@ -242,7 +224,7 @@ class GCRuntime
inline bool wantBackgroundAllocation() const; inline bool wantBackgroundAllocation() const;
bool initZeal(); bool initGCZeal();
void requestInterrupt(JS::gcreason::Reason reason); void requestInterrupt(JS::gcreason::Reason reason);
bool gcCycle(bool incremental, int64_t budget, JSGCInvocationKind gckind, bool gcCycle(bool incremental, int64_t budget, JSGCInvocationKind gckind,
JS::gcreason::Reason reason); JS::gcreason::Reason reason);
@ -475,7 +457,7 @@ class GCRuntime
*/ */
unsigned objectsMarkedInDeadZones; unsigned objectsMarkedInDeadZones;
bool poked; bool poke;
volatile js::HeapState heapState; volatile js::HeapState heapState;
@ -484,7 +466,6 @@ class GCRuntime
js::gc::StoreBuffer storeBuffer; js::gc::StoreBuffer storeBuffer;
#endif #endif
private:
/* /*
* These options control the zealousness of the GC. The fundamental values * These options control the zealousness of the GC. The fundamental values
* are nextScheduled and gcDebugCompartmentGC. At every allocation, * are nextScheduled and gcDebugCompartmentGC. At every allocation,
@ -522,7 +503,10 @@ class GCRuntime
bool validate; bool validate;
bool fullCompartmentChecks; bool fullCompartmentChecks;
Callback<JSGCCallback> gcCallback; JSGCCallback gcCallback;
void *gcCallbackData;
JS::GCSliceCallback sliceCallback;
CallbackVector<JSFinalizeCallback> finalizeCallbacks; CallbackVector<JSFinalizeCallback> finalizeCallbacks;
/* /*
@ -532,7 +516,7 @@ class GCRuntime
mozilla::Atomic<ptrdiff_t, mozilla::ReleaseAcquire> mallocBytes; 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. * below zero.
*/ */
mozilla::Atomic<bool, mozilla::ReleaseAcquire> mallocGCTriggered; mozilla::Atomic<bool, mozilla::ReleaseAcquire> mallocGCTriggered;
@ -546,6 +530,18 @@ class GCRuntime
CallbackVector<JSTraceDataOp> blackRootTracers; CallbackVector<JSTraceDataOp> blackRootTracers;
Callback<JSTraceDataOp> grayRootTracer; 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 #ifdef DEBUG
/* /*
* Some regions of code are hard for the static rooting hazard analysis to * Some regions of code are hard for the static rooting hazard analysis to
@ -556,6 +552,7 @@ class GCRuntime
int inUnsafeRegion; int inUnsafeRegion;
#endif #endif
private:
/* Always preserve JIT code during GCs, for testing. */ /* Always preserve JIT code during GCs, for testing. */
bool alwaysPreserveCode; bool alwaysPreserveCode;
@ -571,42 +568,11 @@ class GCRuntime
ConservativeGCData conservativeGC; ConservativeGCData conservativeGC;
//friend class js::gc::Chunk; // todo: remove
friend class js::GCHelperState; friend class js::GCHelperState;
friend class js::gc::MarkingValidator; 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 gc */
} /* namespace js */ } /* namespace js */

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -3009,6 +3009,7 @@ namespace js {
template <> template <>
struct GCMethods<JSPropertyDescriptor> { struct GCMethods<JSPropertyDescriptor> {
static JSPropertyDescriptor initial() { return JSPropertyDescriptor(); } static JSPropertyDescriptor initial() { return JSPropertyDescriptor(); }
static ThingRootKind kind() { return THING_ROOT_PROPERTY_DESCRIPTOR; }
static bool poisoned(const JSPropertyDescriptor &desc) { static bool poisoned(const JSPropertyDescriptor &desc) {
return (desc.obj && JS::IsPoisonedPtr(desc.obj)) || return (desc.obj && JS::IsPoisonedPtr(desc.obj)) ||
(desc.attrs & JSPROP_GETTER && desc.getter && JS::IsPoisonedPtr(desc.getter)) || (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_FRIEND_API(void)
JS_SetGrayGCRootsTracer(JSRuntime *rt, JSTraceDataOp traceOp, void *data) 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 *) JS_FRIEND_API(JSString *)
@ -863,7 +864,9 @@ js::IsContextRunningJS(JSContext *cx)
JS_FRIEND_API(JS::GCSliceCallback) JS_FRIEND_API(JS::GCSliceCallback)
JS::SetGCSliceCallback(JSRuntime *rt, GCSliceCallback callback) 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) JS_FRIEND_API(bool)
@ -1023,7 +1026,7 @@ JS::IncrementalValueBarrier(const Value &v)
JS_FRIEND_API(void) JS_FRIEND_API(void)
JS::PokeGC(JSRuntime *rt) JS::PokeGC(JSRuntime *rt)
{ {
rt->gc.poke(); rt->gc.poke = true;
} }
JS_FRIEND_API(JSCompartment *) JS_FRIEND_API(JSCompartment *)

View File

@ -1095,7 +1095,7 @@ GCRuntime::GCRuntime(JSRuntime *rt) :
generationalDisabled(0), generationalDisabled(0),
manipulatingDeadZones(false), manipulatingDeadZones(false),
objectsMarkedInDeadZones(0), objectsMarkedInDeadZones(0),
poked(false), poke(false),
heapState(Idle), heapState(Idle),
#ifdef JSGC_GENERATIONAL #ifdef JSGC_GENERATIONAL
nursery(rt), nursery(rt),
@ -1110,8 +1110,11 @@ GCRuntime::GCRuntime(JSRuntime *rt) :
#endif #endif
validate(true), validate(true),
fullCompartmentChecks(false), fullCompartmentChecks(false),
gcCallback(nullptr),
sliceCallback(nullptr),
mallocBytes(0), mallocBytes(0),
mallocGCTriggered(false), mallocGCTriggered(false),
scriptAndCountsVector(nullptr),
#ifdef DEBUG #ifdef DEBUG
inUnsafeRegion(0), inUnsafeRegion(0),
#endif #endif
@ -1127,8 +1130,14 @@ GCRuntime::GCRuntime(JSRuntime *rt) :
#ifdef JS_GC_ZEAL #ifdef JS_GC_ZEAL
extern void
js::SetGCZeal(JSRuntime *rt, uint8_t zeal, uint32_t frequency)
{
rt->gc.setGCZeal(zeal, frequency);
}
void void
GCRuntime::setZeal(uint8_t zeal, uint32_t frequency) GCRuntime::setGCZeal(uint8_t zeal, uint32_t frequency)
{ {
if (verifyPreData) if (verifyPreData)
VerifyBarriers(rt, PreBarrierVerifier); VerifyBarriers(rt, PreBarrierVerifier);
@ -1151,14 +1160,8 @@ GCRuntime::setZeal(uint8_t zeal, uint32_t frequency)
nextScheduled = schedule ? frequency : 0; nextScheduled = schedule ? frequency : 0;
} }
void
GCRuntime::setNextScheduled(uint32_t count)
{
nextScheduled = count;
}
bool bool
GCRuntime::initZeal() GCRuntime::initGCZeal()
{ {
const char *env = getenv("JS_GC_ZEAL"); const char *env = getenv("JS_GC_ZEAL");
if (!env) if (!env)
@ -1194,7 +1197,7 @@ GCRuntime::initZeal()
return false; return false;
} }
setZeal(zeal, frequency); setGCZeal(zeal, frequency);
return true; return true;
} }
@ -1226,7 +1229,7 @@ GCRuntime::init(uint32_t maxbytes)
* for default backward API compatibility. * for default backward API compatibility.
*/ */
maxBytes = maxbytes; maxBytes = maxbytes;
setMaxMallocBytes(maxbytes); rt->setGCMaxMallocBytes(maxbytes);
#ifndef JS_MORE_DETERMINISTIC #ifndef JS_MORE_DETERMINISTIC
jitReleaseTime = PRMJ_Now() + JIT_SCRIPT_RELEASE_TYPES_INTERVAL; jitReleaseTime = PRMJ_Now() + JIT_SCRIPT_RELEASE_TYPES_INTERVAL;
@ -1241,7 +1244,7 @@ GCRuntime::init(uint32_t maxbytes)
#endif #endif
#ifdef JS_GC_ZEAL #ifdef JS_GC_ZEAL
if (!initZeal()) if (!initGCZeal())
return false; return false;
#endif #endif
@ -1326,63 +1329,6 @@ template <typename T> struct BarrierOwner {};
template <typename T> struct BarrierOwner<T *> { typedef T result; }; template <typename T> struct BarrierOwner<T *> { typedef T result; };
template <> struct BarrierOwner<Value> { typedef HeapValue 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> template <typename T>
bool bool
GCRuntime::addRoot(T *rp, const char *name, JSGCRootType rootType) GCRuntime::addRoot(T *rp, const char *name, JSGCRootType rootType)
@ -1403,7 +1349,7 @@ void
GCRuntime::removeRoot(void *rp) GCRuntime::removeRoot(void *rp)
{ {
rootsHash.remove(rp); rootsHash.remove(rp);
poke(); poke = true;
} }
template <typename T> template <typename T>
@ -1477,42 +1423,9 @@ js::RemoveRoot(JSRuntime *rt, void *rp)
rt->gc.removeRoot(rp); rt->gc.removeRoot(rp);
} }
void typedef RootedValueMap::Range RootRange;
GCRuntime::setMaxMallocBytes(size_t value) typedef RootedValueMap::Entry RootEntry;
{ typedef RootedValueMap::Enum RootEnum;
/*
* 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);
}
static size_t static size_t
ComputeTriggerBytes(Zone *zone, size_t lastBytes, size_t maxBytes, JSGCInvocationKind gckind) ComputeTriggerBytes(Zone *zone, size_t lastBytes, size_t maxBytes, JSGCInvocationKind gckind)
@ -4441,7 +4354,7 @@ AutoGCSession::~AutoGCSession()
#ifdef JS_GC_ZEAL #ifdef JS_GC_ZEAL
/* Keeping these around after a GC is dangerous. */ /* Keeping these around after a GC is dangerous. */
gc->clearSelectedForMarking(); gc->selectedForMarking.clearAndFree();
#endif #endif
/* Clear gcMallocBytes for all compartments */ /* Clear gcMallocBytes for all compartments */
@ -4450,7 +4363,7 @@ AutoGCSession::~AutoGCSession()
zone->unscheduleGC(); zone->unscheduleGC();
} }
gc->resetMallocBytes(); gc->rt->resetGCMallocBytes();
} }
AutoCopyFreeListToArenas::AutoCopyFreeListToArenas(JSRuntime *rt, ZoneSelector selector) AutoCopyFreeListToArenas::AutoCopyFreeListToArenas(JSRuntime *rt, ZoneSelector selector)
@ -4783,7 +4696,7 @@ GCRuntime::budgetIncrementalGC(int64_t *budget)
return; return;
} }
if (isTooMuchMalloc()) { if (rt->isTooMuchMalloc()) {
*budget = SliceBudget::Unlimited; *budget = SliceBudget::Unlimited;
stats.nonincremental("malloc bytes trigger"); stats.nonincremental("malloc bytes trigger");
} }
@ -4982,21 +4895,21 @@ GCRuntime::collect(bool incremental, int64_t budget, JSGCInvocationKind gckind,
*/ */
if (incrementalState == NO_INCREMENTAL) { if (incrementalState == NO_INCREMENTAL) {
gcstats::AutoPhase ap(stats, gcstats::PHASE_GC_BEGIN); gcstats::AutoPhase ap(stats, gcstats::PHASE_GC_BEGIN);
if (gcCallback.op) if (gcCallback)
gcCallback.op(rt, JSGC_BEGIN, gcCallback.data); gcCallback(rt, JSGC_BEGIN, gcCallbackData);
} }
poked = false; poke = false;
bool wasReset = gcCycle(incremental, budget, gckind, reason); bool wasReset = gcCycle(incremental, budget, gckind, reason);
if (incrementalState == NO_INCREMENTAL) { if (incrementalState == NO_INCREMENTAL) {
gcstats::AutoPhase ap(stats, gcstats::PHASE_GC_END); gcstats::AutoPhase ap(stats, gcstats::PHASE_GC_END);
if (gcCallback.op) if (gcCallback)
gcCallback.op(rt, JSGC_END, gcCallback.data); gcCallback(rt, JSGC_END, gcCallbackData);
} }
/* Need to re-schedule all zones for GC. */ /* Need to re-schedule all zones for GC. */
if (poked && shouldCleanUpEverything) if (poke && shouldCleanUpEverything)
JS::PrepareForFullGC(rt); 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 * case) until we can be sure that no additional garbage is created
* (which typically happens if roots are dropped during finalizers). * (which typically happens if roots are dropped during finalizers).
*/ */
repeat = (poked && shouldCleanUpEverything) || wasReset || repeatForDeadZone; repeat = (poke && shouldCleanUpEverything) || wasReset || repeatForDeadZone;
} while (repeat); } while (repeat);
if (incrementalState == NO_INCREMENTAL) { if (incrementalState == NO_INCREMENTAL) {
@ -5382,40 +5295,27 @@ GCRuntime::runDebugGC()
} }
void 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 #ifdef JS_GC_ZEAL
bool JSRuntime *rt = cx->runtime();
GCRuntime::selectForMarking(JSObject *object) rt->gc.deterministicOnly = enabled;
{
JS_ASSERT(!isHeapMajorCollecting());
return selectedForMarking.append(object);
}
void
GCRuntime::clearSelectedForMarking()
{
selectedForMarking.clearAndFree();
}
void
GCRuntime::setDeterministic(bool enabled)
{
JS_ASSERT(!isHeapMajorCollecting());
deterministicOnly = enabled;
}
#endif #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 #ifdef DEBUG
@ -5474,6 +5374,105 @@ js::ReleaseAllJITCode(FreeOp *fop)
#endif #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 void
js::PurgeJITCaches(Zone *zone) js::PurgeJITCaches(Zone *zone)
{ {
@ -5602,11 +5601,13 @@ JS::AssertGCThingMustBeTenured(JSObject *obj)
JS_FRIEND_API(void) JS_FRIEND_API(void)
js::gc::AssertGCThingHasType(js::gc::Cell *cell, JSGCTraceKind kind) js::gc::AssertGCThingHasType(js::gc::Cell *cell, JSGCTraceKind kind)
{ {
#ifdef DEBUG
JS_ASSERT(cell); JS_ASSERT(cell);
if (IsInsideNursery(cell)) if (IsInsideNursery(cell))
JS_ASSERT(kind == JSTRACE_OBJECT); JS_ASSERT(kind == JSTRACE_OBJECT);
else else
JS_ASSERT(MapAllocToTraceKind(cell->tenuredGetAllocKind()) == kind); JS_ASSERT(MapAllocToTraceKind(cell->tenuredGetAllocKind()) == kind);
#endif
} }
JS_FRIEND_API(size_t) JS_FRIEND_API(size_t)
@ -5621,7 +5622,7 @@ JS::GetGCNumber()
#ifdef DEBUG #ifdef DEBUG
JS::AutoAssertOnGC::AutoAssertOnGC() JS::AutoAssertOnGC::AutoAssertOnGC()
: gc(nullptr), gcNumber(0) : runtime(nullptr), gcNumber(0)
{ {
js::PerThreadData *data = js::TlsPerThreadData.get(); js::PerThreadData *data = js::TlsPerThreadData.get();
if (data) { if (data) {
@ -5631,38 +5632,38 @@ JS::AutoAssertOnGC::AutoAssertOnGC()
* code that works from both threads, however. We also use this to * code that works from both threads, however. We also use this to
* annotate the off thread run loops. * annotate the off thread run loops.
*/ */
JSRuntime *runtime = data->runtimeIfOnOwnerThread(); runtime = data->runtimeIfOnOwnerThread();
if (runtime) { if (runtime) {
gc = &runtime->gc; gcNumber = runtime->gc.number;
gcNumber = gc->number; ++runtime->gc.inUnsafeRegion;
gc->enterUnsafeRegion();
} }
} }
} }
JS::AutoAssertOnGC::AutoAssertOnGC(JSRuntime *rt) 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() JS::AutoAssertOnGC::~AutoAssertOnGC()
{ {
if (gc) { if (runtime) {
gc->leaveUnsafeRegion(); --runtime->gc.inUnsafeRegion;
MOZ_ASSERT(runtime->gc.inUnsafeRegion >= 0);
/* /*
* The following backstop assertion should never fire: if we bumped the * The following backstop assertion should never fire: if we bumped the
* gcNumber, we should have asserted because inUnsafeRegion was true. * 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 /* static */ void
JS::AutoAssertOnGC::VerifyIsSafeToGC(JSRuntime *rt) JS::AutoAssertOnGC::VerifyIsSafeToGC(JSRuntime *rt)
{ {
if (rt->gc.isInsideUnsafeRegion()) if (rt->gc.inUnsafeRegion > 0)
MOZ_CRASH("[AutoAssertOnGC] possible GC in GC-unsafe region"); MOZ_CRASH("[AutoAssertOnGC] possible GC in GC-unsafe region");
} }
#endif #endif

View File

@ -900,6 +900,11 @@ MinorGC(JSRuntime *rt, JS::gcreason::Reason reason);
extern void extern void
MinorGC(JSContext *cx, JS::gcreason::Reason reason); 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. */ /* Functions for managing cross compartment gray pointers. */
extern void extern void
@ -1156,6 +1161,15 @@ GCIfNeeded(JSContext *cx);
void void
RunDebugGC(JSContext *cx); 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. */ /* Wait for the background thread to finish sweeping if it is running. */
void void
FinishBackgroundFinalize(JSRuntime *rt); FinishBackgroundFinalize(JSRuntime *rt);

View File

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

View File

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

View File

@ -1271,6 +1271,7 @@ template <>
struct GCMethods<const types::Type> struct GCMethods<const types::Type>
{ {
static types::Type initial() { return types::Type::UnknownType(); } static types::Type initial() { return types::Type::UnknownType(); }
static ThingRootKind kind() { return THING_ROOT_TYPE; }
static bool poisoned(const types::Type &v) { static bool poisoned(const types::Type &v) {
return (v.isTypeObject() && IsPoisonedPtr(v.typeObject())) return (v.isTypeObject() && IsPoisonedPtr(v.typeObject()))
|| (v.isSingleObject() && IsPoisonedPtr(v.singleObject())); || (v.isSingleObject() && IsPoisonedPtr(v.singleObject()));
@ -1281,6 +1282,7 @@ template <>
struct GCMethods<types::Type> struct GCMethods<types::Type>
{ {
static types::Type initial() { return types::Type::UnknownType(); } static types::Type initial() { return types::Type::UnknownType(); }
static ThingRootKind kind() { return THING_ROOT_TYPE; }
static bool poisoned(const types::Type &v) { static bool poisoned(const types::Type &v) {
return (v.isTypeObject() && IsPoisonedPtr(v.typeObject())) return (v.isTypeObject() && IsPoisonedPtr(v.typeObject()))
|| (v.isSingleObject() && IsPoisonedPtr(v.singleObject())); || (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); return CallJSDeletePropertyOp(cx, obj->getClass()->delProperty, obj, id, succeeded);
} }
cx->runtime()->gc.poke(); GCPoke(cx->runtime());
if (IsImplicitDenseOrTypedArrayElement(shape)) { if (IsImplicitDenseOrTypedArrayElement(shape)) {
if (obj->is<TypedArrayObject>()) { if (obj->is<TypedArrayObject>()) {

View File

@ -1936,114 +1936,15 @@ js::IsValidBytecodeOffset(JSContext *cx, JSScript *script, size_t offset)
return false; 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_FRIEND_API(size_t)
js::GetPCCountScriptCount(JSContext *cx) js::GetPCCountScriptCount(JSContext *cx)
{ {
JSRuntime *rt = cx->runtime(); JSRuntime *rt = cx->runtime();
if (!rt->scriptAndCountsVector) if (!rt->gc.scriptAndCountsVector)
return 0; return 0;
return rt->scriptAndCountsVector->length(); return rt->gc.scriptAndCountsVector->length();
} }
enum MaybeComma {NO_COMMA, COMMA}; enum MaybeComma {NO_COMMA, COMMA};
@ -2078,12 +1979,12 @@ js::GetPCCountScriptSummary(JSContext *cx, size_t index)
{ {
JSRuntime *rt = cx->runtime(); 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); JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BUFFER_TOO_SMALL);
return nullptr; return nullptr;
} }
const ScriptAndCounts &sac = (*rt->scriptAndCountsVector)[index]; const ScriptAndCounts &sac = (*rt->gc.scriptAndCountsVector)[index];
RootedScript script(cx, sac.script); RootedScript script(cx, sac.script);
/* /*
@ -2338,12 +2239,12 @@ js::GetPCCountScriptContents(JSContext *cx, size_t index)
{ {
JSRuntime *rt = cx->runtime(); 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); JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BUFFER_TOO_SMALL);
return nullptr; return nullptr;
} }
const ScriptAndCounts &sac = (*rt->scriptAndCountsVector)[index]; const ScriptAndCounts &sac = (*rt->gc.scriptAndCountsVector)[index];
JSScript *script = sac.script; JSScript *script = sac.script;
StringBuffer buf(cx); StringBuffer buf(cx);

View File

@ -273,6 +273,7 @@ class Bindings
template <> template <>
struct GCMethods<Bindings> { struct GCMethods<Bindings> {
static Bindings initial(); static Bindings initial();
static ThingRootKind kind() { return THING_ROOT_BINDINGS; }
static bool poisoned(const Bindings &bindings) { static bool poisoned(const Bindings &bindings) {
return IsPoisonedPtr(static_cast<Shape *>(bindings.callObjShape())); return IsPoisonedPtr(static_cast<Shape *>(bindings.callObjShape()));
} }
@ -1927,21 +1928,6 @@ SweepScriptData(JSRuntime *rt);
extern void extern void
FreeScriptData(JSRuntime *rt); 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; struct GSNCache;
jssrcnote * jssrcnote *

View File

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

View File

@ -56,7 +56,7 @@ NewObjectCache::newObjectFromHit(JSContext *cx, EntryIndex entry_, js::gc::Initi
if (type->shouldPreTenure()) if (type->shouldPreTenure())
heap = gc::TenuredHeap; heap = gc::TenuredHeap;
if (cx->runtime()->gc.upcomingZealousGC()) if (cx->runtime()->upcomingZealousGC())
return nullptr; return nullptr;
// Trigger an identical allocation to the one that notified us of OOM // 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) #if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR)
simulatorRuntime_(nullptr), simulatorRuntime_(nullptr),
#endif #endif
scriptAndCountsVector(nullptr),
NaNValue(DoubleNaNValue()), NaNValue(DoubleNaNValue()),
negativeInfinityValue(DoubleValue(NegativeInfinity<double>())), negativeInfinityValue(DoubleValue(NegativeInfinity<double>())),
positiveInfinityValue(DoubleValue(PositiveInfinity<double>())), positiveInfinityValue(DoubleValue(PositiveInfinity<double>())),
@ -695,6 +694,19 @@ JSRuntime::triggerActivityCallback(bool active)
activityCallback(activityCallbackArg, 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 void
JSRuntime::updateMallocCounter(size_t nbytes) JSRuntime::updateMallocCounter(size_t nbytes)
{ {
@ -704,7 +716,12 @@ JSRuntime::updateMallocCounter(size_t nbytes)
void void
JSRuntime::updateMallocCounter(JS::Zone *zone, size_t nbytes) 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) JS_FRIEND_API(void)
@ -712,7 +729,9 @@ JSRuntime::onTooMuchMalloc()
{ {
if (!CurrentThreadCanAccessRuntime(this)) if (!CurrentThreadCanAccessRuntime(this))
return; return;
gc.onTooMuchMalloc();
if (!gc.mallocGCTriggered)
gc.mallocGCTriggered = TriggerGC(this, JS::gcreason::TOO_MUCH_MALLOC);
} }
JS_FRIEND_API(void *) JS_FRIEND_API(void *)

View File

@ -134,8 +134,6 @@ struct ScopeCoordinateNameCache {
void purge(); void purge();
}; };
typedef Vector<ScriptAndCounts, 0, SystemAllocPolicy> ScriptAndCountsVector;
struct EvalCacheEntry struct EvalCacheEntry
{ {
JSScript *script; JSScript *script;
@ -955,7 +953,31 @@ struct JSRuntime : public JS::shadow::Runtime,
bool isHeapMinorCollecting() { return gc.isHeapMinorCollecting(); } bool isHeapMinorCollecting() { return gc.isHeapMinorCollecting(); }
bool isHeapCollecting() { return gc.isHeapCollecting(); } 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() { void lockGC() {
assertCanLock(js::GCLock); assertCanLock(js::GCLock);
@ -980,9 +1002,6 @@ struct JSRuntime : public JS::shadow::Runtime,
void setSimulatorRuntime(js::jit::SimulatorRuntime *srt); void setSimulatorRuntime(js::jit::SimulatorRuntime *srt);
#endif #endif
/* Strong references on scripts held for PCCount profiling API. */
js::ScriptAndCountsVector *scriptAndCountsVector;
/* Well-known numbers held for use by this runtime's contexts. */ /* Well-known numbers held for use by this runtime's contexts. */
const js::Value NaNValue; const js::Value NaNValue;
const js::Value negativeInfinityValue; const js::Value negativeInfinityValue;
@ -1257,6 +1276,13 @@ struct JSRuntime : public JS::shadow::Runtime,
JSRuntime *thisFromCtor() { return this; } 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 * Call this after allocating memory held by GC things, to update memory
* pressure counters or report the OOM error if necessary. If oomError and * 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); } void reportAllocationOverflow() { js_ReportAllocationOverflow(nullptr); }
bool isTooMuchMalloc() const {
return gc.mallocBytes <= 0;
}
/* /*
* The function must be called outside the GC lock. * The function must be called outside the GC lock.
*/ */