diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index ea2deee86087..73b36d7d39e9 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -5612,7 +5612,7 @@ JS_PUBLIC_API(jsword) JS_GetContextThread(JSContext *cx) { #ifdef JS_THREADSAFE - return JS_THREAD_ID(cx); + return reinterpret_cast(JS_THREAD_ID(cx)); #else return 0; #endif @@ -5629,7 +5629,7 @@ JS_SetContextThread(JSContext *cx) JS_ASSERT(cx->requestDepth == 0); if (cx->thread) { JS_ASSERT(CURRENT_THREAD_IS_ME(cx->thread)); - return cx->thread->id; + return reinterpret_cast(cx->thread->id); } if (!js_InitContextThread(cx)) { @@ -5656,7 +5656,7 @@ JS_ClearContextThread(JSContext *cx) if (!cx->thread) return 0; JS_ASSERT(CURRENT_THREAD_IS_ME(cx->thread)); - jsword old = cx->thread->id; + void *old = cx->thread->id; /* * We must not race with a GC that accesses cx->thread for all threads, @@ -5666,7 +5666,7 @@ JS_ClearContextThread(JSContext *cx) AutoLockGC lock(rt); js_WaitForGC(rt); js_ClearContextThread(cx); - return old; + return reinterpret_cast(old); #else return 0; #endif diff --git a/js/src/jscntxt.cpp b/js/src/jscntxt.cpp index c5a1363d1393..694124301005 100644 --- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -195,7 +195,7 @@ JSThreadData::purgeGCFreeLists() #ifdef JS_THREADSAFE static JSThread * -NewThread(jsword id) +NewThread(void *id) { JS_ASSERT(js_CurrentThreadId() == id); JSThread *thread = (JSThread *) js_calloc(sizeof(JSThread)); @@ -223,7 +223,7 @@ DestroyThread(JSThread *thread) JSThread * js_CurrentThread(JSRuntime *rt) { - jsword id = js_CurrentThreadId(); + void *id = js_CurrentThreadId(); JS_LOCK_GC(rt); /* @@ -231,14 +231,11 @@ js_CurrentThread(JSRuntime *rt) * instances on all threads, see bug 476934. */ js_WaitForGC(rt); - JSThreadsHashEntry *entry = (JSThreadsHashEntry *) - JS_DHashTableOperate(&rt->threads, - (const void *) id, - JS_DHASH_LOOKUP); + JSThread *thread; - if (JS_DHASH_ENTRY_IS_BUSY(&entry->base)) { - thread = entry->thread; - JS_ASSERT(thread->id == id); + JSThread::Map::AddPtr p = rt->threads.lookupForAdd(id); + if (p) { + thread = p->value; } else { JS_UNLOCK_GC(rt); thread = NewThread(id); @@ -246,19 +243,16 @@ js_CurrentThread(JSRuntime *rt) return NULL; JS_LOCK_GC(rt); js_WaitForGC(rt); - entry = (JSThreadsHashEntry *) - JS_DHashTableOperate(&rt->threads, (const void *) id, - JS_DHASH_ADD); - if (!entry) { + if (!rt->threads.relookupOrAdd(p, id, thread)) { JS_UNLOCK_GC(rt); DestroyThread(thread); return NULL; } - /* Another thread cannot initialize entry->thread. */ - JS_ASSERT(!entry->thread); - entry->thread = thread; + /* Another thread cannot add an entry for the current thread id. */ + JS_ASSERT(p->value == thread); } + JS_ASSERT(thread->id == id); return thread; } @@ -283,72 +277,6 @@ js_ClearContextThread(JSContext *cx) 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); - - /* - * The following is potentially suboptimal as it also zeros the caches - * in data, but the code simplicity wins here. - */ - thread->data.purgeGCFreeLists(); - DestroyThread(thread); - return JS_DHASH_REMOVE; - } - thread->data.purge(cx); - thread->gcThreadMallocBytes = JS_GC_THREAD_MALLOC_LIMIT; - return JS_DHASH_NEXT; -} - -static JSDHashOperator -thread_marker(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 /* index */, - void *arg) -{ - JSThread *thread = ((JSThreadsHashEntry *) hdr)->thread; - thread->data.mark((JSTracer *) arg); - return JS_DHASH_NEXT; -} - #endif /* JS_THREADSAFE */ JSThreadData * @@ -369,11 +297,8 @@ JSBool js_InitThreads(JSRuntime *rt) { #ifdef JS_THREADSAFE - if (!JS_DHashTableInit(&rt->threads, &threads_ops, NULL, - sizeof(JSThreadsHashEntry), 4)) { - rt->threads.ops = NULL; + if (!rt->threads.init(4)) return false; - } #else rt->threadData.init(); #endif @@ -384,11 +309,14 @@ void js_FinishThreads(JSRuntime *rt) { #ifdef JS_THREADSAFE - if (!rt->threads.ops) + if (!rt->threads.initialized()) return; - JS_DHashTableEnumerate(&rt->threads, thread_destroyer, NULL); - JS_DHashTableFinish(&rt->threads); - rt->threads.ops = NULL; + for (JSThread::Map::Range r = rt->threads.all(); !r.empty(); r.popFront()) { + JSThread *thread = r.front().value; + JS_ASSERT(JS_CLIST_IS_EMPTY(&thread->contextList)); + DestroyThread(thread); + } + rt->threads.clear(); #else rt->threadData.finish(); #endif @@ -398,22 +326,32 @@ void js_PurgeThreads(JSContext *cx) { #ifdef JS_THREADSAFE - JS_DHashTableEnumerate(&cx->runtime->threads, thread_purger, cx); + for (JSThread::Map::Enum e(cx->runtime->threads); + !e.empty(); + e.popFront()) { + JSThread *thread = e.front().value; + + if (JS_CLIST_IS_EMPTY(&thread->contextList)) { + JS_ASSERT(cx->thread != thread); + js_DestroyScriptsToGC(cx, &thread->data); + + /* + * The following is potentially suboptimal as it also zeros the + * caches in data, but the code simplicity wins here. + */ + thread->data.purgeGCFreeLists(); + DestroyThread(thread); + e.removeFront(); + } else { + thread->data.purge(cx); + thread->gcThreadMallocBytes = JS_GC_THREAD_MALLOC_LIMIT; + } + } #else cx->runtime->threadData.purge(cx); #endif } -void -js_TraceThreads(JSRuntime *rt, JSTracer *trc) -{ -#ifdef JS_THREADSAFE - JS_DHashTableEnumerate(&rt->threads, thread_marker, trc); -#else - rt->threadData.mark(trc); -#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 diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index b7f8953eaf55..d39293b78a69 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -588,11 +588,17 @@ struct JSThreadData { * that can be accessed without a global lock. */ struct JSThread { + typedef js::HashMap, + js::SystemAllocPolicy> Map; + + /* Linked list of all contexts in use on this thread. */ JSCList contextList; /* Opaque thread-id, from NSPR's PR_GetCurrentThread(). */ - jsword id; + void *id; /* Indicates that the thread is waiting in ClaimTitle from jslock.cpp. */ JSTitle *titleToShare; @@ -625,11 +631,6 @@ const size_t JS_GC_THREAD_MALLOC_LIMIT = 1 << 19; #define JS_THREAD_DATA(cx) (&(cx)->thread->data) -struct JSThreadsHashEntry { - JSDHashEntryHdr base; - JSThread *thread; -}; - extern JSThread * js_CurrentThread(JSRuntime *rt); @@ -861,7 +862,7 @@ struct JSRuntime { /* Lock and owning thread pointer for JS_LOCK_RUNTIME. */ PRLock *rtLock; #ifdef DEBUG - jsword rtLockOwner; + void * rtLockOwner; #endif /* Used to synchronize down/up state change; protected by gcLock. */ @@ -897,7 +898,7 @@ struct JSRuntime { */ PRLock *debuggerLock; - JSDHashTable threads; + JSThread::Map threads; #endif /* JS_THREADSAFE */ uint32 debuggerMutations; @@ -2049,8 +2050,48 @@ js_FinishThreads(JSRuntime *rt); extern void js_PurgeThreads(JSContext *cx); -extern void -js_TraceThreads(JSRuntime *rt, JSTracer *trc); +namespace js { + +#ifdef JS_THREADSAFE + +/* Iterator over JSThreadData from all JSThread instances. */ +class ThreadDataIter : public JSThread::Map::Range +{ + public: + ThreadDataIter(JSRuntime *rt) : JSThread::Map::Range(rt->threads.all()) {} + + JSThreadData *threadData() const { + return &front().value->data; + } +}; + +#else /* !JS_THREADSAFE */ + +class ThreadDataIter +{ + JSRuntime *runtime; + bool done; + public: + ThreadDataIter(JSRuntime *rt) : runtime(rt), done(false) {} + + bool empty() const { + return done; + } + + void popFront() { + JS_ASSERT(!done); + done = true; + } + + JSThreadData *threadData() const { + JS_ASSERT(!done); + return &runtime->threadData; + } +}; + +#endif /* !JS_THREADSAFE */ + +} /* namespace js */ /* * Ensures the JSOPTION_XML and JSOPTION_ANONFUNFIX bits of cx->options are diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 0b82c469a8dc..8f22c30fbf65 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -2426,7 +2426,8 @@ js_TraceRuntime(JSTracer *trc) while ((acx = js_ContextIterator(rt, JS_TRUE, &iter)) != NULL) js_TraceContext(trc, acx); - js_TraceThreads(rt, trc); + for (ThreadDataIter i(rt); !i.empty(); i.popFront()) + i.threadData()->mark(trc); if (rt->gcExtraRootsTraceOp) rt->gcExtraRootsTraceOp(trc, rt->gcExtraRootsData); diff --git a/js/src/jshashtable.h b/js/src/jshashtable.h index c88ecb3be202..692ee5527c64 100644 --- a/js/src/jshashtable.h +++ b/js/src/jshashtable.h @@ -354,6 +354,11 @@ class HashTable : AllocPolicy return true; } + bool initialized() const + { + return !!table; + } + ~HashTable() { if (table) @@ -780,6 +785,7 @@ class HashMap */ HashMap(AllocPolicy a = AllocPolicy()) : impl(a) {} bool init(uint32 len = 0) { return impl.init(len); } + bool initialized() const { return impl.initialized(); } /* * Return whether the given lookup value is present in the map. E.g.: @@ -947,6 +953,7 @@ class HashSet */ HashSet(AllocPolicy a = AllocPolicy()) : impl(a) {} bool init(uint32 len = 0) { return impl.init(len); } + bool initialized() const { return impl.initialized(); } /* * Return whether the given lookup value is present in the map. E.g.: diff --git a/js/src/jslock.cpp b/js/src/jslock.cpp index cacfed651a00..6cb89424ec83 100644 --- a/js/src/jslock.cpp +++ b/js/src/jslock.cpp @@ -1199,7 +1199,7 @@ void js_UnlockRuntime(JSRuntime *rt) { #ifdef DEBUG - rt->rtLockOwner = 0; + rt->rtLockOwner = NULL; #endif PR_Unlock(rt->rtLock); } diff --git a/js/src/jslock.h b/js/src/jslock.h index a20b7b28a6b4..6297a1e750b7 100644 --- a/js/src/jslock.h +++ b/js/src/jslock.h @@ -118,7 +118,7 @@ struct JSTitle { #define JS_ATOMIC_ADD(p,v) PR_AtomicAdd((PRInt32 *)(p), (PRInt32)(v)) #define JS_ATOMIC_SET(p,v) PR_AtomicSet((PRInt32 *)(p), (PRInt32)(v)) -#define js_CurrentThreadId() (jsword)PR_GetCurrentThread() +#define js_CurrentThreadId() PR_GetCurrentThread() #define JS_NEW_LOCK() PR_NewLock() #define JS_DESTROY_LOCK(l) PR_DestroyLock(l) #define JS_ACQUIRE_LOCK(l) PR_Lock(l)