mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 333236: merge sweep and free phases in GC. r=brendan
This commit is contained in:
parent
fe1659e34d
commit
1dd9e7fca8
@ -397,6 +397,7 @@ js_string_uninterner(JSDHashTable *table, JSDHashEntryHdr *hdr,
|
||||
{
|
||||
JSAtomHashEntry *entry = TO_ATOM_ENTRY(hdr);
|
||||
JSRuntime *rt = (JSRuntime *)arg;
|
||||
JSString *str;
|
||||
|
||||
/*
|
||||
* Any string entry that remains at this point must be initialized, as the
|
||||
@ -404,7 +405,10 @@ js_string_uninterner(JSDHashTable *table, JSDHashEntryHdr *hdr,
|
||||
*/
|
||||
JS_ASSERT(IS_STRING_TABLE(table));
|
||||
JS_ASSERT(entry->keyAndFlags != 0);
|
||||
js_FinalizeStringRT(rt, (JSString *)ATOM_ENTRY_KEY(entry));
|
||||
str = (JSString *)ATOM_ENTRY_KEY(entry);
|
||||
|
||||
/* Pass null as context. */
|
||||
js_FinalizeStringRT(rt, str, GCF_TYPEMASK & *js_GetGCThingFlags(str), NULL);
|
||||
return JS_DHASH_NEXT;
|
||||
}
|
||||
|
||||
|
@ -414,7 +414,7 @@ js_DestroyContext(JSContext *cx, JSDestroyContextMode mode)
|
||||
|
||||
/*
|
||||
* Free unit string storage only after the last GC has completed, so
|
||||
* that js_FinalizeStringRT can detect unit strings and avoid calling
|
||||
* that js_FinalizeString can detect unit strings and avoid calling
|
||||
* free on their chars storage.
|
||||
*/
|
||||
free(rt->unitStrings);
|
||||
|
@ -2168,17 +2168,12 @@ out:
|
||||
void
|
||||
js_FinalizeFunction(JSContext *cx, JSFunction *fun)
|
||||
{
|
||||
JSScript *script;
|
||||
|
||||
/*
|
||||
* Null-check of i.script is required since the parser sets interpreted
|
||||
* very early.
|
||||
*/
|
||||
if (FUN_INTERPRETED(fun) && fun->u.i.script) {
|
||||
script = fun->u.i.script;
|
||||
fun->u.i.script = NULL;
|
||||
js_DestroyScript(cx, script);
|
||||
}
|
||||
if (FUN_INTERPRETED(fun) && fun->u.i.script)
|
||||
js_DestroyScript(cx, fun->u.i.script);
|
||||
}
|
||||
|
||||
JSObject *
|
||||
|
198
js/src/jsgc.c
198
js/src/jsgc.c
@ -816,37 +816,6 @@ js_IsAboutToBeFinalized(JSContext *cx, void *thing)
|
||||
return !(flags & (GCF_MARK | GCF_LOCK | GCF_FINAL));
|
||||
}
|
||||
|
||||
typedef void (*GCFinalizeOp)(JSContext *cx, JSGCThing *thing);
|
||||
|
||||
#ifndef DEBUG
|
||||
# define js_FinalizeDouble NULL
|
||||
#endif
|
||||
|
||||
#if !JS_HAS_XML_SUPPORT
|
||||
# define js_FinalizeXMLNamespace NULL
|
||||
# define js_FinalizeXMLQName NULL
|
||||
# define js_FinalizeXML NULL
|
||||
#endif
|
||||
|
||||
static GCFinalizeOp gc_finalizers[GCX_NTYPES] = {
|
||||
(GCFinalizeOp) js_FinalizeObject, /* GCX_OBJECT */
|
||||
(GCFinalizeOp) js_FinalizeString, /* GCX_STRING */
|
||||
(GCFinalizeOp) js_FinalizeDouble, /* GCX_DOUBLE */
|
||||
(GCFinalizeOp) js_FinalizeFunction, /* GCX_FUNCTION */
|
||||
(GCFinalizeOp) js_FinalizeXMLNamespace, /* GCX_NAMESPACE */
|
||||
(GCFinalizeOp) js_FinalizeXMLQName, /* GCX_QNAME */
|
||||
(GCFinalizeOp) js_FinalizeXML, /* GCX_XML */
|
||||
NULL, /* unused */
|
||||
NULL, /* GCX_EXTERNAL_STRING */
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
#ifdef DEBUG
|
||||
static const char newborn_external_string[] = "newborn external string";
|
||||
|
||||
@ -870,21 +839,6 @@ static const char *gc_typenames[GCX_NTYPES] = {
|
||||
};
|
||||
#endif
|
||||
|
||||
intN
|
||||
js_ChangeExternalStringFinalizer(JSStringFinalizeOp oldop,
|
||||
JSStringFinalizeOp newop)
|
||||
{
|
||||
uintN i;
|
||||
|
||||
for (i = GCX_EXTERNAL_STRING; i < GCX_NTYPES; i++) {
|
||||
if (gc_finalizers[i] == (GCFinalizeOp) oldop) {
|
||||
gc_finalizers[i] = (GCFinalizeOp) newop;
|
||||
return (intN) i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* This is compatible with JSDHashEntryStub. */
|
||||
typedef struct JSGCRootHashEntry {
|
||||
JSDHashEntryHdr hdr;
|
||||
@ -2390,11 +2344,10 @@ js_GC(JSContext *cx, JSGCInvocationKind gckind)
|
||||
uintN i, type;
|
||||
JSTracer trc;
|
||||
uint32 thingSize, indexLimit;
|
||||
JSGCArenaInfo *a, **ap;
|
||||
JSGCArenaInfo *a, **ap, *emptyArenas;
|
||||
uint8 flags, *flagp;
|
||||
JSGCThing *thing, *freeList;
|
||||
JSGCArenaList *arenaList;
|
||||
GCFinalizeOp finalizer;
|
||||
JSBool allClear;
|
||||
#ifdef JS_THREADSAFE
|
||||
uint32 requestDebit;
|
||||
@ -2647,45 +2600,98 @@ restart:
|
||||
* of the file guarantees that JSString and jsdouble instances are
|
||||
* allocated from a different list.
|
||||
*/
|
||||
emptyArenas = NULL;
|
||||
for (i = 0; i < GC_NUM_FREELISTS; i++) {
|
||||
arenaList = &rt->gcArenaList[i == 0
|
||||
? GC_FREELIST_INDEX(sizeof(JSObject))
|
||||
: i == GC_FREELIST_INDEX(sizeof(JSObject))
|
||||
? 0
|
||||
: i];
|
||||
a = arenaList->last;
|
||||
if (!a)
|
||||
ap = &arenaList->last;
|
||||
if (!(a = *ap))
|
||||
continue;
|
||||
|
||||
JS_ASSERT(arenaList->lastCount > 0);
|
||||
arenaList->freeList = NULL;
|
||||
freeList = NULL;
|
||||
METER(arenaList->stats.nthings = 0);
|
||||
METER(arenaList->stats.freelen = 0);
|
||||
thingSize = arenaList->thingSize;
|
||||
indexLimit = THINGS_PER_ARENA(thingSize);
|
||||
JS_ASSERT(arenaList->lastCount > 0);
|
||||
flagp = THING_FLAGP(a, arenaList->lastCount - 1);
|
||||
for (;;) {
|
||||
METER(size_t nfree = 0);
|
||||
|
||||
JS_ASSERT(a->prevUntracedPage == 0);
|
||||
JS_ASSERT(a->untracedThings == 0);
|
||||
allClear = JS_TRUE;
|
||||
do {
|
||||
flags = *flagp;
|
||||
if (flags & GCF_MARK) {
|
||||
if (flags & (GCF_MARK | GCF_LOCK)) {
|
||||
*flagp &= ~GCF_MARK;
|
||||
} else if (!(flags & (GCF_LOCK | GCF_FINAL))) {
|
||||
allClear = JS_FALSE;
|
||||
METER(++arenaList->stats.nthings);
|
||||
} else if (!(flags & GCF_FINAL)) {
|
||||
/* Call the finalizer with GCF_FINAL ORed into flags. */
|
||||
thing = (JSGCThing *) FLAGP_TO_THING(flagp, thingSize);
|
||||
*flagp = (uint8)(flags | GCF_FINAL);
|
||||
type = flags & GCF_TYPEMASK;
|
||||
finalizer = gc_finalizers[type];
|
||||
if (finalizer) {
|
||||
thing = (JSGCThing *) FLAGP_TO_THING(flagp, thingSize);
|
||||
*flagp = (uint8)(flags | GCF_FINAL);
|
||||
if (type >= GCX_EXTERNAL_STRING)
|
||||
js_PurgeDeflatedStringCache(rt, (JSString *)thing);
|
||||
finalizer(cx, thing);
|
||||
switch (type) {
|
||||
case GCX_OBJECT:
|
||||
js_FinalizeObject(cx, (JSObject *) thing);
|
||||
break;
|
||||
case GCX_DOUBLE:
|
||||
/* Do nothing. */
|
||||
break;
|
||||
case GCX_FUNCTION:
|
||||
js_FinalizeFunction(cx, (JSFunction *) thing);
|
||||
break;
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
case GCX_NAMESPACE:
|
||||
js_FinalizeXMLNamespace(cx, (JSXMLNamespace *) thing);
|
||||
break;
|
||||
case GCX_QNAME:
|
||||
js_FinalizeXMLQName(cx, (JSXMLQName *) thing);
|
||||
break;
|
||||
case GCX_XML:
|
||||
js_FinalizeXML(cx, (JSXML *) thing);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
JS_ASSERT(type == GCX_STRING ||
|
||||
type - GCX_EXTERNAL_STRING <
|
||||
GCX_NTYPES - GCX_EXTERNAL_STRING);
|
||||
js_FinalizeStringRT(rt, (JSString *) thing, type, cx);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Set flags to GCF_FINAL, signifying that thing is free. */
|
||||
*flagp = GCF_FINAL;
|
||||
thing->flagp = flagp;
|
||||
thing->next = freeList;
|
||||
freeList = thing;
|
||||
METER(++nfree);
|
||||
}
|
||||
} while (++flagp != THING_FLAGS_END(a));
|
||||
a = a->prev;
|
||||
if (!a)
|
||||
|
||||
if (allClear) {
|
||||
/*
|
||||
* Forget just assembled free list head for the arena and
|
||||
* add the arena itself to the destroy list.
|
||||
*/
|
||||
freeList = arenaList->freeList;
|
||||
if (a == arenaList->last)
|
||||
arenaList->lastCount = indexLimit;
|
||||
*ap = a->prev;
|
||||
JS_ASSERT(rt->gcBytes >= GC_ARENA_SIZE);
|
||||
rt->gcBytes -= GC_ARENA_SIZE;
|
||||
a->prev = emptyArenas;
|
||||
emptyArenas = a;
|
||||
} else {
|
||||
arenaList->freeList = freeList;
|
||||
ap = &a->prev;
|
||||
METER(arenaList->stats.freelen += nfree);
|
||||
METER(arenaList->stats.totalfreelen += nfree);
|
||||
METER(++arenaList->stats.totalarenas);
|
||||
}
|
||||
if (!(a = *ap))
|
||||
break;
|
||||
flagp = THING_FLAGP(a, indexLimit - 1);
|
||||
}
|
||||
@ -2706,63 +2712,13 @@ restart:
|
||||
js_SweepScriptFilenames(rt);
|
||||
|
||||
/*
|
||||
* Free phase.
|
||||
* Free any unused arenas and rebuild the JSGCThing freelist.
|
||||
* Destroy arenas after we finished the sweeping sofinalizers can safely
|
||||
* use js_IsAboutToBeFinalized().
|
||||
*/
|
||||
for (i = 0; i < GC_NUM_FREELISTS; i++) {
|
||||
arenaList = &rt->gcArenaList[i];
|
||||
ap = &arenaList->last;
|
||||
if (!(a = *ap))
|
||||
continue;
|
||||
|
||||
allClear = JS_TRUE;
|
||||
arenaList->freeList = NULL;
|
||||
freeList = NULL;
|
||||
thingSize = arenaList->thingSize;
|
||||
indexLimit = THINGS_PER_ARENA(thingSize);
|
||||
JS_ASSERT(arenaList->lastCount > 0);
|
||||
flagp = THING_FLAGP(a, arenaList->lastCount - 1);
|
||||
METER(arenaList->stats.nthings = 0);
|
||||
METER(arenaList->stats.freelen = 0);
|
||||
for (;;) {
|
||||
METER(size_t nfree = 0);
|
||||
do {
|
||||
if (*flagp != GCF_FINAL) {
|
||||
allClear = JS_FALSE;
|
||||
METER(++arenaList->stats.nthings);
|
||||
} else {
|
||||
thing = (JSGCThing *) FLAGP_TO_THING(flagp, thingSize);
|
||||
thing->flagp = flagp;
|
||||
thing->next = freeList;
|
||||
freeList = thing;
|
||||
METER(++nfree);
|
||||
}
|
||||
} while (++flagp != THING_FLAGS_END(a));
|
||||
|
||||
if (allClear) {
|
||||
/*
|
||||
* Forget just assembled free list head for the arena and
|
||||
* destroy the arena itself.
|
||||
*/
|
||||
freeList = arenaList->freeList;
|
||||
if (a == arenaList->last)
|
||||
arenaList->lastCount = indexLimit;
|
||||
*ap = a->prev;
|
||||
JS_ASSERT(rt->gcBytes >= GC_ARENA_SIZE);
|
||||
rt->gcBytes -= GC_ARENA_SIZE;
|
||||
DestroyGCArena(rt, a);
|
||||
} else {
|
||||
allClear = JS_TRUE;
|
||||
arenaList->freeList = freeList;
|
||||
ap = &a->prev;
|
||||
METER(arenaList->stats.freelen += nfree);
|
||||
METER(arenaList->stats.totalfreelen += nfree);
|
||||
METER(++arenaList->stats.totalarenas);
|
||||
}
|
||||
if (!(a = *ap))
|
||||
break;
|
||||
flagp = THING_FLAGP(a, indexLimit - 1);
|
||||
}
|
||||
while (emptyArenas) {
|
||||
a = emptyArenas;
|
||||
emptyArenas = emptyArenas->prev;
|
||||
DestroyGCArena(rt, a);
|
||||
}
|
||||
|
||||
if (rt->gcCallback)
|
||||
|
@ -91,16 +91,16 @@ js_GetGCStringRuntime(JSString *str);
|
||||
#define GC_POKE(cx, oldval) ((cx)->runtime->gcPoke = JSVAL_IS_GCTHING(oldval))
|
||||
#endif
|
||||
|
||||
extern intN
|
||||
js_ChangeExternalStringFinalizer(JSStringFinalizeOp oldop,
|
||||
JSStringFinalizeOp newop);
|
||||
|
||||
extern JSBool
|
||||
js_InitGC(JSRuntime *rt, uint32 maxbytes);
|
||||
|
||||
extern void
|
||||
js_FinishGC(JSRuntime *rt);
|
||||
|
||||
extern intN
|
||||
js_ChangeExternalStringFinalizer(JSStringFinalizeOp oldop,
|
||||
JSStringFinalizeOp newop);
|
||||
|
||||
extern JSBool
|
||||
js_AddRoot(JSContext *cx, void *rp, const char *name);
|
||||
|
||||
|
@ -2796,7 +2796,6 @@ js_FinalizeObject(JSContext *cx, JSObject *obj)
|
||||
|
||||
/* Drop map and free slots. */
|
||||
js_DropObjectMap(cx, map, obj);
|
||||
obj->map = NULL;
|
||||
FreeSlots(cx, obj);
|
||||
}
|
||||
|
||||
|
@ -2619,19 +2619,38 @@ js_PurgeDeflatedStringCache(JSRuntime *rt, JSString *str)
|
||||
JS_RELEASE_LOCK(rt->deflatedStringCacheLock);
|
||||
}
|
||||
|
||||
void
|
||||
js_FinalizeString(JSContext *cx, JSString *str)
|
||||
static JSStringFinalizeOp str_finalizers[GCX_NTYPES - GCX_EXTERNAL_STRING] = {
|
||||
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
|
||||
};
|
||||
|
||||
intN
|
||||
js_ChangeExternalStringFinalizer(JSStringFinalizeOp oldop,
|
||||
JSStringFinalizeOp newop)
|
||||
{
|
||||
js_FinalizeStringRT(cx->runtime, str);
|
||||
uintN i;
|
||||
|
||||
for (i = 0; i != JS_ARRAY_LENGTH(str_finalizers); i++) {
|
||||
if (str_finalizers[i] == oldop) {
|
||||
str_finalizers[i] = newop;
|
||||
return (intN) i + GCX_EXTERNAL_STRING;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* cx is NULL when we are called from js_FinishAtomState to force the
|
||||
* finalization of the permanently interned strings.
|
||||
*/
|
||||
void
|
||||
js_FinalizeStringRT(JSRuntime *rt, JSString *str)
|
||||
js_FinalizeStringRT(JSRuntime *rt, JSString *str, uintN gctype, JSContext *cx)
|
||||
{
|
||||
JSBool valid;
|
||||
JSStringFinalizeOp finalizer;
|
||||
|
||||
JS_RUNTIME_UNMETER(rt, liveStrings);
|
||||
if (JSSTRING_IS_DEPENDENT(str)) {
|
||||
JS_ASSERT(gctype == GCX_STRING);
|
||||
/* If JSSTRFLAG_DEPENDENT is set, this string must be valid. */
|
||||
JS_ASSERT(JSSTRDEP_BASE(str));
|
||||
JS_RUNTIME_UNMETER(rt, liveDependentStrings);
|
||||
@ -2642,17 +2661,26 @@ js_FinalizeStringRT(JSRuntime *rt, JSString *str)
|
||||
if (valid) {
|
||||
if (IN_UNIT_STRING_SPACE_RT(rt, str->u.chars)) {
|
||||
JS_ASSERT(rt->unitStrings[*str->u.chars] == str);
|
||||
JS_ASSERT(gctype == GCX_STRING);
|
||||
rt->unitStrings[*str->u.chars] = NULL;
|
||||
} else {
|
||||
} else if (gctype == GCX_STRING) {
|
||||
free(str->u.chars);
|
||||
} else {
|
||||
JS_ASSERT(gctype - GCX_EXTERNAL_STRING <
|
||||
JS_ARRAY_LENGTH(str_finalizers));
|
||||
finalizer = str_finalizers[gctype - GCX_EXTERNAL_STRING];
|
||||
if (finalizer) {
|
||||
/*
|
||||
* Assume that the finalizer for the permanently interned
|
||||
* string knows how to deal with null context.
|
||||
*/
|
||||
finalizer(cx, str);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (valid)
|
||||
js_PurgeDeflatedStringCache(rt, str);
|
||||
#ifdef DEBUG
|
||||
memset(str, JS_FREE_PATTERN, sizeof *str);
|
||||
#endif
|
||||
}
|
||||
|
||||
JS_FRIEND_API(const char *)
|
||||
|
@ -406,12 +406,13 @@ js_NewStringCopyN(JSContext *cx, const jschar *s, size_t n);
|
||||
extern JSString *
|
||||
js_NewStringCopyZ(JSContext *cx, const jschar *s);
|
||||
|
||||
/* Free the chars held by str when it is finalized by the GC. */
|
||||
/*
|
||||
* Free the chars held by str when it is finalized by the GC.
|
||||
*
|
||||
* This function always needs rt but can live with null cx.
|
||||
*/
|
||||
extern void
|
||||
js_FinalizeString(JSContext *cx, JSString *str);
|
||||
|
||||
extern void
|
||||
js_FinalizeStringRT(JSRuntime *rt, JSString *str);
|
||||
js_FinalizeStringRT(JSRuntime *rt, JSString *str, uintN gctype, JSContext *cx);
|
||||
|
||||
/*
|
||||
* Convert a value to a printable C string.
|
||||
|
Loading…
Reference in New Issue
Block a user