Bug 488731 - Avoid shape regeneration and property cache purge during the GC (r=mrbkap).

This commit is contained in:
Brendan Eich 2009-07-24 06:55:28 -07:00
parent 1fcf4af3b3
commit b4a10de944
7 changed files with 80 additions and 49 deletions

View File

@ -104,9 +104,20 @@ FinishThreadData(JSThreadData *data)
static void
PurgeThreadData(JSContext *cx, JSThreadData *data)
{
js_PurgeGSNCache(&data->gsnCache);
if (cx->runtime->gcRegenShapes)
js_PurgePropertyCache(cx, &data->propertyCache);
# ifdef JS_TRACER
JSTraceMonitor *tm = &data->traceMonitor;
tm->reservedDoublePoolPtr = tm->reservedDoublePool;
/*
* FIXME: bug 506117. We should flush only if (cx->runtime->gcRegenShapes),
* but we can't yet, because traces may embed sprop and object references,
* and we don't yet mark such embedded refs.
*/
tm->needFlush = JS_TRUE;
if (tm->recorder)
@ -123,9 +134,6 @@ PurgeThreadData(JSContext *cx, JSThreadData *data)
/* Destroy eval'ed scripts. */
js_DestroyScriptsToGC(cx, data);
js_PurgeGSNCache(&data->gsnCache);
js_PurgePropertyCache(cx, &data->propertyCache);
}
#ifdef JS_THREADSAFE

View File

