зеркало из https://github.com/mozilla/pjs.git
bug 437325 - JSThread is not shared between runtimes. r=brendan
This commit is contained in:
Родитель
63cf3003f6
Коммит
a10e39d51a
|
@ -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);
|
||||
}
|
||||
|
|
143
js/src/jscntxt.h
143
js/src/jscntxt.h
|
@ -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
|
||||
}
|
||||
|
|
110
js/src/jsgc.cpp
110
js/src/jsgc.cpp
|
@ -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)
|
||||
{
|
||||
|
|
Загрузка…
Ссылка в новой задаче