bug 437325 - JSThread is not shared between runtimes. r=brendan

This commit is contained in:
Igor Bukanov 2009-03-13 12:10:34 +01:00
Родитель 63cf3003f6
Коммит a10e39d51a
14 изменённых файлов: 455 добавлений и 447 удалений

Просмотреть файл

@ -787,8 +787,6 @@ JS_NewRuntime(uint32 maxbytes)
if (!js_InitDeflatedStringCache(rt))
goto bad;
#ifdef JS_THREADSAFE
if (!js_InitThreadPrivateIndex(js_ThreadDestructorCB))
goto bad;
rt->gcLock = JS_NEW_LOCK();
if (!rt->gcLock)
goto bad;
@ -817,10 +815,8 @@ JS_NewRuntime(uint32 maxbytes)
#endif
if (!js_InitPropertyTree(rt))
goto bad;
#if !defined JS_THREADSAFE && defined JS_TRACER
js_InitJIT(&rt->traceMonitor);
#endif
if (!js_InitThreads(rt))
goto bad;
return rt;
@ -849,10 +845,7 @@ JS_DestroyRuntime(JSRuntime *rt)
}
#endif
#if !defined JS_THREADSAFE && defined JS_TRACER
js_FinishJIT(&rt->traceMonitor);
#endif
js_FinishThreads(rt);
js_FreeRuntimeScriptState(rt);
js_FinishAtomState(rt);
@ -884,8 +877,6 @@ JS_DestroyRuntime(JSRuntime *rt)
JS_DESTROY_CONDVAR(rt->titleSharingDone);
if (rt->debuggerLock)
JS_DESTROY_LOCK(rt->debuggerLock);
#else
GSN_CACHE_CLEAR(&rt->gsnCache);
#endif
js_FinishPropertyTree(rt);
free(rt);
@ -902,7 +893,6 @@ JS_ShutDown(void)
js_FinishDtoa();
#ifdef JS_THREADSAFE
js_CleanupThreadPrivateData(); /* Fixes bug 464828. */
js_CleanupLocks();
#endif
PRMJ_NowShutdown();
@ -926,7 +916,7 @@ JS_BeginRequest(JSContext *cx)
#ifdef JS_THREADSAFE
JSRuntime *rt;
JS_ASSERT(cx->thread->id == js_CurrentThreadId());
JS_ASSERT(CURRENT_THREAD_IS_ME(cx->thread));
if (!cx->requestDepth) {
JS_ASSERT(cx->gcLocalFreeLists == &js_GCEmptyFreeListSet);
@ -934,7 +924,6 @@ JS_BeginRequest(JSContext *cx)
rt = cx->runtime;
JS_LOCK_GC(rt);
/* NB: we use cx->thread here, not js_GetCurrentThread(). */
if (rt->gcThread != cx->thread) {
while (rt->gcLevel > 0)
JS_AWAIT_GC_DONE(rt);
@ -961,6 +950,7 @@ JS_EndRequest(JSContext *cx)
JSBool shared;
CHECK_REQUEST(cx);
JS_ASSERT(CURRENT_THREAD_IS_ME(cx->thread));
JS_ASSERT(cx->requestDepth > 0);
JS_ASSERT(cx->outstandingRequests > 0);
if (cx->requestDepth == 1) {
@ -2850,19 +2840,6 @@ JS_SetPrototype(JSContext *cx, JSObject *obj, JSObject *proto)
{
CHECK_REQUEST(cx);
JS_ASSERT(obj != proto);
#ifdef DEBUG
/*
* FIXME: bug 408416. The cycle-detection required for script-writeable
* __proto__ lives in js_SetProtoOrParent over in jsobj.c, also known as
* js_ObjectOps.setProto. This hook must detect cycles, to prevent scripts
* from ilooping SpiderMonkey trivially. But the overhead of detecting
* cycles is high enough, and the threat from JS-API-calling C++ code is
* low enough, that it's not worth burdening the non-DEBUG callers. Same
* goes for JS_SetParent, below.
*/
if (obj->map->ops->setProto)
return obj->map->ops->setProto(cx, obj, JSSLOT_PROTO, proto);
#else
if (OBJ_IS_NATIVE(obj)) {
JS_LOCK_OBJ(cx, obj);
if (!js_GetMutableScope(cx, obj)) {
@ -2873,7 +2850,6 @@ JS_SetPrototype(JSContext *cx, JSObject *obj, JSObject *proto)
JS_UNLOCK_OBJ(cx, obj);
return JS_TRUE;
}
#endif
OBJ_SET_PROTO(cx, obj, proto);
return JS_TRUE;
}
@ -2894,11 +2870,6 @@ JS_SetParent(JSContext *cx, JSObject *obj, JSObject *parent)
{
CHECK_REQUEST(cx);
JS_ASSERT(obj != parent);
#ifdef DEBUG
/* FIXME: bug 408416, see JS_SetPrototype just above. */
if (obj->map->ops->setParent)
return obj->map->ops->setParent(cx, obj, JSSLOT_PARENT, parent);
#endif
OBJ_SET_PARENT(cx, obj, parent);
return JS_TRUE;
}
@ -5958,25 +5929,17 @@ JS_SetContextThread(JSContext *cx)
#ifdef JS_THREADSAFE
JS_ASSERT(cx->requestDepth == 0);
if (cx->thread) {
JS_ASSERT(cx->thread->id == js_CurrentThreadId());
JS_ASSERT(CURRENT_THREAD_IS_ME(cx->thread));
return cx->thread->id;
}
JSRuntime *rt = cx->runtime;
JSThread *thread = js_GetCurrentThread(rt);
if (!thread) {
if (!js_InitContextThread(cx)) {
js_ReportOutOfMemory(cx);
return -1;
}
/*
* We must not race with a GC that accesses cx->thread for all threads,
* see bug 476934.
*/
JS_LOCK_GC(rt);
js_WaitForGC(rt);
js_InitContextThread(cx, thread);
JS_UNLOCK_GC(rt);
/* Here the GC lock is still held after js_InitContextThread took it. */
JS_UNLOCK_GC(cx->runtime);
#endif
return 0;
}
@ -5993,8 +5956,8 @@ JS_ClearContextThread(JSContext *cx)
JS_ASSERT(cx->requestDepth == 0);
if (!cx->thread)
return 0;
JS_ASSERT(CURRENT_THREAD_IS_ME(cx->thread));
jsword old = cx->thread->id;
JS_ASSERT(old == js_CurrentThreadId());
/*
* We must not race with a GC that accesses cx->thread for all threads,
@ -6003,9 +5966,8 @@ JS_ClearContextThread(JSContext *cx)
JSRuntime *rt = cx->runtime;
JS_LOCK_GC(rt);
js_WaitForGC(rt);
JS_REMOVE_AND_INIT_LINK(&cx->threadLinks);
cx->thread = NULL;
JS_UNLOCK_GC(cx->runtime);
js_ClearContextThread(cx);
JS_UNLOCK_GC(rt);
return old;
#else
return 0;

Просмотреть файл

@ -69,141 +69,231 @@
#include "jstracer.h"
#ifdef JS_THREADSAFE
#include "prtypes.h"
/*
* The index for JSThread info, returned by PR_NewThreadPrivateIndex. The
* index value is visible and shared by all threads, but the data associated
* with it is private to each thread.
*/
static PRUintn threadTPIndex;
static JSBool tpIndexInited = JS_FALSE;
struct JSThreadsHashEntry : JSDHashEntryHdr {
JSThread *thread;
};
JS_BEGIN_EXTERN_C
JSBool
js_InitThreadPrivateIndex(void (*ptr)(void *))
{
PRStatus status;
if (tpIndexInited)
return JS_TRUE;
status = PR_NewThreadPrivateIndex(&threadTPIndex, ptr);
if (status == PR_SUCCESS)
tpIndexInited = JS_TRUE;
return status == PR_SUCCESS;
}
JS_END_EXTERN_C
JS_BEGIN_EXTERN_C
JSBool
js_CleanupThreadPrivateData()
{
if (!tpIndexInited)
return JS_TRUE;
return PR_SetThreadPrivate(threadTPIndex, NULL) == PR_SUCCESS;
}
JS_END_EXTERN_C
/*
* Callback function to delete a JSThread info when the thread that owns it
* is destroyed.
*/
void
js_ThreadDestructorCB(void *ptr)
{
JSThread *thread = (JSThread *)ptr;
if (!thread)
return;
/*
* Check that this thread properly called either JS_DestroyContext or
* JS_ClearContextThread on each JSContext it created or used.
*/
JS_ASSERT(JS_CLIST_IS_EMPTY(&thread->contextList));
GSN_CACHE_CLEAR(&thread->gsnCache);
#if defined JS_TRACER
js_FinishJIT(&thread->traceMonitor);
#endif
free(thread);
}
/*
* Get current thread-local JSThread info, creating one if it doesn't exist.
* Each thread has a unique JSThread pointer.
*
* Since we are dealing with thread-local data, no lock is needed.
*
* Return a pointer to the thread local info, NULL if the system runs out
* of memory, or it failed to set thread private data (neither case is very
* likely; both are probably due to out-of-memory). It is up to the caller
* to report an error, if possible.
*/
JSThread *
js_GetCurrentThread(JSRuntime *rt)
static void
FreeContext(JSContext *cx);
static void
InitThreadData(JSThreadData *data)
{
JSThread *thread;
thread = (JSThread *)PR_GetThreadPrivate(threadTPIndex);
if (!thread) {
thread = (JSThread *) malloc(sizeof(JSThread));
if (!thread)
return NULL;
#ifdef DEBUG
memset(thread, JS_FREE_PATTERN, sizeof(JSThread));
/* The data must be already zeroed. */
for (size_t i = 0; i != sizeof(*data); ++i)
JS_ASSERT(reinterpret_cast<uint8*>(data)[i] == 0);
#endif
if (PR_FAILURE == PR_SetThreadPrivate(threadTPIndex, thread)) {
free(thread);
return NULL;
}
JS_INIT_CLIST(&thread->contextList);
thread->id = js_CurrentThreadId();
thread->gcMallocBytes = 0;
#ifdef JS_TRACER
memset(&thread->traceMonitor, 0, sizeof(thread->traceMonitor));
js_InitJIT(&thread->traceMonitor);
js_InitJIT(&data->traceMonitor);
#endif
memset(thread->scriptsToGC, 0, sizeof thread->scriptsToGC);
}
/*
* js_InitContextThread initializes the remaining fields as necessary.
*/
}
static void
FinishThreadData(JSThreadData *data)
{
#ifdef DEBUG
/* All GC-related things must be already removed at this point. */
for (size_t i = 0; i != JS_ARRAY_LENGTH(data->scriptsToGC); ++i)
JS_ASSERT(!data->scriptsToGC[i]);
#endif
js_FinishGSNCache(&data->gsnCache);
js_FinishPropertyCache(&data->propertyCache);
#if defined JS_TRACER
js_FinishJIT(&data->traceMonitor);
#endif
}
static void
PurgeThreadData(JSContext *cx, JSThreadData *data)
{
# ifdef JS_TRACER
js_PurgeTraceMonitor(cx, &data->traceMonitor);
# endif
/* Destroy eval'ed scripts. */
js_DestroyScriptsToGC(cx, data);
js_PurgeGSNCache(&data->gsnCache);
js_PurgePropertyCache(cx, &data->propertyCache);
}
#ifdef JS_THREADSAFE
static JSThread *
NewThread(jsword id)
{
JS_ASSERT(js_CurrentThreadId() == id);
JSThread *thread = (JSThread *) calloc(1, sizeof(JSThread));
if (!thread)
return NULL;
JS_INIT_CLIST(&thread->contextList);
thread->id = id;
InitThreadData(&thread->data);
return thread;
}
/*
* Sets current thread as owning thread of a context by assigning the
* thread-private info to the context.
*/
void
js_InitContextThread(JSContext *cx, JSThread *thread)
static void
DestroyThread(JSThread *thread)
{
JS_ASSERT(CURRENT_THREAD_IS_ME(thread));
/* The thread must have zero contexts. */
JS_ASSERT(JS_CLIST_IS_EMPTY(&thread->contextList));
FinishThreadData(&thread->data);
free(thread);
}
JSBool
js_InitContextThread(JSContext *cx)
{
JSRuntime *rt;
jsword id = js_CurrentThreadId();
const void *key = (const void *) id;
JSThreadsHashEntry *entry;
JSThread *thread;
JS_ASSERT(!cx->thread);
JS_ASSERT(cx->requestDepth == 0);
rt = cx->runtime;
JS_LOCK_GC(rt);
/*
* Clear caches on each transition from 0 to 1 context active on the
* current thread. See bug 425828.
* We must not race with a GC that accesses cx->thread for JSContext
* instances on all threads, see bug 476934.
*/
if (JS_CLIST_IS_EMPTY(&thread->contextList)) {
memset(&thread->gsnCache, 0, sizeof thread->gsnCache);
memset(&thread->propertyCache, 0, sizeof thread->propertyCache);
#ifdef DEBUG
memset(&thread->evalCacheMeter, 0, sizeof thread->evalCacheMeter);
#endif
js_WaitForGC(rt);
entry = (JSThreadsHashEntry *)
JS_DHashTableOperate(&rt->threads, key, JS_DHASH_LOOKUP);
if (JS_DHASH_ENTRY_IS_BUSY(entry)) {
thread = entry->thread;
JS_ASSERT(thread->id == id);
} else {
JS_UNLOCK_GC(rt);
thread = NewThread(id);
if (!thread)
return false;
JS_LOCK_GC(rt);
js_WaitForGC(rt);
entry = (JSThreadsHashEntry *)
JS_DHashTableOperate(&rt->threads, key, JS_DHASH_ADD);
if (!entry) {
JS_UNLOCK_GC(rt);
DestroyThread(thread);
return false;
}
/* Another thread cannot initialize entry->thread. */
JS_ASSERT(!entry->thread);
entry->thread = thread;
}
JS_APPEND_LINK(&cx->threadLinks, &thread->contextList);
cx->thread = thread;
return true;
}
void
js_ClearContextThread(JSContext *cx)
{
JS_ASSERT(CURRENT_THREAD_IS_ME(cx->thread));
JS_REMOVE_AND_INIT_LINK(&cx->threadLinks);
cx->thread = NULL;
}
static JSBool
thread_matchEntry(JSDHashTable *table,
const JSDHashEntryHdr *hdr,
const void *key)
{
const JSThreadsHashEntry *entry = (const JSThreadsHashEntry *) hdr;
return entry->thread->id == (jsword) key;
}
static const JSDHashTableOps threads_ops = {
JS_DHashAllocTable,
JS_DHashFreeTable,
JS_DHashVoidPtrKeyStub,
thread_matchEntry,
JS_DHashMoveEntryStub,
JS_DHashClearEntryStub,
JS_DHashFinalizeStub,
NULL
};
static JSDHashOperator
thread_destroyer(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 /* index */,
void * /* arg */)
{
JSThreadsHashEntry *entry = (JSThreadsHashEntry *) hdr;
JSThread *thread = entry->thread;
JS_ASSERT(JS_CLIST_IS_EMPTY(&thread->contextList));
DestroyThread(thread);
return JS_DHASH_REMOVE;
}
static JSDHashOperator
thread_purger(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 /* index */,
void *arg)
{
JSContext* cx = (JSContext *) arg;
JSThread *thread = ((JSThreadsHashEntry *) hdr)->thread;
if (JS_CLIST_IS_EMPTY(&thread->contextList)) {
JS_ASSERT(cx->thread != thread);
js_DestroyScriptsToGC(cx, &thread->data);
DestroyThread(thread);
return JS_DHASH_REMOVE;
}
PurgeThreadData(cx, &thread->data);
return JS_DHASH_NEXT;
}
#endif /* JS_THREADSAFE */
JSBool
js_InitThreads(JSRuntime *rt)
{
#ifdef JS_THREADSAFE
if (!JS_DHashTableInit(&rt->threads, &threads_ops, NULL,
sizeof(JSThreadsHashEntry), 4)) {
rt->threads.ops = NULL;
return false;
}
#else
InitThreadData(&rt->threadData);
#endif
return true;
}
void
js_FinishThreads(JSRuntime *rt)
{
#ifdef JS_THREADSAFE
if (!rt->threads.ops)
return;
JS_DHashTableEnumerate(&rt->threads, thread_destroyer, NULL);
JS_DHashTableFinish(&rt->threads);
rt->threads.ops = NULL;
#else
FinishThreadData(&rt->threadData);
#endif
}
void
js_PurgeThreads(JSContext *cx)
{
#ifdef JS_THREADSAFE
JS_DHashTableEnumerate(&cx->runtime->threads, thread_purger, cx);
#else
PurgeThreadData(cx, &cx->runtime->threadData);
#endif
}
/*
* JSOPTION_XML and JSOPTION_ANONFUNFIX must be part of the JS version
* associated with scripts, so in addition to storing them in cx->options we
@ -260,12 +350,6 @@ js_NewContext(JSRuntime *rt, size_t stackChunkSize)
JSContext *cx;
JSBool ok, first;
JSContextCallback cxCallback;
#ifdef JS_THREADSAFE
JSThread *thread = js_GetCurrentThread(rt);
if (!thread)
return NULL;
#endif
/*
* We need to initialize the new context fully before adding it to the
@ -284,7 +368,6 @@ js_NewContext(JSRuntime *rt, size_t stackChunkSize)
cx->scriptStackQuota = JS_DEFAULT_SCRIPT_STACK_QUOTA;
#ifdef JS_THREADSAFE
cx->gcLocalFreeLists = (JSGCFreeListSet *) &js_GCEmptyFreeListSet;
js_InitContextThread(cx, thread);
#endif
JS_STATIC_ASSERT(JSVERSION_DEFAULT == 0);
JS_ASSERT(cx->version == JSVERSION_DEFAULT);
@ -299,12 +382,18 @@ js_NewContext(JSRuntime *rt, size_t stackChunkSize)
js_InitRegExpStatics(cx);
JS_ASSERT(cx->resolveFlags == 0);
JS_LOCK_GC(rt);
#ifdef JS_THREADSAFE
if (!js_InitContextThread(cx)) {
FreeContext(cx);
return NULL;
}
#endif
/*
* Here the GC lock is still held after js_InitContextThread took it and
* the GC is not running on another thread.
*/
for (;;) {
/*
* Ensure that we don't race with the GC on other threads, bug 478336.
*/
js_WaitForGC(rt);
if (rt->state == JSRTS_UP) {
JS_ASSERT(!JS_CLIST_IS_EMPTY(&rt->contextList));
first = JS_FALSE;
@ -317,6 +406,15 @@ js_NewContext(JSRuntime *rt, size_t stackChunkSize)
break;
}
JS_WAIT_CONDVAR(rt->stateChange, JS_NO_TIMEOUT);
/*
* During the above wait after we are notified about the state change
* but before we wake up, another thread could enter the GC from
* js_DestroyContext, bug 478336. So we must wait here to ensure that
* when we exit the loop with the first flag set to true, that GC is
* finished.
*/
js_WaitForGC(rt);
}
JS_APPEND_LINK(&cx->link, &rt->contextList);
JS_UNLOCK_GC(rt);
@ -403,7 +501,7 @@ DumpEvalCacheMeter(JSContext *cx)
EVAL_CACHE_METER_LIST(frob)
#undef frob
};
JSEvalCacheMeter *ecm = &JS_CACHE_LOCUS(cx)->evalCacheMeter;
JSEvalCacheMeter *ecm = &JS_THREAD_DATA(cx)->evalCacheMeter;
static AutoFile fp;
if (!fp) {
@ -438,9 +536,6 @@ js_DestroyContext(JSContext *cx, JSDestroyContextMode mode)
JSRuntime *rt;
JSContextCallback cxCallback;
JSBool last;
JSArgumentFormatMap *map;
JSLocalRootStack *lrs;
JSLocalRootChunk *lrc;
#ifdef JS_THREADSAFE
JS_ASSERT(CURRENT_THREAD_IS_ME(cx->thread));
@ -477,73 +572,99 @@ js_DestroyContext(JSContext *cx, JSDestroyContextMode mode)
last = (rt->contextList.next == &rt->contextList);
if (last)
rt->state = JSRTS_LANDING;
JS_UNLOCK_GC(rt);
if (last) {
if (last || mode == JSDCM_FORCE_GC || mode == mode == JSDCM_MAYBE_GC
#ifdef JS_THREADSAFE
/*
* If cx is not in a request already, begin one now so that we wait
* for any racing GC started on a not-last context to finish, before
* we plow ahead and unpin atoms. Note that even though we begin a
* request here if necessary, we end all requests on cx below before
* forcing a final GC. This lets any not-last context destruction
* racing in another thread try to force or maybe run the GC, but by
* that point, rt->state will not be JSRTS_UP, and that GC attempt
* will return early.
*/
if (cx->requestDepth == 0)
JS_BeginRequest(cx);
|| cx->requestDepth != 0
#endif
/* Unlock and clear GC things held by runtime pointers. */
js_FinishRuntimeNumberState(cx);
js_FinishRuntimeStringState(cx);
/* Unpin all common atoms before final GC. */
js_FinishCommonAtoms(cx);
/* Clear debugging state to remove GC roots. */
JS_ClearAllTraps(cx);
JS_ClearAllWatchPoints(cx);
}
/* Remove more GC roots in regExpStatics, then collect garbage. */
JS_ClearRegExpRoots(cx);
#ifdef JS_THREADSAFE
/*
* Destroying a context implicitly calls JS_EndRequest(). Also, we must
* end our request here in case we are "last" -- in that event, another
* js_DestroyContext that was not last might be waiting in the GC for our
* request to end. We'll let it run below, just before we do the truly
* final GC and then free atom state.
*/
while (cx->requestDepth != 0)
JS_EndRequest(cx);
#endif
if (last) {
js_GC(cx, GC_LAST_CONTEXT);
DUMP_EVAL_CACHE_METER(cx);
/*
* Free the script filename table if it exists and is empty. Do this
* after the last GC to avoid finalizers tripping on free memory.
*/
if (rt->scriptFilenameTable && rt->scriptFilenameTable->nentries == 0)
js_FinishRuntimeScriptState(rt);
/* Take the runtime down, now that it has no contexts or atoms. */
JS_LOCK_GC(rt);
rt->state = JSRTS_DOWN;
JS_NOTIFY_ALL_CONDVAR(rt->stateChange);
) {
JS_UNLOCK_GC(rt);
} else {
if (mode == JSDCM_FORCE_GC)
js_GC(cx, GC_NORMAL);
else if (mode == JSDCM_MAYBE_GC)
JS_MaybeGC(cx);
if (last) {
#ifdef JS_THREADSAFE
/*
* If cx is not in a request already, begin one now so that we wait
* for any racing GC started on a not-last context to finish, before
* we plow ahead and unpin atoms. Note that even though we begin a
* request here if necessary, we end all requests on cx below before
* forcing a final GC. This lets any not-last context destruction
* racing in another thread try to force or maybe run the GC, but by
* that point, rt->state will not be JSRTS_UP, and that GC attempt
* will return early.
*/
if (cx->requestDepth == 0)
JS_BeginRequest(cx);
#endif
/* Unlock and clear GC things held by runtime pointers. */
js_FinishRuntimeNumberState(cx);
js_FinishRuntimeStringState(cx);
/* Unpin all common atoms before final GC. */
js_FinishCommonAtoms(cx);
/* Clear debugging state to remove GC roots. */
JS_ClearAllTraps(cx);
JS_ClearAllWatchPoints(cx);
}
/* Remove more GC roots in regExpStatics, then collect garbage. */
JS_ClearRegExpRoots(cx);
#ifdef JS_THREADSAFE
/*
* Destroying a context implicitly calls JS_EndRequest(). Also, we must
* end our request here in case we are "last" -- in that event, another
* js_DestroyContext that was not last might be waiting in the GC for our
* request to end. We'll let it run below, just before we do the truly
* final GC and then free atom state.
*/
while (cx->requestDepth != 0)
JS_EndRequest(cx);
#endif
if (last) {
js_GC(cx, GC_LAST_CONTEXT);
DUMP_EVAL_CACHE_METER(cx);
/*
* Free the script filename table if it exists and is empty. Do this
* after the last GC to avoid finalizers tripping on free memory.
*/
if (rt->scriptFilenameTable &&
rt->scriptFilenameTable->nentries == 0) {
js_FinishRuntimeScriptState(rt);
}
/* Take the runtime down, now that it has no contexts or atoms. */
JS_LOCK_GC(rt);
rt->state = JSRTS_DOWN;
JS_NOTIFY_ALL_CONDVAR(rt->stateChange);
} else {
if (mode == JSDCM_FORCE_GC)
js_GC(cx, GC_NORMAL);
else if (mode == JSDCM_MAYBE_GC)
JS_MaybeGC(cx);
JS_LOCK_GC(rt);
js_WaitForGC(rt);
}
}
#ifdef JS_THREADSAFE
js_ClearContextThread(cx);
#endif
JS_UNLOCK_GC(rt);
FreeContext(cx);
}
static void
FreeContext(JSContext *cx)
{
JSArgumentFormatMap *map;
JSLocalRootStack *lrs;
JSLocalRootChunk *lrc;
#ifdef JS_THREADSAFE
JS_ASSERT(!cx->thread);
#endif
/* Free the stuff hanging off of cx. */
js_FreeRegExpStatics(cx);
@ -577,16 +698,6 @@ js_DestroyContext(JSContext *cx, JSDestroyContextMode mode)
JS_free(cx, lrs);
}
#ifdef JS_THREADSAFE
/*
* Since cx is not on rt->contextList, it cannot be accessed by the GC
* running on another thread. Thus, compared with JS_ClearContextThread,
* we can safely unlink cx from from JSThread.contextList without taking
* the GC lock.
*/
JS_REMOVE_LINK(&cx->threadLinks);
#endif
/* Finally, free cx itself. */
free(cx);
}

Просмотреть файл

@ -72,25 +72,20 @@ typedef struct JSGSNCache {
uint32 hits;
uint32 misses;
uint32 fills;
uint32 clears;
uint32 purges;
# define GSN_CACHE_METER(cache,cnt) (++(cache)->cnt)
#else
# define GSN_CACHE_METER(cache,cnt) /* nothing */
#endif
} JSGSNCache;
#define GSN_CACHE_CLEAR(cache) \
JS_BEGIN_MACRO \
(cache)->code = NULL; \
if ((cache)->table.ops) { \
JS_DHashTableFinish(&(cache)->table); \
(cache)->table.ops = NULL; \
} \
GSN_CACHE_METER(cache, clears); \
JS_END_MACRO
#define js_FinishGSNCache(cache) js_PurgeGSNCache(cache)
extern void
js_PurgeGSNCache(JSGSNCache *cache);
/* These helper macros take a cx as parameter and operate on its GSN cache. */
#define JS_CLEAR_GSN_CACHE(cx) GSN_CACHE_CLEAR(&JS_GSN_CACHE(cx))
#define JS_PURGE_GSN_CACHE(cx) js_PurgeGSNCache(&JS_GSN_CACHE(cx))
#define JS_METER_GSN_CACHE(cx,cnt) GSN_CACHE_METER(&JS_GSN_CACHE(cx), cnt)
typedef struct InterpState InterpState;
@ -125,7 +120,7 @@ struct GlobalState {
* JS_THREADSAFE) has an associated trace monitor that keeps track of loop
* frequencies for all JavaScript code loaded into that runtime.
*/
typedef struct JSTraceMonitor {
struct JSTraceMonitor {
/*
* Flag set when running (or recording) JIT-compiled code. This prevents
* both interpreter activation and last-ditch garbage collection when up
@ -176,7 +171,7 @@ typedef struct JSTraceMonitor {
/* Keep a list of recorders we need to abort on cache flush. */
CLS(TraceRecorder) abortStack;
} JSTraceMonitor;
};
typedef struct InterpStruct InterpStruct;
@ -206,11 +201,31 @@ typedef struct JSEvalCacheMeter {
} JSEvalCacheMeter;
# undef ID
# define DECLARE_EVAL_CACHE_METER JSEvalCacheMeter evalCacheMeter;
#else
# define DECLARE_EVAL_CACHE_METER /* nothing */
#endif
struct JSThreadData {
/*
* The GSN cache is per thread since even multi-cx-per-thread embeddings
* do not interleave js_GetSrcNote calls.
*/
JSGSNCache gsnCache;
/* Property cache for faster call/get/set invocation. */
JSPropertyCache propertyCache;
#ifdef JS_TRACER
/* Trace-tree JIT recorder/interpreter state. */
JSTraceMonitor traceMonitor;
#endif
/* Lock-free hashed lists of scripts created by eval to garbage-collect. */
JSScript *scriptsToGC[JS_EVAL_CACHE_SIZE];
#ifdef JS_EVAL_CACHE_METERING
JSEvalCacheMeter evalCacheMeter;
#endif
};
#ifdef JS_THREADSAFE
/*
@ -230,39 +245,24 @@ struct JSThread {
*/
uint32 gcMallocBytes;
/*
* Store the GSN cache in struct JSThread, not struct JSContext, both to
* save space and to simplify cleanup in js_GC. Any embedding (Firefox
* or another Gecko application) that uses many contexts per thread is
* unlikely to interleave js_GetSrcNote-intensive loops in the decompiler
* among two or more contexts running script in one thread.
*/
JSGSNCache gsnCache;
/* Property cache for faster call/get/set invocation. */
JSPropertyCache propertyCache;
#ifdef JS_TRACER
/* Trace-tree JIT recorder/interpreter state. */
JSTraceMonitor traceMonitor;
#endif
/* Lock-free hashed lists of scripts created by eval to garbage-collect. */
JSScript *scriptsToGC[JS_EVAL_CACHE_SIZE];
DECLARE_EVAL_CACHE_METER
JSThreadData data;
};
#define JS_CACHE_LOCUS(cx) ((cx)->thread)
#define JS_THREAD_DATA(cx) (&(cx)->thread->data)
/*
* The function takes the GC lock and does not release in successful return.
* On error (out of memory) the function releases the lock but delegates
* the error reporting to the caller.
*/
extern JSBool
js_InitContextThread(JSContext *cx);
/*
* On entrance the GC lock must be held and it will be held on exit.
*/
extern void
js_ThreadDestructorCB(void *ptr);
extern void
js_InitContextThread(JSContext *cx, JSThread *thread);
extern JSThread *
js_GetCurrentThread(JSRuntime *rt);
js_ClearContextThread(JSContext *cx);
#endif /* JS_THREADSAFE */
@ -474,6 +474,8 @@ struct JSRuntime {
* case too.
*/
PRLock *debuggerLock;
JSDHashTable threads;
#endif /* JS_THREADSAFE */
uint32 debuggerMutations;
@ -523,26 +525,9 @@ struct JSRuntime {
JSNativeEnumerator *nativeEnumerators;
#ifndef JS_THREADSAFE
/*
* For thread-unsafe embeddings, the GSN cache lives in the runtime and
* not each context, since we expect it to be filled once when decompiling
* a longer script, then hit repeatedly as js_GetSrcNote is called during
* the decompiler activation that filled it.
*/
JSGSNCache gsnCache;
JSThreadData threadData;
/* Property cache for faster call/get/set invocation. */
JSPropertyCache propertyCache;
/* Trace-tree JIT recorder/interpreter state. */
JSTraceMonitor traceMonitor;
/* Lock-free hashed lists of scripts created by eval to garbage-collect. */
JSScript *scriptsToGC[JS_EVAL_CACHE_SIZE];
DECLARE_EVAL_CACHE_METER
#define JS_CACHE_LOCUS(cx) ((cx)->runtime)
#define JS_THREAD_DATA(cx) (&(cx)->runtime->threadData)
#endif
/*
@ -652,13 +637,13 @@ struct JSRuntime {
};
/* Common macros to access thread-local caches in JSThread or JSRuntime. */
#define JS_GSN_CACHE(cx) (JS_CACHE_LOCUS(cx)->gsnCache)
#define JS_PROPERTY_CACHE(cx) (JS_CACHE_LOCUS(cx)->propertyCache)
#define JS_TRACE_MONITOR(cx) (JS_CACHE_LOCUS(cx)->traceMonitor)
#define JS_SCRIPTS_TO_GC(cx) (JS_CACHE_LOCUS(cx)->scriptsToGC)
#define JS_GSN_CACHE(cx) (JS_THREAD_DATA(cx)->gsnCache)
#define JS_PROPERTY_CACHE(cx) (JS_THREAD_DATA(cx)->propertyCache)
#define JS_TRACE_MONITOR(cx) (JS_THREAD_DATA(cx)->traceMonitor)
#define JS_SCRIPTS_TO_GC(cx) (JS_THREAD_DATA(cx)->scriptsToGC)
#ifdef JS_EVAL_CACHE_METERING
# define EVAL_CACHE_METER(x) (JS_CACHE_LOCUS(cx)->evalCacheMeter.x++)
# define EVAL_CACHE_METER(x) (JS_THREAD_DATA(cx)->evalCacheMeter.x++)
#else
# define EVAL_CACHE_METER(x) ((void) 0)
#endif
@ -1138,22 +1123,14 @@ class JSAutoResolveFlags
#define JS_HAS_XML_OPTION(cx) ((cx)->version & JSVERSION_HAS_XML || \
JSVERSION_NUMBER(cx) >= JSVERSION_1_6)
/*
* Initialize a library-wide thread private data index, and remember that it
* has already been done, so that it happens only once ever. Returns true on
* success.
*/
extern JSBool
js_InitThreadPrivateIndex(void (*ptr)(void *));
js_InitThreads(JSRuntime *rt);
/*
* Clean up thread-private data on the current thread. NSPR automatically
* cleans up thread-private data for every thread except the main thread
* (see bug 383977) on shutdown. Thus, this function should be called for
* exactly those threads that survive JS_ShutDown, including the main thread.
*/
extern JSBool
js_CleanupThreadPrivateData();
extern void
js_FinishThreads(JSRuntime *rt);
extern void
js_PurgeThreads(JSContext *cx);
/*
* Ensures the JSOPTION_XML and JSOPTION_ANONFUNFIX bits of cx->options are

Просмотреть файл

@ -123,7 +123,7 @@ js_UntrapScriptCode(JSContext *cx, JSScript *script)
if (!code)
break;
memcpy(code, script->code, nbytes);
JS_CLEAR_GSN_CACHE(cx);
JS_PURGE_GSN_CACHE(cx);
}
code[trap->pc - script->code] = trap->op;
}

Просмотреть файл

@ -2712,4 +2712,8 @@ js_FreezeLocalNames(JSContext *cx, JSFunction *fun)
if (array)
fun->u.i.names.array = array;
}
#ifdef DEBUG
if (n > MAX_ARRAY_LOCALS)
JS_DHashMarkTableImmutable(&fun->u.i.names.map->names);
#endif
}

Просмотреть файл

@ -3109,23 +3109,23 @@ js_TraceContext(JSTracer *trc, JSContext *acx)
js_TraceRegExpStatics(trc, acx);
}
#ifdef JS_TRACER
void
js_TraceTraceMonitor(JSTracer *trc, JSTraceMonitor *tm)
js_PurgeTraceMonitor(JSContext *cx, JSTraceMonitor *tm)
{
if (IS_GC_MARKING_TRACER(trc)) {
tm->reservedDoublePoolPtr = tm->reservedDoublePool;
tm->reservedDoublePoolPtr = tm->reservedDoublePool;
tm->needFlush = JS_TRUE;
tm->needFlush = JS_TRUE;
/* Keep the reserved objects. */
for (JSObject *obj = tm->reservedObjects; obj; obj = JSVAL_TO_OBJECT(obj->fslots[0])) {
uint8 *flagp = GetGCThingFlags(obj);
JS_ASSERT((*flagp & GCF_TYPEMASK) == GCX_OBJECT);
JS_ASSERT(*flagp != GCF_FINAL);
*flagp |= GCF_MARK;
}
/* Keep the reserved objects. */
for (JSObject *obj = tm->reservedObjects; obj; obj = JSVAL_TO_OBJECT(obj->fslots[0])) {
uint8 *flagp = GetGCThingFlags(obj);
JS_ASSERT((*flagp & GCF_TYPEMASK) == GCX_OBJECT);
JS_ASSERT(*flagp != GCF_FINAL);
*flagp |= GCF_MARK;
}
}
#endif
JS_REQUIRES_STACK void
js_TraceRuntime(JSTracer *trc, JSBool allAtoms)
@ -3152,17 +3152,6 @@ js_TraceRuntime(JSTracer *trc, JSBool allAtoms)
if (rt->builtinFunctions[i])
JS_CALL_OBJECT_TRACER(trc, rt->builtinFunctions[i], "builtin function");
}
#ifdef JS_THREADSAFE
/* Trace the loop table(s) which can contain pointers to code objects. */
while ((acx = js_ContextIterator(rt, JS_FALSE, &iter)) != NULL) {
if (!acx->thread)
continue;
js_TraceTraceMonitor(trc, &acx->thread->traceMonitor);
}
#else
js_TraceTraceMonitor(trc, &rt->traceMonitor);
#endif
#endif
}
@ -3241,15 +3230,18 @@ ProcessSetSlotRequest(JSContext *cx, JSSetSlotRequest *ssr)
STOBJ_SET_DELEGATE(pobj);
}
static void
DestroyScriptsToGC(JSContext *cx, JSScript **listp)
void
js_DestroyScriptsToGC(JSContext *cx, JSThreadData *data)
{
JSScript *script;
JSScript **listp, *script;
while ((script = *listp) != NULL) {
*listp = script->u.nextToGC;
script->u.nextToGC = NULL;
js_DestroyScript(cx, script);
for (size_t i = 0; i != JS_ARRAY_LENGTH(data->scriptsToGC); ++i) {
listp = &data->scriptsToGC[i];
while ((script = *listp) != NULL) {
*listp = script->u.nextToGC;
script->u.nextToGC = NULL;
js_DestroyScript(cx, script);
}
}
}
@ -3281,7 +3273,14 @@ js_GC(JSContext *cx, JSGCInvocationKind gckind)
JS_ASSERT_IF(gckind == GC_LAST_DITCH, !JS_ON_TRACE(cx));
rt = cx->runtime;
#ifdef JS_THREADSAFE
/*
* We allow js_GC calls outside a request but the context must be bound
* to the current thread.
*/
JS_ASSERT(CURRENT_THREAD_IS_ME(cx->thread));
/* Avoid deadlock. */
JS_ASSERT(!JS_IS_RUNTIME_LOCKED(rt));
#endif
@ -3357,11 +3356,10 @@ js_GC(JSContext *cx, JSGCInvocationKind gckind)
/*
* If we're in one or more requests (possibly on more than one context)
* running on the current thread, indicate, temporarily, that all these
* requests are inactive. If cx->thread is NULL, then cx is not using
* the request model, and does not contribute to rt->requestCount.
* requests are inactive.
*/
requestDebit = 0;
if (cx->thread) {
{
JSCList *head, *link;
/*
@ -3375,17 +3373,6 @@ js_GC(JSContext *cx, JSGCInvocationKind gckind)
if (acx->requestDepth)
requestDebit++;
}
} else {
/*
* We assert, but check anyway, in case someone is misusing the API.
* Avoiding the loop over all of rt's contexts is a win in the event
* that the GC runs only on request-less contexts with null threads,
* in a special thread such as might be used by the UI/DOM/Layout
* "mozilla" or "main" thread in Mozilla-the-browser.
*/
JS_ASSERT(cx->requestDepth == 0);
if (cx->requestDepth)
requestDebit = 1;
}
if (requestDebit) {
JS_ASSERT(requestDebit <= rt->requestCount);
@ -3495,43 +3482,10 @@ js_GC(JSContext *cx, JSGCInvocationKind gckind)
}
#endif
/* Clear property and JIT oracle caches (only for cx->thread if JS_THREADSAFE). */
js_FlushPropertyCache(cx);
#ifdef JS_TRACER
js_FlushJITOracle(cx);
#endif
/* Destroy eval'ed scripts. */
for (i = 0; i < JS_ARRAY_LENGTH(JS_SCRIPTS_TO_GC(cx)); i++)
DestroyScriptsToGC(cx, &JS_SCRIPTS_TO_GC(cx)[i]);
#ifdef JS_THREADSAFE
/*
* Clear thread-based caches. To avoid redundant clearing we unroll the
* current thread's step.
*
* In case a JSScript wrapped within an object was finalized, we null
* acx->thread->gsnCache.script and finish the cache's hashtable. Note
* that js_DestroyScript, called from script_finalize, will have already
* cleared cx->thread->gsnCache above during finalization, so we don't
* have to here.
*/
iter = NULL;
while ((acx = js_ContextIterator(rt, JS_FALSE, &iter)) != NULL) {
if (!acx->thread || acx->thread == cx->thread)
continue;
GSN_CACHE_CLEAR(&acx->thread->gsnCache);
js_FlushPropertyCache(acx);
#ifdef JS_TRACER
js_FlushJITOracle(acx);
#endif
for (i = 0; i < JS_ARRAY_LENGTH(acx->thread->scriptsToGC); i++)
DestroyScriptsToGC(cx, &acx->thread->scriptsToGC[i]);
}
#else
/* The thread-unsafe case just has to clear the runtime's GSN cache. */
GSN_CACHE_CLEAR(&rt->gsnCache);
js_PurgeJITOracle();
#endif
js_PurgeThreads(cx);
restart:
rt->gcNumber++;

Просмотреть файл

@ -247,6 +247,9 @@ js_TraceRuntime(JSTracer *trc, JSBool allAtoms);
extern JS_REQUIRES_STACK JS_FRIEND_API(void)
js_TraceContext(JSTracer *trc, JSContext *acx);
extern void
js_PurgeTraceMonitor(JSContext *cx, JSTraceMonitor *tm);
/*
* Kinds of js_GC invocation.
*/
@ -340,6 +343,9 @@ extern const JSGCFreeListSet js_GCEmptyFreeListSet;
extern void
js_RevokeGCLocalFreeLists(JSContext *cx);
extern void
js_DestroyScriptsToGC(JSContext *cx, JSThreadData *data);
struct JSWeakRoots {
/* Most recently created things by type, members of the GC's root set. */
void *newborn[GCX_NTYPES];

Просмотреть файл

@ -425,11 +425,8 @@ js_FullTestPropertyCache(JSContext *cx, jsbytecode *pc,
JS_STATIC_ASSERT(PCVAL_NULL == 0);
void
js_FlushPropertyCache(JSContext *cx)
js_PurgePropertyCache(JSContext *cx, JSPropertyCache *cache)
{
JSPropertyCache *cache;
cache = &JS_PROPERTY_CACHE(cx);
if (cache->empty) {
ASSERT_CACHE_IS_EMPTY(cache);
return;
@ -497,7 +494,7 @@ js_FlushPropertyCache(JSContext *cx)
}
void
js_FlushPropertyCacheForScript(JSContext *cx, JSScript *script)
js_PurgePropertyCacheForScript(JSContext *cx, JSScript *script)
{
JSPropertyCache *cache;
JSPropCacheEntry *entry;

Просмотреть файл

@ -353,11 +353,14 @@ js_FullTestPropertyCache(JSContext *cx, jsbytecode *pc,
JSObject **objp, JSObject **pobjp,
JSPropCacheEntry **entryp);
extern void
js_FlushPropertyCache(JSContext *cx);
/* The property cache does not need a destructor. */
#define js_FinishPropertyCache(cache) ((void) 0)
extern void
js_FlushPropertyCacheForScript(JSContext *cx, JSScript *script);
js_PurgePropertyCache(JSContext *cx, JSPropertyCache *cache);
extern void
js_PurgePropertyCacheForScript(JSContext *cx, JSScript *script);
extern void
js_DisablePropertyCache(JSContext *cx);

Просмотреть файл

@ -101,10 +101,12 @@ typedef struct JSPropCacheEntry JSPropCacheEntry;
typedef struct JSSharpObjectMap JSSharpObjectMap;
typedef struct JSTempValueRooter JSTempValueRooter;
typedef struct JSThread JSThread;
typedef struct JSThreadData JSThreadData;
typedef struct JSToken JSToken;
typedef struct JSTokenPos JSTokenPos;
typedef struct JSTokenPtr JSTokenPtr;
typedef struct JSTokenStream JSTokenStream;
typedef struct JSTraceMonitor JSTraceMonitor;
typedef struct JSTreeContext JSTreeContext;
typedef struct JSTryNote JSTryNote;
typedef struct JSWeakRoots JSWeakRoots;

Просмотреть файл

@ -1578,13 +1578,13 @@ js_DestroyScript(JSContext *cx, JSScript *script)
JSPRINCIPALS_DROP(cx, script->principals);
if (JS_GSN_CACHE(cx).code == script->code)
JS_CLEAR_GSN_CACHE(cx);
JS_PURGE_GSN_CACHE(cx);
/*
* The GC flushes all property caches, so no need to purge just the
* entries for this script.
*
* JS_THREADSAFE note: js_FlushPropertyCacheForScript flushes only the
* 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
* GC, must be used by only one thread over its lifetime.
@ -1605,9 +1605,9 @@ js_DestroyScript(JSContext *cx, JSScript *script)
#ifdef CHECK_SCRIPT_OWNER
JS_ASSERT(script->owner == cx->thread);
#endif
js_FlushPropertyCacheForScript(cx, script);
js_PurgePropertyCacheForScript(cx, script);
#ifdef JS_TRACER
js_FlushScriptFragments(cx, script);
js_PurgeScriptFragments(cx, script);
#endif
}
}
@ -1676,6 +1676,17 @@ typedef struct GSNCacheEntry {
#define GSN_CACHE_THRESHOLD 100
void
js_PurgeGSNCache(JSGSNCache *cache)
{
cache->code = NULL;
if (cache->table.ops) {
JS_DHashTableFinish(&cache->table);
cache->table.ops = NULL;
}
GSN_CACHE_METER(cache, purges);
}
jssrcnote *
js_GetSrcNoteCached(JSContext *cx, JSScript *script, jsbytecode *pc)
{
@ -1713,7 +1724,7 @@ js_GetSrcNoteCached(JSContext *cx, JSScript *script, jsbytecode *pc)
if (JS_GSN_CACHE(cx).code != script->code &&
script->length >= GSN_CACHE_THRESHOLD) {
JS_CLEAR_GSN_CACHE(cx);
JS_PURGE_GSN_CACHE(cx);
nsrcnotes = 0;
for (sn = SCRIPT_NOTES(script); !SN_IS_TERMINATOR(sn);
sn = SN_NEXT(sn)) {

Просмотреть файл

@ -4629,19 +4629,17 @@ TraceRecorder::popAbortStack()
}
void
js_FlushJITOracle(JSContext* cx)
js_PurgeJITOracle()
{
if (!TRACING_ENABLED(cx))
return;
oracle.clear();
}
JS_REQUIRES_STACK void
js_FlushScriptFragments(JSContext* cx, JSScript* script)
js_PurgeScriptFragments(JSContext* cx, JSScript* script)
{
if (!TRACING_ENABLED(cx))
return;
debug_only_v(printf("Flushing fragments for JSScript %p.\n", (void*)script);)
debug_only_v(printf("Purging fragments for JSScript %p.\n", (void*)script);)
JSTraceMonitor* tm = &JS_TRACE_MONITOR(cx);
for (size_t i = 0; i < FRAGMENT_TABLE_SIZE; ++i) {
for (VMFragment **f = &(tm->vmfragments[i]); *f; ) {

Просмотреть файл

@ -639,13 +639,13 @@ extern void
js_FinishJIT(JSTraceMonitor *tm);
extern void
js_FlushScriptFragments(JSContext* cx, JSScript* script);
js_PurgeScriptFragments(JSContext* cx, JSScript* script);
extern void
js_FlushJITCache(JSContext* cx);
extern void
js_FlushJITOracle(JSContext* cx);
js_PurgeJITOracle();
extern JSObject *
js_GetBuiltinFunction(JSContext *cx, uintN index);

Просмотреть файл

@ -1090,23 +1090,6 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect)
// these jsids filled in later when we have a JSContext to work with.
mStrIDs[0] = 0;
// Call XPCPerThreadData::GetData to initialize
// XPCPerThreadData::gTLSIndex before initializing
// JSRuntime::threadTPIndex in JS_NewRuntime.
//
// XPConnect uses a thread local storage (XPCPerThreadData) indexed by
// XPCPerThreadData::gTLSIndex, and SpiderMonkey GC uses a thread local
// storage indexed by JSRuntime::threadTPIndex.
//
// The destructor for XPCPerThreadData::gTLSIndex may access
// thread local storage indexed by JSRuntime::threadTPIndex.
// Thus, the destructor for JSRuntime::threadTPIndex must be called
// later than the one for XPCPerThreadData::gTLSIndex.
//
// We rely on the implementation of NSPR that calls destructors at
// the same order of calling PR_NewThreadPrivateIndex.
XPCPerThreadData::GetData(nsnull);
mJSRuntime = JS_NewRuntime(32L * 1024L * 1024L); // pref ?
if(mJSRuntime)
{