@ -389,7 +389,8 @@ struct JSRuntime {
*/
JSPackedBool gcPoke;
JSPackedBool gcRunning;
uint16 gcPadding;
JSPackedBool gcRegenShapes;
uint8 gcPadding;
#ifdef JS_GC_ZEAL
jsrefcount gcZeal;
#endif
@ -1539,6 +1540,7 @@ static JS_INLINE uint32
js_RegenerateShapeForGC(JSContext *cx)
{
JS_ASSERT(cx->runtime->gcRunning);
JS_ASSERT(cx->runtime->gcRegenShapes);
/*
* Under the GC, compared with js_GenerateShape, we don't need to use

View File

@ -3599,7 +3599,6 @@ js_GC(JSContext *cx, JSGCInvocationKind gckind)
#ifdef JS_TRACER
js_PurgeJITOracle();
#endif
js_PurgeThreads(cx);
restart:
rt->gcNumber++;
@ -3611,8 +3610,13 @@ js_GC(JSContext *cx, JSGCInvocationKind gckind)
* Same for the protoHazardShape proxy-shape standing in for all object
* prototypes having readonly or setter properties.
*/
rt->shapeGen = 0;
rt->protoHazardShape = 0;
if (rt->shapeGen & SHAPE_OVERFLOW_BIT) {
rt->gcRegenShapes = true;
rt->shapeGen = 0;
rt->protoHazardShape = 0;
}
js_PurgeThreads(cx);
/*
* Mark phase.
@ -3887,7 +3891,7 @@ out:
rt->setGCLastBytes(rt->gcBytes);
done_running:
rt->gcLevel = 0;
rt->gcRunning = JS_FALSE;
rt->gcRunning = rt->gcRegenShapes = false;
#ifdef JS_THREADSAFE
rt->gcThread = NULL;

View File

@ -5060,11 +5060,15 @@ js_TraceNativeEnumerators(JSTracer *trc)
jsid *cursor, *end;
/*
* Purge native enumerators cached by shape id, which we are about to
* re-number completely when tracing is done for the GC.
* Purge native enumerators cached by shape id when the GC is about to
* re-number shapes due to shape generation overflow. Do this also when
* shutting down the runtime.
*/
rt = trc->context->runtime;
if (IS_GC_MARKING_TRACER(trc)) {
bool doGC = IS_GC_MARKING_TRACER(trc) &&
(rt->gcRegenShapes || rt->state == JSRTS_LANDING);
if (doGC) {
memset(&rt->nativeEnumCache, 0, sizeof rt->nativeEnumCache);
#ifdef JS_DUMP_ENUM_CACHE_STATS
printf("nativeEnumCache hit rate %g%%\n",
@ -5083,7 +5087,7 @@ js_TraceNativeEnumerators(JSTracer *trc)
do {
TRACE_ID(trc, *cursor);
} while (++cursor != end);
} else if (IS_GC_MARKING_TRACER(trc)) {
} else if (doGC) {
js_RemoveAsGCBytes(rt, NativeEnumeratorSize(ne->length));
*nep = ne->next;
JS_free(trc->context, ne);
@ -5768,23 +5772,22 @@ js_TraceObject(JSTracer *trc, JSObject *obj)
MeterEntryCount(scope->entryCount);
#endif
sprop = SCOPE_LAST_PROP(scope);
sprop = scope->lastProp;
if (sprop) {
JS_ASSERT(scope->has(sprop));
/* Regenerate property cache shape ids if GC'ing. */
if (IS_GC_MARKING_TRACER(trc)) {
uint32 shape, oldshape;
shape = js_RegenerateShapeForGC(cx);
if (!(sprop->flags & SPROP_MARK)) {
oldshape = sprop->shape;
sprop->shape = shape;
if (IS_GC_MARKING_TRACER(trc) && cx->runtime->gcRegenShapes) {
if (!(sprop->flags & SPROP_FLAG_SHAPE_REGEN)) {
sprop->shape = js_RegenerateShapeForGC(cx);
sprop->flags |= SPROP_FLAG_SHAPE_REGEN;
if (scope->shape != oldshape)
shape = js_RegenerateShapeForGC(cx);
}
uint32 shape = sprop->shape;
if (scope->hasOwnShape()) {
shape = js_RegenerateShapeForGC(cx);
JS_ASSERT(shape != sprop->shape);
}
scope->shape = shape;
}

View File

@ -1032,12 +1032,14 @@ JSScope::reportReadOnlyScope(JSContext *cx)
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_READ_ONLY, bytes);
}
static inline void
js_MakeScopeShapeUnique(JSContext *cx, JSScope *scope)
void
JSScope::generateOwnShape(JSContext *cx)
{
if (scope->object)
js_LeaveTraceIfGlobalObject(cx, scope->object);
scope->shape = js_GenerateShape(cx, false);
if (object)
js_LeaveTraceIfGlobalObject(cx, object);
shape = js_GenerateShape(cx, false);
setOwnShape();
}
JSScopeProperty *
@ -1154,7 +1156,6 @@ JSScope::add(JSContext *cx, jsid id,
}
setMiddleDelete();
}
js_MakeScopeShapeUnique(cx, this);
/*
* If we fail later on trying to find or create a new sprop, we will
@ -1537,7 +1538,7 @@ JSScope::remove(JSContext *cx, jsid id)
} else if (!hadMiddleDelete()) {
setMiddleDelete();
}
js_MakeScopeShapeUnique(cx, this);
generateOwnShape(cx);
CHECK_ANCESTOR_LINE(this, true);
/* Last, consider shrinking this->table if its load factor is <= .25. */
@ -1567,25 +1568,25 @@ JSScope::clear(JSContext *cx)
void
JSScope::brandingShapeChange(JSContext *cx, uint32 slot, jsval v)
{
js_MakeScopeShapeUnique(cx, this);
generateOwnShape(cx);
}
void
JSScope::deletingShapeChange(JSContext *cx, JSScopeProperty *sprop)
{
js_MakeScopeShapeUnique(cx, this);
generateOwnShape(cx);
}
void
JSScope::methodShapeChange(JSContext *cx, uint32 slot, jsval toval)
{
js_MakeScopeShapeUnique(cx, this);
generateOwnShape(cx);
}
void
JSScope::protoShapeChange(JSContext *cx)
{
js_MakeScopeShapeUnique(cx, this);
generateOwnShape(cx);
}
void
@ -1594,19 +1595,19 @@ JSScope::replacingShapeChange(JSContext *cx, JSScopeProperty *sprop, JSScopeProp
if (shape == sprop->shape)
shape = newsprop->shape;
else
js_MakeScopeShapeUnique(cx, this);
generateOwnShape(cx);
}
void
JSScope::sealingShapeChange(JSContext *cx)
{
js_MakeScopeShapeUnique(cx, this);
generateOwnShape(cx);
}
void
JSScope::shadowingShapeChange(JSContext *cx, JSScopeProperty *sprop)
{
js_MakeScopeShapeUnique(cx, this);
generateOwnShape(cx);
}
void
@ -1826,10 +1827,12 @@ js_SweepScopeProperties(JSContext *cx)
*/
if (sprop->flags & SPROP_MARK) {
sprop->flags &= ~SPROP_MARK;
if (sprop->flags & SPROP_FLAG_SHAPE_REGEN)
sprop->flags &= ~SPROP_FLAG_SHAPE_REGEN;
else
sprop->shape = js_RegenerateShapeForGC(cx);
if (rt->gcRegenShapes) {
if (sprop->flags & SPROP_FLAG_SHAPE_REGEN)
sprop->flags &= ~SPROP_FLAG_SHAPE_REGEN;
else
sprop->shape = js_RegenerateShapeForGC(cx);
}
liveCount++;
continue;
}
@ -1869,7 +1872,7 @@ js_SweepScopeProperties(JSContext *cx)
sprop->kids = NULL;
parent = sprop->parent;
/* Assert that grandparent has no kids or chunky kids. */
/* The grandparent must have either no kids or chunky kids. */
JS_ASSERT(!parent || !parent->kids ||
KIDS_IS_CHUNKY(parent->kids));
if (KIDS_IS_CHUNKY(kids)) {
@ -1888,8 +1891,7 @@ js_SweepScopeProperties(JSContext *cx)
* re-use by InsertPropertyTreeChild.
*/
chunk->kids[i] = NULL;
if (!InsertPropertyTreeChild(rt, parent, kid,
chunk)) {
if (!InsertPropertyTreeChild(rt, parent, kid, chunk)) {
/*
* This can happen only if we failed to add an
* entry to the root property hash table.

View File

@ -219,6 +219,7 @@ struct JSScope {
bool createTable(JSContext *cx, bool report);
bool changeTable(JSContext *cx, int change);
void reportReadOnlyScope(JSContext *cx);
void generateOwnShape(JSContext *cx);
JSScopeProperty **searchTable(jsid id, bool adding);
inline JSScopeProperty **search(jsid id, bool adding);
JSScope *createEmptyScope(JSContext *cx, JSClass *clasp);
@ -281,16 +282,14 @@ struct JSScope {
MIDDLE_DELETE = 0x0001,
SEALED = 0x0002,
BRANDED = 0x0004,
INDEXED_PROPERTIES = 0x0008
INDEXED_PROPERTIES = 0x0008,
OWN_SHAPE = 0x0010
};
bool hadMiddleDelete() { return flags & MIDDLE_DELETE; }
void setMiddleDelete() { flags |= MIDDLE_DELETE; }
void clearMiddleDelete() { flags &= ~MIDDLE_DELETE; }
bool hadIndexedProperties() { return flags & INDEXED_PROPERTIES; }
void setIndexedProperties() { flags |= INDEXED_PROPERTIES; }
/*
* Don't define clearSealed, as it can't be done safely because JS_LOCK_OBJ
* will avoid taking the lock if the object owns its scope and the scope is
@ -306,7 +305,12 @@ struct JSScope {
*/
bool branded() { return flags & BRANDED; }
void setBranded() { flags |= BRANDED; }
void clearBranded() { flags &= ~BRANDED; }
bool hadIndexedProperties() { return flags & INDEXED_PROPERTIES; }
void setIndexedProperties() { flags |= INDEXED_PROPERTIES; }
bool hasOwnShape() { return flags & OWN_SHAPE; }
void setOwnShape() { flags |= OWN_SHAPE; }
bool owned() { return object != NULL; }
};

View File

@ -1610,6 +1610,14 @@ js_DestroyScript(JSContext *cx, JSScript *script)
* to its bytecode if this script is being destroyed from JS_DestroyScript
* or equivalent according to a mandatory "New/Destroy" protocol.
*
* The GC purges all property caches when regenerating shapes upon shape
* generator overflow, so no need in that event to purge just the entries
* for this script.
*
* The GC purges trace-JITted code on every GC activation, not just when
* regenerating shapes, so we don't have to purge fragments if the GC is
* currently running.
*
* JS_THREADSAFE note: js_PurgePropertyCacheForScript purges only the
* current thread's property cache, so a script not owned by a function
* or object, which hands off lifetime management for that script to the
@ -1624,7 +1632,7 @@ js_DestroyScript(JSContext *cx, JSScript *script)
JS_ASSERT_IF(cx->runtime->gcRunning, !script->owner);
#endif
if (!cx->runtime->gcRunning) {
if (!cx->runtime->gcRegenShapes) {
JSStackFrame *fp = js_GetTopStackFrame(cx);
if (!(fp && (fp->flags & JSFRAME_EVAL))) {