bug 563345 - using js::HashMap for JSRuntime::threads. r=lw

This commit is contained in:
Igor Bukanov 2010-05-13 19:50:43 +02:00
parent 18d22c68c6
commit 307503fef7
7 changed files with 105 additions and 118 deletions

View File

@ -5612,7 +5612,7 @@ JS_PUBLIC_API(jsword)
JS_GetContextThread(JSContext *cx)
{
#ifdef JS_THREADSAFE
return JS_THREAD_ID(cx);
return reinterpret_cast<jsword>(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<jsword>(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<jsword>(old);
#else
return 0;
#endif

View File

@ -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

View File

@ -588,11 +588,17 @@ struct JSThreadData {
* that can be accessed without a global lock.
*/
struct JSThread {
typedef js::HashMap<void *,
JSThread *,
js::DefaultHasher<void *>,
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

View File

@ -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);

View File

@ -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.:

View File

@ -1199,7 +1199,7 @@ void
js_UnlockRuntime(JSRuntime *rt)
{
#ifdef DEBUG
rt->rtLockOwner = 0;
rt->rtLockOwner = NULL;
#endif
PR_Unlock(rt->rtLock);
}

View File

@ -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)