mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-14 10:43:24 +00:00
bug 517199 - typed GC free lists. r=brendan
This commit is contained in:
parent
46afee49ed
commit
79cf339dff
@ -454,8 +454,7 @@ js_string_uninterner(JSDHashTable *table, JSDHashEntryHdr *hdr,
|
||||
JS_ASSERT(entry->keyAndFlags != 0);
|
||||
str = (JSString *)ATOM_ENTRY_KEY(entry);
|
||||
|
||||
/* Pass null as context. */
|
||||
js_FinalizeStringRT(rt, str, js_GetExternalStringGCType(str), NULL);
|
||||
js_FinalizeStringRT(rt, str);
|
||||
return JS_DHASH_NEXT;
|
||||
}
|
||||
|
||||
|
@ -363,7 +363,7 @@ struct JSThread {
|
||||
/* Indicates that the thread is waiting in ClaimTitle from jslock.cpp. */
|
||||
JSTitle *titleToShare;
|
||||
|
||||
JSGCThing *gcFreeLists[GC_NUM_FREELISTS];
|
||||
JSGCThing *gcFreeLists[FINALIZE_LIMIT];
|
||||
|
||||
/* Factored out of JSThread for !JS_THREADSAFE embedding in JSRuntime. */
|
||||
JSThreadData data;
|
||||
@ -450,7 +450,7 @@ struct JSRuntime {
|
||||
|
||||
/* Garbage collector state, used by jsgc.c. */
|
||||
JSGCChunkInfo *gcChunkList;
|
||||
JSGCArenaList gcArenaList[GC_NUM_FREELISTS];
|
||||
JSGCArenaList gcArenaList[FINALIZE_LIMIT];
|
||||
JSGCDoubleArenaList gcDoubleArenaList;
|
||||
JSDHashTable gcRootsHash;
|
||||
JSDHashTable *gcLocksHash;
|
||||
|
535
js/src/jsgc.cpp
535
js/src/jsgc.cpp
@ -168,12 +168,6 @@ JS_STATIC_ASSERT(JSTRACE_XML == 3);
|
||||
*/
|
||||
JS_STATIC_ASSERT(JSTRACE_STRING + 1 == JSTRACE_XML);
|
||||
|
||||
/*
|
||||
* The number of used GCX-types must stay within GCX_LIMIT.
|
||||
*/
|
||||
JS_STATIC_ASSERT(GCX_NTYPES <= GCX_LIMIT);
|
||||
|
||||
|
||||
/*
|
||||
* Check that we can reinterpret double as JSGCDoubleCell.
|
||||
*/
|
||||
@ -184,6 +178,11 @@ JS_STATIC_ASSERT(sizeof(JSGCDoubleCell) == sizeof(double));
|
||||
*/
|
||||
JS_STATIC_ASSERT(JSVAL_NULL == 0);
|
||||
|
||||
/*
|
||||
* Check consistency of external string constants from JSFinalizeGCThingKind.
|
||||
*/
|
||||
JS_STATIC_ASSERT(FINALIZE_EXTERNAL_STRING_LAST - FINALIZE_EXTERNAL_STRING0 ==
|
||||
JS_EXTERNAL_STRING_LIMIT - 1);
|
||||
|
||||
/*
|
||||
* A GC arena contains a fixed number of flag bits for each thing in its heap,
|
||||
@ -343,6 +342,19 @@ struct JSGCArenaInfo {
|
||||
#endif
|
||||
};
|
||||
|
||||
/* GC flag definitions, must fit in 8 bits (type index goes in the low bits). */
|
||||
const uint8 GCF_MARK = JS_BIT(0);
|
||||
const uint8 GCF_FINAL = JS_BIT(1);
|
||||
const uint8 GCF_LOCK = JS_BIT(2); /* lock request bit in API */
|
||||
|
||||
/*
|
||||
* The private JSGCThing struct, which describes a JSRuntime.gcFreeList element.
|
||||
*/
|
||||
struct JSGCThing {
|
||||
JSGCThing *next;
|
||||
uint8 *flagp;
|
||||
};
|
||||
|
||||
/*
|
||||
* Verify that the bit fields are indeed shared and JSGCArenaInfo is as small
|
||||
* as possible. The code does not rely on this check so if on a particular
|
||||
@ -665,16 +677,6 @@ JS_STATIC_ASSERT(sizeof(JSGCThing) >= sizeof(jsdouble));
|
||||
/* We want to use all the available GC thing space for object's slots. */
|
||||
JS_STATIC_ASSERT(sizeof(JSObject) % sizeof(JSGCThing) == 0);
|
||||
|
||||
/*
|
||||
* Ensure that JSObject is allocated from a different GC-list rather than
|
||||
* jsdouble and JSString so we can easily finalize JSObject before these 2
|
||||
* types of GC things. See comments in js_GC.
|
||||
*/
|
||||
JS_STATIC_ASSERT(GC_FREELIST_INDEX(sizeof(JSString)) !=
|
||||
GC_FREELIST_INDEX(sizeof(JSObject)));
|
||||
JS_STATIC_ASSERT(GC_FREELIST_INDEX(sizeof(jsdouble)) !=
|
||||
GC_FREELIST_INDEX(sizeof(JSObject)));
|
||||
|
||||
/*
|
||||
* JSPtrTable capacity growth descriptor. The table grows by powers of two
|
||||
* starting from capacity JSPtrTableInfo.minCapacity, but switching to linear
|
||||
@ -1084,18 +1086,68 @@ DestroyGCArenas(JSRuntime *rt, JSGCArenaInfo *last)
|
||||
}
|
||||
}
|
||||
|
||||
static inline size_t
|
||||
GetFinalizableThingSize(unsigned thingKind)
|
||||
{
|
||||
JS_STATIC_ASSERT(JS_EXTERNAL_STRING_LIMIT == 8);
|
||||
|
||||
static const uint8 map[FINALIZE_LIMIT] = {
|
||||
sizeof(JSObject), /* FINALIZE_OBJECT */
|
||||
sizeof(JSFunction), /* FINALIZE_FUNCTION */
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
sizeof(JSXML), /* FINALIZE_XML */
|
||||
#endif
|
||||
sizeof(JSString), /* FINALIZE_STRING */
|
||||
sizeof(JSString), /* FINALIZE_EXTERNAL_STRING0 */
|
||||
sizeof(JSString), /* FINALIZE_EXTERNAL_STRING1 */
|
||||
sizeof(JSString), /* FINALIZE_EXTERNAL_STRING2 */
|
||||
sizeof(JSString), /* FINALIZE_EXTERNAL_STRING3 */
|
||||
sizeof(JSString), /* FINALIZE_EXTERNAL_STRING4 */
|
||||
sizeof(JSString), /* FINALIZE_EXTERNAL_STRING5 */
|
||||
sizeof(JSString), /* FINALIZE_EXTERNAL_STRING6 */
|
||||
sizeof(JSString), /* FINALIZE_EXTERNAL_STRING7 */
|
||||
};
|
||||
|
||||
JS_ASSERT(thingKind < FINALIZE_LIMIT);
|
||||
return map[thingKind];
|
||||
}
|
||||
|
||||
static inline size_t
|
||||
GetFinalizableArenaTraceKind(JSGCArenaInfo *a)
|
||||
{
|
||||
JS_STATIC_ASSERT(JS_EXTERNAL_STRING_LIMIT == 8);
|
||||
JS_ASSERT(a->list);
|
||||
|
||||
static const uint8 map[FINALIZE_LIMIT] = {
|
||||
JSTRACE_OBJECT, /* FINALIZE_OBJECT */
|
||||
JSTRACE_OBJECT, /* FINALIZE_FUNCTION */
|
||||
#if JS_HAS_XML_SUPPORT /* FINALIZE_XML */
|
||||
JSTRACE_XML,
|
||||
#endif /* FINALIZE_STRING */
|
||||
JSTRACE_STRING,
|
||||
JSTRACE_STRING, /* FINALIZE_EXTERNAL_STRING0 */
|
||||
JSTRACE_STRING, /* FINALIZE_EXTERNAL_STRING1 */
|
||||
JSTRACE_STRING, /* FINALIZE_EXTERNAL_STRING2 */
|
||||
JSTRACE_STRING, /* FINALIZE_EXTERNAL_STRING3 */
|
||||
JSTRACE_STRING, /* FINALIZE_EXTERNAL_STRING4 */
|
||||
JSTRACE_STRING, /* FINALIZE_EXTERNAL_STRING5 */
|
||||
JSTRACE_STRING, /* FINALIZE_EXTERNAL_STRING6 */
|
||||
JSTRACE_STRING, /* FINALIZE_EXTERNAL_STRING7 */
|
||||
};
|
||||
|
||||
JS_ASSERT(a->list->thingKind < FINALIZE_LIMIT);
|
||||
return map[a->list->thingKind];
|
||||
}
|
||||
|
||||
static void
|
||||
InitGCArenaLists(JSRuntime *rt)
|
||||
{
|
||||
uintN i, thingSize;
|
||||
JSGCArenaList *arenaList;
|
||||
|
||||
for (i = 0; i < GC_NUM_FREELISTS; i++) {
|
||||
arenaList = &rt->gcArenaList[i];
|
||||
thingSize = GC_FREELIST_NBYTES(i);
|
||||
for (unsigned i = 0; i != FINALIZE_LIMIT; ++i) {
|
||||
JSGCArenaList *arenaList = &rt->gcArenaList[i];
|
||||
arenaList->thingSize = GetFinalizableThingSize(i);
|
||||
arenaList->last = NULL;
|
||||
arenaList->lastCount = THINGS_PER_ARENA(thingSize);
|
||||
arenaList->thingSize = thingSize;
|
||||
arenaList->lastCount = THINGS_PER_ARENA(arenaList->thingSize);
|
||||
arenaList->thingKind = i;
|
||||
arenaList->freeList = NULL;
|
||||
}
|
||||
rt->gcDoubleArenaList.first = NULL;
|
||||
@ -1105,11 +1157,8 @@ InitGCArenaLists(JSRuntime *rt)
|
||||
static void
|
||||
FinishGCArenaLists(JSRuntime *rt)
|
||||
{
|
||||
uintN i;
|
||||
JSGCArenaList *arenaList;
|
||||
|
||||
for (i = 0; i < GC_NUM_FREELISTS; i++) {
|
||||
arenaList = &rt->gcArenaList[i];
|
||||
for (unsigned i = 0; i < FINALIZE_LIMIT; i++) {
|
||||
JSGCArenaList *arenaList = &rt->gcArenaList[i];
|
||||
DestroyGCArenas(rt, arenaList->last);
|
||||
arenaList->last = NULL;
|
||||
arenaList->lastCount = THINGS_PER_ARENA(arenaList->thingSize);
|
||||
@ -1137,73 +1186,41 @@ GetGCThingFlags(void *thing)
|
||||
return THING_FLAGP(a, index);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function returns null when thing is jsdouble.
|
||||
*/
|
||||
static uint8 *
|
||||
GetGCThingFlagsOrNull(void *thing)
|
||||
{
|
||||
JSGCArenaInfo *a;
|
||||
uint32 index;
|
||||
|
||||
if (JSString::isStatic(thing))
|
||||
return NULL;
|
||||
a = THING_TO_ARENA(thing);
|
||||
if (!a->list)
|
||||
return NULL;
|
||||
index = THING_TO_INDEX(thing, a->list->thingSize);
|
||||
return THING_FLAGP(a, index);
|
||||
}
|
||||
|
||||
intN
|
||||
js_GetExternalStringGCType(JSString *str)
|
||||
{
|
||||
JS_STATIC_ASSERT(FINALIZE_STRING + 1 == FINALIZE_EXTERNAL_STRING0);
|
||||
JS_ASSERT(!JSString::isStatic(str));
|
||||
|
||||
uintN type = (uintN) *GetGCThingFlags(str) & GCF_TYPEMASK;
|
||||
JS_ASSERT(type == GCX_STRING || type >= GCX_EXTERNAL_STRING);
|
||||
return (type == GCX_STRING) ? -1 : (intN) (type - GCX_EXTERNAL_STRING);
|
||||
}
|
||||
|
||||
static uint32
|
||||
MapGCFlagsToTraceKind(uintN flags)
|
||||
{
|
||||
uint32 type;
|
||||
|
||||
type = flags & GCF_TYPEMASK;
|
||||
JS_ASSERT(type != GCX_DOUBLE);
|
||||
JS_ASSERT(type < GCX_NTYPES);
|
||||
return (type < GCX_EXTERNAL_STRING) ? type : JSTRACE_STRING;
|
||||
unsigned thingKind = THING_TO_ARENA(str)->list->thingKind;
|
||||
JS_ASSERT(IsFinalizableStringKind(thingKind));
|
||||
return intN(thingKind) - intN(FINALIZE_EXTERNAL_STRING0);
|
||||
}
|
||||
|
||||
JS_FRIEND_API(uint32)
|
||||
js_GetGCThingTraceKind(void *thing)
|
||||
{
|
||||
JSGCArenaInfo *a;
|
||||
uint32 index;
|
||||
|
||||
if (JSString::isStatic(thing))
|
||||
return JSTRACE_STRING;
|
||||
|
||||
a = THING_TO_ARENA(thing);
|
||||
JSGCArenaInfo *a = THING_TO_ARENA(thing);
|
||||
if (!a->list)
|
||||
return JSTRACE_DOUBLE;
|
||||
|
||||
index = THING_TO_INDEX(thing, a->list->thingSize);
|
||||
return MapGCFlagsToTraceKind(*THING_FLAGP(a, index));
|
||||
return GetFinalizableArenaTraceKind(a);
|
||||
}
|
||||
|
||||
JSRuntime*
|
||||
js_GetGCStringRuntime(JSString *str)
|
||||
{
|
||||
JSGCArenaList *list;
|
||||
JSGCArenaList *list = THING_TO_ARENA(str)->list;
|
||||
JS_ASSERT(list->thingSize == sizeof(JSString));
|
||||
|
||||
list = THING_TO_ARENA(str)->list;
|
||||
|
||||
JS_ASSERT(list->thingSize == sizeof(JSGCThing));
|
||||
JS_ASSERT(GC_FREELIST_INDEX(sizeof(JSGCThing)) == 0);
|
||||
|
||||
return (JSRuntime *)((uint8 *)list - offsetof(JSRuntime, gcArenaList));
|
||||
unsigned i = list->thingKind;
|
||||
JS_ASSERT(i == FINALIZE_STRING ||
|
||||
(FINALIZE_EXTERNAL_STRING0 <= i &&
|
||||
i < FINALIZE_EXTERNAL_STRING0 + JS_EXTERNAL_STRING_LIMIT));
|
||||
return (JSRuntime *)((uint8 *)(list - i) -
|
||||
offsetof(JSRuntime, gcArenaList));
|
||||
}
|
||||
|
||||
JSBool
|
||||
@ -1766,8 +1783,9 @@ IsGCThresholdReached(JSRuntime *rt)
|
||||
}
|
||||
|
||||
template <typename T, typename NewbornType>
|
||||
static JS_INLINE T*
|
||||
NewGCThing(JSContext *cx, uintN flags, NewbornType** newbornRoot)
|
||||
static inline T*
|
||||
NewFinalizableGCThing(JSContext *cx, unsigned thingKind,
|
||||
NewbornType** newbornRoot)
|
||||
{
|
||||
JSRuntime *rt;
|
||||
bool doGC;
|
||||
@ -1789,11 +1807,11 @@ NewGCThing(JSContext *cx, uintN flags, NewbornType** newbornRoot)
|
||||
uintN maxFreeThings; /* max to take from the global free list */
|
||||
#endif
|
||||
|
||||
JS_ASSERT((flags & GCF_TYPEMASK) != GCX_DOUBLE);
|
||||
rt = cx->runtime;
|
||||
|
||||
size_t nbytes = sizeof(T);
|
||||
JS_ASSERT(nbytes == GetFinalizableThingSize(thingKind));
|
||||
JS_ASSERT(JS_ROUNDUP(nbytes, sizeof(JSGCThing)) == nbytes);
|
||||
uintN flindex = GC_FREELIST_INDEX(nbytes);
|
||||
|
||||
/* Updates of metering counters here may not be thread-safe. */
|
||||
METER(astats = &cx->runtime->gcStats.arenaStats[flindex]);
|
||||
@ -1803,7 +1821,7 @@ NewGCThing(JSContext *cx, uintN flags, NewbornType** newbornRoot)
|
||||
gcLocked = JS_FALSE;
|
||||
JS_ASSERT(cx->thread);
|
||||
|
||||
JSGCThing *&freeList = cx->thread->gcFreeLists[flindex];
|
||||
JSGCThing *&freeList = cx->thread->gcFreeLists[thingKind];
|
||||
thing = freeList;
|
||||
localMallocBytes = JS_THREAD_DATA(cx)->gcMallocBytes;
|
||||
if (thing && rt->gcMaxMallocBytes - rt->gcMallocBytes > localMallocBytes) {
|
||||
@ -1837,7 +1855,7 @@ NewGCThing(JSContext *cx, uintN flags, NewbornType** newbornRoot)
|
||||
goto testReservedObjects;
|
||||
#endif
|
||||
|
||||
arenaList = &rt->gcArenaList[flindex];
|
||||
arenaList = &rt->gcArenaList[thingKind];
|
||||
doGC = IsGCThresholdReached(rt);
|
||||
for (;;) {
|
||||
if (doGC
|
||||
@ -2000,11 +2018,11 @@ testReservedObjects:
|
||||
}
|
||||
|
||||
/* We can't fail now, so update flags. */
|
||||
*flagp = (uint8)flags;
|
||||
*flagp = uint8(0);
|
||||
|
||||
#ifdef DEBUG_gchist
|
||||
gchist[gchpos].lastDitch = doGC;
|
||||
gchist[gchpos].freeList = rt->gcArenaList[flindex].freeList;
|
||||
gchist[gchpos].freeList = rt->gcArenaList[thingKind].freeList;
|
||||
if (++gchpos == NGCHIST)
|
||||
gchpos = 0;
|
||||
#endif
|
||||
@ -2031,34 +2049,38 @@ fail:
|
||||
JSObject *
|
||||
js_NewGCObject(JSContext *cx)
|
||||
{
|
||||
return NewGCThing<JSObject>(cx, GCX_OBJECT, &cx->weakRoots.newbornObject);
|
||||
return NewFinalizableGCThing<JSObject>(cx, FINALIZE_OBJECT,
|
||||
&cx->weakRoots.newbornObject);
|
||||
}
|
||||
|
||||
JSString *
|
||||
js_NewGCString(JSContext *cx)
|
||||
{
|
||||
return NewGCThing<JSString>(cx, GCX_STRING, &cx->weakRoots.newbornString);
|
||||
return NewFinalizableGCThing<JSString>(cx, FINALIZE_STRING,
|
||||
&cx->weakRoots.newbornString);
|
||||
}
|
||||
|
||||
JSString *
|
||||
js_NewGCExternalString(JSContext *cx, uintN type)
|
||||
{
|
||||
JS_ASSERT(type < JS_EXTERNAL_STRING_LIMIT);
|
||||
return NewGCThing<JSString>(cx, GCX_EXTERNAL_STRING + type,
|
||||
&cx->weakRoots.newbornExternalString[type]);
|
||||
return NewFinalizableGCThing<JSString>(cx, FINALIZE_EXTERNAL_STRING0 + type,
|
||||
&cx->weakRoots.newbornExternalString[type]);
|
||||
}
|
||||
|
||||
JSFunction *
|
||||
js_NewGCFunction(JSContext *cx)
|
||||
{
|
||||
return NewGCThing<JSFunction>(cx, GCX_OBJECT, &cx->weakRoots.newbornObject);
|
||||
return NewFinalizableGCThing<JSFunction>(cx, FINALIZE_FUNCTION,
|
||||
&cx->weakRoots.newbornObject);
|
||||
}
|
||||
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
JSXML *
|
||||
js_NewGCXML(JSContext *cx)
|
||||
{
|
||||
return NewGCThing<JSXML>(cx, GCX_XML, &cx->weakRoots.newbornXML);
|
||||
return NewFinalizableGCThing<JSXML>(cx,FINALIZE_XML,
|
||||
&cx->weakRoots.newbornXML);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -2315,11 +2337,23 @@ js_RemoveAsGCBytes(JSRuntime *rt, size_t sz)
|
||||
* they have no descendants to mark during the GC. Currently the optimization
|
||||
* is only used for non-dependant strings.
|
||||
*/
|
||||
#define GC_THING_IS_SHALLOW(flagp, thing) \
|
||||
((flagp) && \
|
||||
((*(flagp) & GCF_TYPEMASK) >= GCX_EXTERNAL_STRING || \
|
||||
((*(flagp) & GCF_TYPEMASK) == GCX_STRING && \
|
||||
!((JSString *) (thing))->isDependent())))
|
||||
static uint8 *
|
||||
GetShallowGCThingFlag(void *thing)
|
||||
{
|
||||
if (JSString::isStatic(thing))
|
||||
return NULL;
|
||||
JSGCArenaInfo *a = THING_TO_ARENA(thing);
|
||||
if (!a->list || !IsFinalizableStringKind(a->list->thingKind))
|
||||
return NULL;
|
||||
|
||||
JS_ASSERT(sizeof(JSString) == a->list->thingSize);
|
||||
JSString *str = (JSString *) thing;
|
||||
if (str->isDependent())
|
||||
return NULL;
|
||||
|
||||
uint32 index = THING_TO_INDEX(thing, sizeof(JSString));
|
||||
return THING_FLAGP(a, index);
|
||||
}
|
||||
|
||||
/* This is compatible with JSDHashEntryStub. */
|
||||
typedef struct JSGCLockHashEntry {
|
||||
@ -2331,22 +2365,19 @@ typedef struct JSGCLockHashEntry {
|
||||
JSBool
|
||||
js_LockGCThingRT(JSRuntime *rt, void *thing)
|
||||
{
|
||||
JSBool shallow, ok;
|
||||
uint8 *flagp;
|
||||
JSGCLockHashEntry *lhe;
|
||||
|
||||
if (!thing)
|
||||
return JS_TRUE;
|
||||
|
||||
flagp = GetGCThingFlagsOrNull(thing);
|
||||
JS_LOCK_GC(rt);
|
||||
shallow = GC_THING_IS_SHALLOW(flagp, thing);
|
||||
|
||||
/*
|
||||
* Avoid adding a rt->gcLocksHash entry for shallow things until someone
|
||||
* nests a lock.
|
||||
*/
|
||||
if (shallow && !(*flagp & GCF_LOCK)) {
|
||||
uint8 *flagp = GetShallowGCThingFlag(thing);
|
||||
JSBool ok;
|
||||
JSGCLockHashEntry *lhe;
|
||||
if (flagp && !(*flagp & GCF_LOCK)) {
|
||||
*flagp |= GCF_LOCK;
|
||||
METER(rt->gcStats.lock++);
|
||||
ok = JS_TRUE;
|
||||
@ -2387,18 +2418,14 @@ js_LockGCThingRT(JSRuntime *rt, void *thing)
|
||||
JSBool
|
||||
js_UnlockGCThingRT(JSRuntime *rt, void *thing)
|
||||
{
|
||||
uint8 *flagp;
|
||||
JSBool shallow;
|
||||
JSGCLockHashEntry *lhe;
|
||||
|
||||
if (!thing)
|
||||
return JS_TRUE;
|
||||
|
||||
flagp = GetGCThingFlagsOrNull(thing);
|
||||
JS_LOCK_GC(rt);
|
||||
shallow = GC_THING_IS_SHALLOW(flagp, thing);
|
||||
|
||||
if (shallow && !(*flagp & GCF_LOCK))
|
||||
uint8 *flagp = GetShallowGCThingFlag(thing);
|
||||
JSGCLockHashEntry *lhe;
|
||||
if (flagp && !(*flagp & GCF_LOCK))
|
||||
goto out;
|
||||
if (!rt->gcLocksHash ||
|
||||
(lhe = (JSGCLockHashEntry *)
|
||||
@ -2406,7 +2433,7 @@ js_UnlockGCThingRT(JSRuntime *rt, void *thing)
|
||||
JS_DHASH_LOOKUP),
|
||||
JS_DHASH_ENTRY_IS_FREE(&lhe->hdr))) {
|
||||
/* Shallow entry is not in the hash -> clear its lock bit. */
|
||||
if (shallow)
|
||||
if (flagp)
|
||||
*flagp &= ~GCF_LOCK;
|
||||
else
|
||||
goto out;
|
||||
@ -2523,7 +2550,7 @@ TraceDelayedChildren(JSTracer *trc)
|
||||
{
|
||||
JSRuntime *rt;
|
||||
JSGCArenaInfo *a, *aprev;
|
||||
uint32 thingSize;
|
||||
uint32 thingSize, traceKind;
|
||||
uint32 thingsPerUntracedBit;
|
||||
uint32 untracedBitIndex, thingIndex, indexLimit, endIndex;
|
||||
JSGCThing *thing;
|
||||
@ -2545,6 +2572,7 @@ TraceDelayedChildren(JSTracer *trc)
|
||||
JS_ASSERT(a->prevUntracedPage != 0);
|
||||
JS_ASSERT(rt->gcUntracedArenaStackTop->prevUntracedPage != 0);
|
||||
thingSize = a->list->thingSize;
|
||||
traceKind = GetFinalizableArenaTraceKind(a);
|
||||
indexLimit = (a == a->list->last)
|
||||
? a->list->lastCount
|
||||
: THINGS_PER_ARENA(thingSize);
|
||||
@ -2583,7 +2611,7 @@ TraceDelayedChildren(JSTracer *trc)
|
||||
--rt->gcTraceLaterCount;
|
||||
#endif
|
||||
thing = FLAGP_TO_THING(flagp, thingSize);
|
||||
JS_TraceChildren(trc, thing, MapGCFlagsToTraceKind(*flagp));
|
||||
JS_TraceChildren(trc, thing, traceKind);
|
||||
} while (++thingIndex != endIndex);
|
||||
}
|
||||
|
||||
@ -2660,9 +2688,10 @@ JS_CallTracer(JSTracer *trc, void *thing, uint32 kind)
|
||||
for (;;) {
|
||||
if (JSString::isStatic(thing))
|
||||
goto out;
|
||||
flagp = THING_TO_FLAGP(thing, sizeof(JSGCThing));
|
||||
a = THING_TO_ARENA(thing);
|
||||
flagp = THING_FLAGP(a, THING_TO_INDEX(thing, sizeof(JSString)));
|
||||
JS_ASSERT((*flagp & GCF_FINAL) == 0);
|
||||
JS_ASSERT(kind == MapGCFlagsToTraceKind(*flagp));
|
||||
JS_ASSERT(kind == GetFinalizableArenaTraceKind(a));
|
||||
if (!((JSString *) thing)->isDependent()) {
|
||||
*flagp |= GCF_MARK;
|
||||
goto out;
|
||||
@ -2675,8 +2704,8 @@ JS_CallTracer(JSTracer *trc, void *thing, uint32 kind)
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
JS_ASSERT(kind == GetFinalizableArenaTraceKind(THING_TO_ARENA(thing)));
|
||||
flagp = GetGCThingFlags(thing);
|
||||
JS_ASSERT(kind == MapGCFlagsToTraceKind(*flagp));
|
||||
if (*flagp & GCF_MARK)
|
||||
goto out;
|
||||
|
||||
@ -2766,33 +2795,26 @@ gc_root_traversal(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 num,
|
||||
JSVAL_IS_GCTHING(v) &&
|
||||
!JSString::isStatic(JSVAL_TO_GCTHING(v))) {
|
||||
#ifdef DEBUG
|
||||
JSBool root_points_to_gcArenaList = JS_FALSE;
|
||||
bool root_points_to_gcArenaList = false;
|
||||
jsuword thing = (jsuword) JSVAL_TO_GCTHING(v);
|
||||
JSRuntime *rt;
|
||||
uintN i;
|
||||
JSGCArenaList *arenaList;
|
||||
uint32 thingSize;
|
||||
JSGCArenaInfo *a;
|
||||
size_t limit;
|
||||
|
||||
rt = trc->context->runtime;
|
||||
for (i = 0; i < GC_NUM_FREELISTS; i++) {
|
||||
arenaList = &rt->gcArenaList[i];
|
||||
thingSize = arenaList->thingSize;
|
||||
limit = (size_t) arenaList->lastCount * thingSize;
|
||||
for (a = arenaList->last; a; a = a->prev) {
|
||||
JSRuntime *rt = trc->context->runtime;
|
||||
for (unsigned i = 0; i != FINALIZE_LIMIT; i++) {
|
||||
JSGCArenaList *arenaList = &rt->gcArenaList[i];
|
||||
size_t thingSize = arenaList->thingSize;
|
||||
size_t limit = arenaList->lastCount * thingSize;
|
||||
for (JSGCArenaInfo *a = arenaList->last; a; a = a->prev) {
|
||||
if (thing - ARENA_INFO_TO_START(a) < limit) {
|
||||
root_points_to_gcArenaList = JS_TRUE;
|
||||
root_points_to_gcArenaList = true;
|
||||
break;
|
||||
}
|
||||
limit = (size_t) THINGS_PER_ARENA(thingSize) * thingSize;
|
||||
limit = THINGS_PER_ARENA(thingSize) * thingSize;
|
||||
}
|
||||
}
|
||||
if (!root_points_to_gcArenaList) {
|
||||
for (a = rt->gcDoubleArenaList.first; a; a = a->prev) {
|
||||
for (JSGCArenaInfo *a = rt->gcDoubleArenaList.first; a; a = a->prev) {
|
||||
if (thing - ARENA_INFO_TO_START(a) <
|
||||
DOUBLES_PER_ARENA * sizeof(jsdouble)) {
|
||||
root_points_to_gcArenaList = JS_TRUE;
|
||||
root_points_to_gcArenaList = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -3069,8 +3091,9 @@ MarkReservedObjects(JSTraceMonitor *tm)
|
||||
{
|
||||
/* Keep the reserved objects. */
|
||||
for (JSObject *obj = tm->reservedObjects; obj; obj = JSVAL_TO_OBJECT(obj->fslots[0])) {
|
||||
JS_ASSERT(js_GetGCThingTraceKind(obj) == JSTRACE_OBJECT);
|
||||
|
||||
uint8 *flagp = GetGCThingFlags(obj);
|
||||
JS_ASSERT((*flagp & GCF_TYPEMASK) == GCX_OBJECT);
|
||||
JS_ASSERT(*flagp != GCF_FINAL);
|
||||
*flagp |= GCF_MARK;
|
||||
}
|
||||
@ -3189,9 +3212,11 @@ js_DestroyScriptsToGC(JSContext *cx, JSThreadData *data)
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
FinalizeObject(JSContext *cx, JSObject *obj)
|
||||
static inline void
|
||||
FinalizeGCThing(JSContext *cx, JSObject *obj, unsigned thingKind)
|
||||
{
|
||||
JS_ASSERT(thingKind == FINALIZE_FUNCTION || thingKind == FINALIZE_OBJECT);
|
||||
|
||||
/* Cope with stillborn objects that have no map. */
|
||||
if (!obj->map)
|
||||
return;
|
||||
@ -3216,6 +3241,21 @@ FinalizeObject(JSContext *cx, JSObject *obj)
|
||||
js_FreeSlots(cx, obj);
|
||||
}
|
||||
|
||||
static inline void
|
||||
FinalizeGCThing(JSContext *cx, JSFunction *fun, unsigned thingKind)
|
||||
{
|
||||
JS_ASSERT(thingKind == FINALIZE_FUNCTION);
|
||||
FinalizeGCThing(cx, FUN_OBJECT(fun), thingKind);
|
||||
}
|
||||
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
static inline void
|
||||
FinalizeGCThing(JSContext *cx, JSXML *xml, unsigned thingKind)
|
||||
{
|
||||
js_FinalizeXML(cx, xml);
|
||||
}
|
||||
#endif
|
||||
|
||||
JS_STATIC_ASSERT(JS_EXTERNAL_STRING_LIMIT == 8);
|
||||
static JSStringFinalizeOp str_finalizers[JS_EXTERNAL_STRING_LIMIT] = {
|
||||
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
|
||||
@ -3238,8 +3278,8 @@ js_ChangeExternalStringFinalizer(JSStringFinalizeOp oldop,
|
||||
* 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, intN type, JSContext *cx)
|
||||
static void
|
||||
FinalizeString(JSRuntime *rt, JSString *str, unsigned thingKind, JSContext *cx)
|
||||
{
|
||||
jschar *chars;
|
||||
JSBool valid;
|
||||
@ -3247,9 +3287,10 @@ js_FinalizeStringRT(JSRuntime *rt, JSString *str, intN type, JSContext *cx)
|
||||
|
||||
JS_RUNTIME_UNMETER(rt, liveStrings);
|
||||
JS_ASSERT(!JSString::isStatic(str));
|
||||
JS_ASSERT(IsFinalizableStringKind(thingKind));
|
||||
if (str->isDependent()) {
|
||||
/* A dependent string can not be external and must be valid. */
|
||||
JS_ASSERT(type < 0);
|
||||
JS_ASSERT(thingKind == FINALIZE_STRING);
|
||||
JS_ASSERT(str->dependentBase());
|
||||
JS_RUNTIME_UNMETER(rt, liveDependentStrings);
|
||||
valid = JS_TRUE;
|
||||
@ -3258,13 +3299,14 @@ js_FinalizeStringRT(JSRuntime *rt, JSString *str, intN type, JSContext *cx)
|
||||
chars = str->flatChars();
|
||||
valid = (chars != NULL);
|
||||
if (valid) {
|
||||
if (type < 0) {
|
||||
if (thingKind == FINALIZE_STRING) {
|
||||
if (cx)
|
||||
cx->free(chars);
|
||||
else
|
||||
rt->free(chars);
|
||||
} else {
|
||||
JS_ASSERT((uintN) type < JS_ARRAY_LENGTH(str_finalizers));
|
||||
unsigned type = thingKind - FINALIZE_EXTERNAL_STRING0;
|
||||
JS_ASSERT(type < JS_ARRAY_LENGTH(str_finalizers));
|
||||
finalizer = str_finalizers[type];
|
||||
if (finalizer) {
|
||||
/*
|
||||
@ -3280,6 +3322,97 @@ js_FinalizeStringRT(JSRuntime *rt, JSString *str, intN type, JSContext *cx)
|
||||
js_PurgeDeflatedStringCache(rt, str);
|
||||
}
|
||||
|
||||
static inline void
|
||||
FinalizeGCThing(JSContext *cx, JSString *str, unsigned thingKind)
|
||||
{
|
||||
return FinalizeString(cx->runtime, str, thingKind, cx);
|
||||
}
|
||||
|
||||
void
|
||||
js_FinalizeStringRT(JSRuntime *rt, JSString *str)
|
||||
{
|
||||
JS_ASSERT(!JSString::isStatic(str));
|
||||
|
||||
unsigned thingKind = THING_TO_ARENA(str)->list->thingKind;
|
||||
FinalizeString(rt, str, thingKind, NULL);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static void
|
||||
FinalizeArenaList(JSContext *cx, unsigned thingKind,
|
||||
JSGCArenaInfo **emptyArenas)
|
||||
{
|
||||
JSGCArenaList *arenaList = &cx->runtime->gcArenaList[thingKind];
|
||||
JS_ASSERT(sizeof(T) == arenaList->thingSize);
|
||||
|
||||
JSGCArenaInfo **ap = &arenaList->last;
|
||||
JSGCArenaInfo *a = *ap;
|
||||
if (!a)
|
||||
return;
|
||||
|
||||
JS_ASSERT(arenaList->lastCount > 0);
|
||||
|
||||
JSGCThing *freeList = NULL;
|
||||
arenaList->freeList = NULL;
|
||||
|
||||
uint32 indexLimit = THINGS_PER_ARENA(sizeof(T));
|
||||
uint8 *flagp = THING_FLAGP(a, arenaList->lastCount - 1);
|
||||
|
||||
#ifdef JS_GCMETER
|
||||
uint32 nlivearenas = 0, nkilledarenas = 0, nthings = 0;
|
||||
#endif
|
||||
for (;;) {
|
||||
JS_ASSERT(a->prevUntracedPage == 0);
|
||||
JS_ASSERT(a->u.untracedThings == 0);
|
||||
|
||||
bool allClear = true;
|
||||
do {
|
||||
if (*flagp & (GCF_MARK | GCF_LOCK)) {
|
||||
*flagp &= ~GCF_MARK;
|
||||
allClear = false;
|
||||
METER(nthings++);
|
||||
} else {
|
||||
JSGCThing *thing = FLAGP_TO_THING(flagp, sizeof(T));
|
||||
if (!(*flagp & GCF_FINAL)) {
|
||||
/* Call the finalizer with GCF_FINAL ORed into flags. */
|
||||
*flagp |= GCF_FINAL;
|
||||
FinalizeGCThing(cx, (T *) thing, thingKind);
|
||||
#ifdef DEBUG
|
||||
memset(thing, JS_FREE_PATTERN, sizeof(T));
|
||||
#endif
|
||||
}
|
||||
thing->flagp = flagp;
|
||||
thing->next = freeList;
|
||||
freeList = thing;
|
||||
}
|
||||
} while (++flagp != THING_FLAGS_END(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;
|
||||
a->prev = *emptyArenas;
|
||||
*emptyArenas = a;
|
||||
METER(nkilledarenas++);
|
||||
} else {
|
||||
arenaList->freeList = freeList;
|
||||
ap = &a->prev;
|
||||
METER(nlivearenas++);
|
||||
}
|
||||
if (!(a = *ap))
|
||||
break;
|
||||
flagp = THING_FLAGP(a, indexLimit - 1);
|
||||
}
|
||||
|
||||
METER(UpdateArenaStats(&rt->gcStats.arenaStats[thingKind],
|
||||
nlivearenas, nkilledarenas, nthings));
|
||||
}
|
||||
|
||||
/*
|
||||
* The gckind flag bit GC_LOCK_HELD indicates a call from js_NewGCThing with
|
||||
* rt->gcLock already held, so the lock should be kept on return.
|
||||
@ -3290,14 +3423,8 @@ js_GC(JSContext *cx, JSGCInvocationKind gckind)
|
||||
JSRuntime *rt;
|
||||
JSBool keepAtoms;
|
||||
JSGCCallback callback;
|
||||
uintN i, type;
|
||||
JSTracer trc;
|
||||
uint32 thingSize, indexLimit;
|
||||
JSGCArenaInfo *a, **ap, *emptyArenas;
|
||||
uint8 flags, *flagp;
|
||||
JSGCThing *thing, *freeList;
|
||||
JSGCArenaList *arenaList;
|
||||
JSBool allClear;
|
||||
JSGCArenaInfo *emptyArenas, *a, **ap;
|
||||
#ifdef JS_THREADSAFE
|
||||
uint32 requestDebit;
|
||||
#endif
|
||||
@ -3586,107 +3713,25 @@ js_GC(JSContext *cx, JSGCInvocationKind gckind)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Here we need to ensure that JSObject instances are finalized before GC-
|
||||
* allocated JSString and jsdouble instances so object's finalizer can
|
||||
* access them even if they will be freed. For that we simply finalize the
|
||||
* list containing JSObject first since the static assert at the beginning
|
||||
* of the file guarantees that JSString and jsdouble instances are
|
||||
* allocated from a different list.
|
||||
* We finalize JSObject instances before JSString, double and other GC
|
||||
* things to ensure that object's finalizer can access them even if they
|
||||
* will be freed.
|
||||
*
|
||||
* To minimize the number of checks per each to be freed object and
|
||||
* function we use separated list finalizers when a debug hook is
|
||||
* installed.
|
||||
*/
|
||||
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];
|
||||
ap = &arenaList->last;
|
||||
if (!(a = *ap))
|
||||
continue;
|
||||
|
||||
JS_ASSERT(arenaList->lastCount > 0);
|
||||
arenaList->freeList = NULL;
|
||||
freeList = NULL;
|
||||
thingSize = arenaList->thingSize;
|
||||
indexLimit = THINGS_PER_ARENA(thingSize);
|
||||
flagp = THING_FLAGP(a, arenaList->lastCount - 1);
|
||||
METER((nlivearenas = 0, nkilledarenas = 0, nthings = 0));
|
||||
for (;;) {
|
||||
JS_ASSERT(a->prevUntracedPage == 0);
|
||||
JS_ASSERT(a->u.untracedThings == 0);
|
||||
allClear = JS_TRUE;
|
||||
do {
|
||||
flags = *flagp;
|
||||
if (flags & (GCF_MARK | GCF_LOCK)) {
|
||||
*flagp &= ~GCF_MARK;
|
||||
allClear = JS_FALSE;
|
||||
METER(nthings++);
|
||||
} else {
|
||||
thing = FLAGP_TO_THING(flagp, thingSize);
|
||||
if (!(flags & GCF_FINAL)) {
|
||||
/*
|
||||
* Call the finalizer with GCF_FINAL ORed into flags.
|
||||
*/
|
||||
*flagp = (uint8)(flags | GCF_FINAL);
|
||||
type = flags & GCF_TYPEMASK;
|
||||
switch (type) {
|
||||
case GCX_OBJECT:
|
||||
FinalizeObject(cx, (JSObject *) thing);
|
||||
break;
|
||||
FinalizeArenaList<JSObject>(cx, FINALIZE_OBJECT, &emptyArenas);
|
||||
FinalizeArenaList<JSFunction>(cx, FINALIZE_FUNCTION, &emptyArenas);
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
case GCX_XML:
|
||||
js_FinalizeXML(cx, (JSXML *) thing);
|
||||
break;
|
||||
FinalizeArenaList<JSXML>(cx, FINALIZE_XML, &emptyArenas);
|
||||
#endif
|
||||
default:
|
||||
JS_ASSERT(type == GCX_STRING ||
|
||||
type - GCX_EXTERNAL_STRING <
|
||||
GCX_NTYPES - GCX_EXTERNAL_STRING);
|
||||
js_FinalizeStringRT(rt, (JSString *) thing,
|
||||
(intN) (type -
|
||||
GCX_EXTERNAL_STRING),
|
||||
cx);
|
||||
break;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
memset(thing, JS_FREE_PATTERN, thingSize);
|
||||
#endif
|
||||
}
|
||||
thing->flagp = flagp;
|
||||
thing->next = freeList;
|
||||
freeList = thing;
|
||||
}
|
||||
} while (++flagp != THING_FLAGS_END(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;
|
||||
a->prev = emptyArenas;
|
||||
emptyArenas = a;
|
||||
METER(nkilledarenas++);
|
||||
} else {
|
||||
arenaList->freeList = freeList;
|
||||
ap = &a->prev;
|
||||
METER(nlivearenas++);
|
||||
}
|
||||
if (!(a = *ap))
|
||||
break;
|
||||
flagp = THING_FLAGP(a, indexLimit - 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* We use arenaList - &rt->gcArenaList[0], not i, as the stat index
|
||||
* due to the enumeration reorder at the beginning of the loop.
|
||||
*/
|
||||
METER(UpdateArenaStats(&rt->gcStats.arenaStats[arenaList -
|
||||
&rt->gcArenaList[0]],
|
||||
nlivearenas, nkilledarenas, nthings));
|
||||
FinalizeArenaList<JSString>(cx, FINALIZE_STRING, &emptyArenas);
|
||||
for (unsigned i = FINALIZE_EXTERNAL_STRING0;
|
||||
i <= FINALIZE_EXTERNAL_STRING_LAST;
|
||||
++i) {
|
||||
FinalizeArenaList<JSString>(cx, i, &emptyArenas);
|
||||
}
|
||||
|
||||
ap = &rt->gcDoubleArenaList.first;
|
||||
@ -3731,7 +3776,7 @@ js_GC(JSContext *cx, JSGCInvocationKind gckind)
|
||||
js_SweepScriptFilenames(rt);
|
||||
|
||||
/*
|
||||
* Destroy arenas after we finished the sweeping sofinalizers can safely
|
||||
* Destroy arenas after we finished the sweeping so finalizers can safely
|
||||
* use js_IsAboutToBeFinalized().
|
||||
*/
|
||||
DestroyGCArenas(rt, emptyArenas);
|
||||
|
@ -58,33 +58,8 @@ JS_BEGIN_EXTERN_C
|
||||
*/
|
||||
#define JSTRACE_LIMIT 4
|
||||
|
||||
/*
|
||||
* We use the trace kinds as the types for all GC things except external
|
||||
* strings.
|
||||
*/
|
||||
#define GCX_OBJECT JSTRACE_OBJECT /* JSObject */
|
||||
#define GCX_DOUBLE JSTRACE_DOUBLE /* jsdouble */
|
||||
#define GCX_STRING JSTRACE_STRING /* JSString */
|
||||
#define GCX_XML JSTRACE_XML /* JSXML */
|
||||
#define GCX_EXTERNAL_STRING JSTRACE_LIMIT /* JSString with external
|
||||
chars */
|
||||
const uintN JS_EXTERNAL_STRING_LIMIT = 8;
|
||||
|
||||
/*
|
||||
* The number of defined GC types and the maximum limit for the number of
|
||||
* possible GC types.
|
||||
*/
|
||||
#define GCX_NTYPES (GCX_EXTERNAL_STRING + JS_EXTERNAL_STRING_LIMIT)
|
||||
#define GCX_LIMIT_LOG2 4 /* type index bits */
|
||||
#define GCX_LIMIT JS_BIT(GCX_LIMIT_LOG2)
|
||||
|
||||
/* GC flag definitions, must fit in 8 bits (type index goes in the low bits). */
|
||||
#define GCF_TYPEMASK JS_BITMASK(GCX_LIMIT_LOG2)
|
||||
#define GCF_MARK JS_BIT(GCX_LIMIT_LOG2)
|
||||
#define GCF_FINAL JS_BIT(GCX_LIMIT_LOG2 + 1)
|
||||
#define GCF_LOCKSHIFT (GCX_LIMIT_LOG2 + 2) /* lock bit shift */
|
||||
#define GCF_LOCK JS_BIT(GCF_LOCKSHIFT) /* lock request bit in API */
|
||||
|
||||
/*
|
||||
* Get the type of the external string or -1 if the string was not created
|
||||
* with JS_NewExternalString.
|
||||
@ -151,19 +126,6 @@ typedef struct JSPtrTable {
|
||||
extern JSBool
|
||||
js_RegisterCloseableIterator(JSContext *cx, JSObject *obj);
|
||||
|
||||
/*
|
||||
* The private JSGCThing struct, which describes a gcFreeList element.
|
||||
*/
|
||||
struct JSGCThing {
|
||||
JSGCThing *next;
|
||||
uint8 *flagp;
|
||||
};
|
||||
|
||||
#define GC_NBYTES_MAX (10 * sizeof(JSGCThing))
|
||||
#define GC_NUM_FREELISTS (GC_NBYTES_MAX / sizeof(JSGCThing))
|
||||
#define GC_FREELIST_NBYTES(i) (((i) + 1) * sizeof(JSGCThing))
|
||||
#define GC_FREELIST_INDEX(n) (((n) / sizeof(JSGCThing)) - 1)
|
||||
|
||||
/*
|
||||
* Allocates a new GC thing of the given size. After a successful allocation
|
||||
* the caller must fully initialize the thing before calling any function that
|
||||
@ -289,6 +251,36 @@ typedef enum JSGCInvocationKind {
|
||||
extern void
|
||||
js_GC(JSContext *cx, JSGCInvocationKind gckind);
|
||||
|
||||
/*
|
||||
* The kind of GC thing with a finalizer. The external strings follow the
|
||||
* ordinary string to simplify js_GetExternalStringGCType.
|
||||
*/
|
||||
enum JSFinalizeGCThingKind {
|
||||
FINALIZE_OBJECT,
|
||||
FINALIZE_FUNCTION,
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
FINALIZE_XML,
|
||||
#endif
|
||||
FINALIZE_STRING,
|
||||
FINALIZE_EXTERNAL_STRING0,
|
||||
FINALIZE_EXTERNAL_STRING1,
|
||||
FINALIZE_EXTERNAL_STRING2,
|
||||
FINALIZE_EXTERNAL_STRING3,
|
||||
FINALIZE_EXTERNAL_STRING4,
|
||||
FINALIZE_EXTERNAL_STRING5,
|
||||
FINALIZE_EXTERNAL_STRING6,
|
||||
FINALIZE_EXTERNAL_STRING7,
|
||||
FINALIZE_EXTERNAL_STRING_LAST = FINALIZE_EXTERNAL_STRING7,
|
||||
FINALIZE_LIMIT
|
||||
};
|
||||
|
||||
static inline bool
|
||||
IsFinalizableStringKind(unsigned thingKind)
|
||||
{
|
||||
return unsigned(FINALIZE_STRING) <= thingKind &&
|
||||
thingKind <= unsigned(FINALIZE_EXTERNAL_STRING_LAST);
|
||||
}
|
||||
|
||||
typedef struct JSGCArenaInfo JSGCArenaInfo;
|
||||
typedef struct JSGCArenaList JSGCArenaList;
|
||||
typedef struct JSGCChunkInfo JSGCChunkInfo;
|
||||
@ -297,6 +289,7 @@ struct JSGCArenaList {
|
||||
JSGCArenaInfo *last; /* last allocated GC arena */
|
||||
uint32 lastCount; /* number of allocated things in the last
|
||||
arena */
|
||||
uint32 thingKind; /* one of JSFinalizeGCThingKind */
|
||||
uint32 thingSize; /* size of things to allocate on this list
|
||||
*/
|
||||
JSGCThing *freeList; /* list of free GC things */
|
||||
@ -359,15 +352,8 @@ class JSFreePointerListTask : public JSBackgroundTask {
|
||||
};
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Free the chars held by str when it is finalized by the GC. When type is
|
||||
* less then zero, it denotes an internal string. Otherwise it denotes the
|
||||
* type of the external string allocated with JS_NewExternalString.
|
||||
*
|
||||
* This function always needs rt but can live with null cx.
|
||||
*/
|
||||
extern void
|
||||
js_FinalizeStringRT(JSRuntime *rt, JSString *str, intN type, JSContext *cx);
|
||||
js_FinalizeStringRT(JSRuntime *rt, JSString *str);
|
||||
|
||||
#ifdef DEBUG_notme
|
||||
#define JS_GCMETER 1
|
||||
@ -416,7 +402,7 @@ typedef struct JSGCStats {
|
||||
uint32 closelater; /* number of close hooks scheduled to run */
|
||||
uint32 maxcloselater; /* max number of close hooks scheduled to run */
|
||||
|
||||
JSGCArenaStats arenaStats[GC_NUM_FREELISTS];
|
||||
JSGCArenaStats arenaStats[FINALIZE_LIST_LIMIT];
|
||||
JSGCArenaStats doubleArenaStats;
|
||||
} JSGCStats;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user