mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-19 00:05:36 +00:00
Patch from Feng Qian <feng.qian.moz@gmail.com> with assist from Igor, based on ancient patch from me, to factor per-thread state from JSContext into JSThread to support per-thread lock-free GC allocation (312238, r=me).
This commit is contained in:
parent
2d7096065f
commit
10cbbda3cf
@ -63,11 +63,11 @@ INCLUDES += -I$(OBJDIR)
|
||||
|
||||
ifdef JS_THREADSAFE
|
||||
DEFINES += -DJS_THREADSAFE
|
||||
INCLUDES += -I../../dist/$(OBJDIR)/include
|
||||
INCLUDES += -I$(DIST)/include/nspr
|
||||
ifdef USE_MSVC
|
||||
OTHER_LIBS += ../../dist/$(OBJDIR)/lib/libnspr${NSPR_LIBSUFFIX}.lib
|
||||
OTHER_LIBS += $(DIST)/lib/libnspr$(NSPR_LIBSUFFIX).lib
|
||||
else
|
||||
OTHER_LIBS += -L../../dist/$(OBJDIR)/lib -lnspr${NSPR_LIBSUFFIX}
|
||||
OTHER_LIBS += -L$(DIST)/lib -lnspr$(NSPR_LIBSUFFIX)
|
||||
endif
|
||||
endif
|
||||
|
||||
@ -92,7 +92,7 @@ endif
|
||||
|
||||
# Prevent floating point errors caused by VC++ optimizations
|
||||
ifeq ($(OS_ARCH),WINNT)
|
||||
CFLAGS += -Op
|
||||
#CFLAGS += -Op
|
||||
endif # WINNT
|
||||
|
||||
#
|
||||
|
@ -40,7 +40,7 @@
|
||||
ifdef JS_DIST
|
||||
DIST = $(JS_DIST)
|
||||
else
|
||||
DIST = $(DEPTH)/../../dist/$(OBJDIR)
|
||||
DIST = $(DEPTH)/../../dist
|
||||
endif
|
||||
|
||||
# Set os+release dependent make variables
|
||||
@ -112,13 +112,13 @@ ifeq ($(OS_ARCH), WINNT)
|
||||
INSTALL = nsinstall
|
||||
CP = cp
|
||||
else
|
||||
INSTALL = $(DEPTH)/../../dist/$(OBJDIR)/bin/nsinstall
|
||||
INSTALL = $(DIST)/bin/nsinstall
|
||||
CP = cp
|
||||
endif
|
||||
|
||||
ifdef BUILD_OPT
|
||||
OPTIMIZER = -O
|
||||
DEFINES += -UDEBUG -DNDEBUG -UDEBUG_$(shell whoami)
|
||||
DEFINES += -UDEBUG -DNDEBUG -UDEBUG_$(USERNAME)
|
||||
OBJDIR_TAG = _OPT
|
||||
else
|
||||
ifdef USE_MSVC
|
||||
@ -126,7 +126,7 @@ OPTIMIZER = -Zi
|
||||
else
|
||||
OPTIMIZER = -g
|
||||
endif
|
||||
DEFINES += -DDEBUG -DDEBUG_$(shell whoami)
|
||||
DEFINES += -DDEBUG -DDEBUG_$(USERNAME)
|
||||
OBJDIR_TAG = _DBG
|
||||
endif
|
||||
|
||||
|
@ -2533,6 +2533,10 @@ main(int argc, char **argv, char **envp)
|
||||
return 1;
|
||||
JS_SetErrorReporter(cx, my_ErrorReporter);
|
||||
|
||||
#ifdef JS_THREADSAFE
|
||||
JS_BeginRequest(cx);
|
||||
#endif
|
||||
|
||||
glob = JS_NewObject(cx, &global_class, NULL, NULL);
|
||||
if (!glob)
|
||||
return 1;
|
||||
@ -2628,6 +2632,10 @@ main(int argc, char **argv, char **envp)
|
||||
JSD_DebuggerOff(_jsdc);
|
||||
#endif /* JSDEBUGGER */
|
||||
|
||||
#ifdef JS_THREADSAFE
|
||||
JS_EndRequest(cx);
|
||||
#endif
|
||||
|
||||
JS_DestroyContext(cx);
|
||||
JS_DestroyRuntime(rt);
|
||||
JS_ShutDown();
|
||||
|
@ -676,6 +676,10 @@ JS_NewRuntime(uint32 maxbytes)
|
||||
if (!js_InitGC(rt, maxbytes))
|
||||
goto bad;
|
||||
#ifdef JS_THREADSAFE
|
||||
if (PR_FAILURE == PR_NewThreadPrivateIndex(&rt->threadTPIndex,
|
||||
js_ThreadDestructorCB)) {
|
||||
goto bad;
|
||||
}
|
||||
rt->gcLock = JS_NEW_LOCK();
|
||||
if (!rt->gcLock)
|
||||
goto bad;
|
||||
@ -786,13 +790,13 @@ JS_BeginRequest(JSContext *cx)
|
||||
{
|
||||
JSRuntime *rt;
|
||||
|
||||
JS_ASSERT(cx->thread == js_CurrentThreadId());
|
||||
JS_ASSERT(cx->thread->id == js_CurrentThreadId());
|
||||
if (!cx->requestDepth) {
|
||||
/* Wait until the GC is finished. */
|
||||
rt = cx->runtime;
|
||||
JS_LOCK_GC(rt);
|
||||
|
||||
/* NB: we use cx->thread here, not js_CurrentThreadId(). */
|
||||
/* NB: we use cx->thread here, not js_GetCurrentThread(). */
|
||||
if (rt->gcThread != cx->thread) {
|
||||
while (rt->gcLevel > 0)
|
||||
JS_AWAIT_GC_DONE(rt);
|
||||
@ -1822,7 +1826,7 @@ JS_MarkGCThing(JSContext *cx, void *thing, const char *name, void *arg)
|
||||
{
|
||||
JS_ASSERT(cx->runtime->gcLevel > 0);
|
||||
#ifdef JS_THREADSAFE
|
||||
JS_ASSERT(cx->runtime->gcThread == js_CurrentThreadId());
|
||||
JS_ASSERT(cx->runtime->gcThread->id == js_CurrentThreadId());
|
||||
#endif
|
||||
|
||||
GC_MARK(cx, thing, name);
|
||||
@ -4786,25 +4790,34 @@ JS_ThrowReportedError(JSContext *cx, const char *message,
|
||||
}
|
||||
|
||||
#ifdef JS_THREADSAFE
|
||||
/*
|
||||
* Get the owning thread id of a context. Returns 0 if the context is not
|
||||
* owned by any thread.
|
||||
*/
|
||||
JS_PUBLIC_API(jsword)
|
||||
JS_GetContextThread(JSContext *cx)
|
||||
{
|
||||
return cx->thread;
|
||||
return JS_THREAD_ID(cx);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the current thread as the owning thread of a context. Returns the
|
||||
* old owning thread id, or -1 if the operation failed.
|
||||
*/
|
||||
JS_PUBLIC_API(jsword)
|
||||
JS_SetContextThread(JSContext *cx)
|
||||
{
|
||||
jsword old = cx->thread;
|
||||
cx->thread = js_CurrentThreadId();
|
||||
jsword old = JS_THREAD_ID(cx);
|
||||
if (!js_SetContextThread(cx))
|
||||
return -1;
|
||||
return old;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(jsword)
|
||||
JS_ClearContextThread(JSContext *cx)
|
||||
{
|
||||
jsword old = cx->thread;
|
||||
cx->thread = 0;
|
||||
jsword old = JS_THREAD_ID(cx);
|
||||
js_ClearContextThread(cx);
|
||||
return old;
|
||||
}
|
||||
#endif
|
||||
|
@ -64,6 +64,88 @@
|
||||
#include "jsscript.h"
|
||||
#include "jsstr.h"
|
||||
|
||||
#ifdef JS_THREADSAFE
|
||||
|
||||
/*
|
||||
* Callback function to delete a JSThread info when the thread that owns it
|
||||
* is destroyed.
|
||||
*/
|
||||
void JS_DLL_CALLBACK
|
||||
js_ThreadDestructorCB(void *ptr)
|
||||
{
|
||||
JSThread *thread = (JSThread *)ptr;
|
||||
|
||||
if (!thread)
|
||||
return;
|
||||
while (!JS_CLIST_IS_EMPTY(&thread->contextList))
|
||||
JS_REMOVE_AND_INIT_LINK(thread->contextList.next);
|
||||
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)
|
||||
{
|
||||
JSThread *thread;
|
||||
|
||||
thread = (JSThread *)PR_GetThreadPrivate(rt->threadTPIndex);
|
||||
if (!thread) {
|
||||
/* New memory is set to 0 so that elements in gcFreeLists are NULL. */
|
||||
thread = (JSThread *) calloc(1, sizeof(JSThread));
|
||||
if (!thread)
|
||||
return NULL;
|
||||
|
||||
if (PR_FAILURE == PR_SetThreadPrivate(rt->threadTPIndex, thread)) {
|
||||
free(thread);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
JS_INIT_CLIST(&thread->contextList);
|
||||
thread->id = js_CurrentThreadId();
|
||||
}
|
||||
return thread;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets current thread as owning thread of a context by assigning the
|
||||
* thread-private info to the context. If the current thread doesn't have
|
||||
* private JSThread info, create one.
|
||||
*/
|
||||
JSBool
|
||||
js_SetContextThread(JSContext *cx)
|
||||
{
|
||||
JSThread *thread = js_GetCurrentThread(cx->runtime);
|
||||
|
||||
if (!thread) {
|
||||
JS_ReportOutOfMemory(cx);
|
||||
return JS_FALSE;
|
||||
}
|
||||
cx->thread = thread;
|
||||
JS_REMOVE_LINK(&cx->threadLinks);
|
||||
JS_APPEND_LINK(&cx->threadLinks, &thread->contextList);
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
/* Remove the owning thread info of a context. */
|
||||
void
|
||||
js_ClearContextThread(JSContext *cx)
|
||||
{
|
||||
cx->thread = NULL;
|
||||
JS_REMOVE_LINK(&cx->threadLinks);
|
||||
}
|
||||
|
||||
#endif /* JS_THREADSAFE */
|
||||
|
||||
void
|
||||
js_OnVersionChange(JSContext *cx)
|
||||
{
|
||||
@ -101,7 +183,8 @@ js_NewContext(JSRuntime *rt, size_t stackChunkSize)
|
||||
cx->stackLimit = (jsuword)-1;
|
||||
#endif
|
||||
#ifdef JS_THREADSAFE
|
||||
js_InitContextForLocking(cx);
|
||||
JS_INIT_CLIST(&cx->threadLinks);
|
||||
js_SetContextThread(cx);
|
||||
#endif
|
||||
|
||||
JS_LOCK_GC(rt);
|
||||
@ -140,9 +223,6 @@ js_NewContext(JSRuntime *rt, size_t stackChunkSize)
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
#if JS_HAS_EXCEPTIONS
|
||||
cx->throwing = JS_FALSE;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* If cx is the first context on this runtime, initialize well-known atoms,
|
||||
@ -320,6 +400,10 @@ js_DestroyContext(JSContext *cx, JSGCMode gcmode)
|
||||
JS_free(cx, lrs);
|
||||
}
|
||||
|
||||
#ifdef JS_THREADSAFE
|
||||
js_ClearContextThread(cx);
|
||||
#endif
|
||||
|
||||
/* Finally, free cx itself. */
|
||||
free(cx);
|
||||
}
|
||||
@ -700,7 +784,7 @@ ReportError(JSContext *cx, const char *message, JSErrorReport *reportp)
|
||||
* exception is thrown, then the JSREPORT_EXCEPTION flag will be set
|
||||
* on the error report, and exception-aware hosts should ignore it.
|
||||
*/
|
||||
JS_ASSERT(reportp);
|
||||
JS_ASSERT(reportp);
|
||||
if (reportp->errorNumber == JSMSG_UNCAUGHT_EXCEPTION)
|
||||
reportp->flags |= JSREPORT_EXCEPTION;
|
||||
|
||||
@ -741,7 +825,7 @@ js_ReportOutOfMemory(JSContext *cx)
|
||||
JSErrorReporter onError = cx->errorReporter;
|
||||
|
||||
/* Get the message for this error, but we won't expand any arguments. */
|
||||
const JSErrorFormatString *efs =
|
||||
const JSErrorFormatString *efs =
|
||||
js_GetLocalizedErrorMessage(cx, NULL, NULL, JSMSG_OUT_OF_MEMORY);
|
||||
const char *msg = efs ? efs->format : "Out of memory";
|
||||
|
||||
|
@ -57,6 +57,39 @@
|
||||
|
||||
JS_BEGIN_EXTERN_C
|
||||
|
||||
#ifdef JS_THREADSAFE
|
||||
|
||||
/*
|
||||
* Structure uniquely representing a thread. It holds thread-private data
|
||||
* that can be accessed without a global lock.
|
||||
*/
|
||||
struct JSThread {
|
||||
/* Linked list of all contexts active on this thread. */
|
||||
JSCList contextList;
|
||||
|
||||
/* Opaque thread-id, from NSPR's PR_GetCurrentThread(). */
|
||||
jsword id;
|
||||
|
||||
#if 0
|
||||
/* Thread-local gc free lists array. */
|
||||
JSGCThing *gcFreeLists[GC_NUM_FREELISTS];
|
||||
#endif
|
||||
};
|
||||
|
||||
extern void JS_DLL_CALLBACK
|
||||
js_ThreadDestructorCB(void *ptr);
|
||||
|
||||
extern JSBool
|
||||
js_SetContextThread(JSContext *cx);
|
||||
|
||||
extern void
|
||||
js_ClearContextThread(JSContext *cx);
|
||||
|
||||
extern JSThread *
|
||||
js_GetCurrentThread(JSRuntime *rt);
|
||||
|
||||
#endif /* JS_THREADSAFE */
|
||||
|
||||
typedef enum JSGCMode { JS_NO_GC, JS_MAYBE_GC, JS_FORCE_GC } JSGCMode;
|
||||
|
||||
typedef enum JSRuntimeState {
|
||||
@ -187,7 +220,7 @@ struct JSRuntime {
|
||||
PRCondVar *gcDone;
|
||||
PRCondVar *requestDone;
|
||||
uint32 requestCount;
|
||||
jsword gcThread;
|
||||
JSThread *gcThread;
|
||||
|
||||
/* Lock and owning thread pointer for JS_LOCK_RUNTIME. */
|
||||
PRLock *rtLock;
|
||||
@ -225,6 +258,13 @@ struct JSRuntime {
|
||||
* value.
|
||||
*/
|
||||
#define NO_SCOPE_SHARING_TODO ((JSScope *) 0xfeedbeef)
|
||||
|
||||
/*
|
||||
* The index for JSThread info, returned by PR_NewThreadPrivateIndex.
|
||||
* The value is visible and shared by all threads, but the data is
|
||||
* private to each thread.
|
||||
*/
|
||||
PRUintn threadTPIndex;
|
||||
#endif /* JS_THREADSAFE */
|
||||
|
||||
/*
|
||||
@ -431,6 +471,7 @@ struct JSTempValueRooter {
|
||||
JS_END_MACRO
|
||||
|
||||
struct JSContext {
|
||||
/* JSRuntime contextList linkage. */
|
||||
JSCList links;
|
||||
|
||||
/* Interpreter activation count. */
|
||||
@ -491,11 +532,15 @@ struct JSContext {
|
||||
/* GC and thread-safe state. */
|
||||
JSStackFrame *dormantFrameChain; /* dormant stack frame to scan */
|
||||
#ifdef JS_THREADSAFE
|
||||
jsword thread;
|
||||
JSThread *thread;
|
||||
jsrefcount requestDepth;
|
||||
JSScope *scopeToShare; /* weak reference, see jslock.c */
|
||||
JSScope *lockedSealedScope; /* weak ref, for low-cost sealed
|
||||
scope locking */
|
||||
JSCList threadLinks; /* JSThread contextList linkage */
|
||||
|
||||
#define CX_FROM_THREAD_LINKS(tl) \
|
||||
((JSContext *)((char *)(tl) - offsetof(JSContext, threadLinks)))
|
||||
#endif
|
||||
|
||||
#if JS_HAS_LVALUE_RETURN
|
||||
@ -564,6 +609,8 @@ struct JSContext {
|
||||
#endif
|
||||
};
|
||||
|
||||
#define JS_THREAD_ID(cx) ((cx)->thread ? (cx)->thread->id : 0)
|
||||
|
||||
/*
|
||||
* Slightly more readable macros for testing per-context option settings (also
|
||||
* to hide bitset implementation detail).
|
||||
@ -584,7 +631,7 @@ struct JSContext {
|
||||
*
|
||||
* Note that JS_SetVersion API calls never pass JSVERSION_HAS_XML or'd into
|
||||
* that API's version parameter.
|
||||
*
|
||||
*
|
||||
* Note also that script->version must contain this XML option flag in order
|
||||
* for XDR'ed scripts to serialize and deserialize with that option preserved
|
||||
* for detection at run-time. We can't copy other compile-time options into
|
||||
|
@ -270,7 +270,7 @@ DestroyGCArena(JSGCArenaList *arenaList, JSGCArena **ap)
|
||||
JS_ASSERT(a);
|
||||
METER(--arenaList->stats.narenas);
|
||||
if (a == arenaList->last)
|
||||
arenaList->lastLimit = a->prev ? GC_THINGS_SIZE : 0;
|
||||
arenaList->lastLimit = (uint16)(a->prev ? GC_THINGS_SIZE : 0);
|
||||
*ap = a->prev;
|
||||
|
||||
#ifdef DEBUG
|
||||
@ -485,6 +485,9 @@ js_DumpGCStats(JSRuntime *rt, FILE *fp)
|
||||
fprintf(fp, " public bytes allocated: %lu\n", UL(rt->gcBytes));
|
||||
fprintf(fp, " private bytes allocated: %lu\n", UL(rt->gcPrivateBytes));
|
||||
fprintf(fp, " alloc attempts: %lu\n", ULSTAT(alloc));
|
||||
#ifdef JS_THREADSAFE
|
||||
fprintf(fp, " alloc without locks: %1u\n", ULSTAT(localalloc));
|
||||
#endif
|
||||
fprintf(fp, " total GC things: %lu\n", UL(totalThings));
|
||||
fprintf(fp, " max total GC things: %lu\n", UL(totalMaxThings));
|
||||
fprintf(fp, " GC things size: %lu\n", UL(totalBytes));
|
||||
@ -609,7 +612,7 @@ js_AddRootRT(JSRuntime *rt, void *rp, const char *name)
|
||||
JS_LOCK_GC(rt);
|
||||
#ifdef JS_THREADSAFE
|
||||
JS_ASSERT(!rt->gcRunning || rt->gcLevel > 0);
|
||||
if (rt->gcRunning && rt->gcThread != js_CurrentThreadId()) {
|
||||
if (rt->gcRunning && rt->gcThread->id != js_CurrentThreadId()) {
|
||||
do {
|
||||
JS_AWAIT_GC_DONE(rt);
|
||||
} while (rt->gcLevel > 0);
|
||||
@ -638,7 +641,7 @@ js_RemoveRoot(JSRuntime *rt, void *rp)
|
||||
JS_LOCK_GC(rt);
|
||||
#ifdef JS_THREADSAFE
|
||||
JS_ASSERT(!rt->gcRunning || rt->gcLevel > 0);
|
||||
if (rt->gcRunning && rt->gcThread != js_CurrentThreadId()) {
|
||||
if (rt->gcRunning && rt->gcThread->id != js_CurrentThreadId()) {
|
||||
do {
|
||||
JS_AWAIT_GC_DONE(rt);
|
||||
} while (rt->gcLevel > 0);
|
||||
@ -1425,8 +1428,8 @@ MarkGCThingChildren(JSContext *cx, void *thing, uint8 *flagp,
|
||||
}
|
||||
|
||||
/*
|
||||
* Not using PAGE_THING_GAP inside this macro to optimize
|
||||
* thingsPerUnscannedChunk calculation when thingSize == 2^power.
|
||||
* Avoid using PAGE_THING_GAP inside this macro to optimize the
|
||||
* thingsPerUnscannedChunk calculation when thingSize is a power of two.
|
||||
*/
|
||||
#define GET_GAP_AND_CHUNK_SPAN(thingSize, thingsPerUnscannedChunk, pageGap) \
|
||||
JS_BEGIN_MACRO \
|
||||
@ -1807,7 +1810,6 @@ js_GC(JSContext *cx, uintN gcflags)
|
||||
uint32 *bytesptr;
|
||||
JSBool all_clear;
|
||||
#ifdef JS_THREADSAFE
|
||||
jsword currentThread;
|
||||
uint32 requestDebit;
|
||||
#endif
|
||||
|
||||
@ -1851,8 +1853,7 @@ js_GC(JSContext *cx, uintN gcflags)
|
||||
|
||||
#ifdef JS_THREADSAFE
|
||||
/* Bump gcLevel and return rather than nest on this thread. */
|
||||
currentThread = js_CurrentThreadId();
|
||||
if (rt->gcThread == currentThread) {
|
||||
if (rt->gcThread && rt->gcThread->id == js_CurrentThreadId()) {
|
||||
JS_ASSERT(rt->gcLevel > 0);
|
||||
rt->gcLevel++;
|
||||
METER(if (rt->gcLevel > rt->gcStats.maxlevel)
|
||||
@ -1865,25 +1866,29 @@ js_GC(JSContext *cx, uintN gcflags)
|
||||
/*
|
||||
* 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. NB: if cx->thread is 0, then cx is not using
|
||||
* requests are inactive. If cx->thread is NULL, then cx is not using
|
||||
* the request model, and does not contribute to rt->requestCount.
|
||||
*/
|
||||
requestDebit = 0;
|
||||
if (cx->thread) {
|
||||
JSCList *head, *link;
|
||||
|
||||
/*
|
||||
* Check all contexts for any with the same thread-id. XXX should we
|
||||
* keep a sub-list of contexts having the same id?
|
||||
* Check all contexts on cx->thread->contextList for active requests,
|
||||
* counting each such context against requestDebit.
|
||||
*/
|
||||
iter = NULL;
|
||||
while ((acx = js_ContextIterator(rt, JS_FALSE, &iter)) != NULL) {
|
||||
if (acx->thread == cx->thread && acx->requestDepth)
|
||||
head = &cx->thread->contextList;
|
||||
for (link = head->next; link != head; link = link->next) {
|
||||
acx = CX_FROM_THREAD_LINKS(link);
|
||||
JS_ASSERT(acx->thread == cx->thread);
|
||||
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 0 thread-ids,
|
||||
* 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.
|
||||
*/
|
||||
@ -1917,7 +1922,8 @@ js_GC(JSContext *cx, uintN gcflags)
|
||||
|
||||
/* No other thread is in GC, so indicate that we're now in GC. */
|
||||
rt->gcLevel = 1;
|
||||
rt->gcThread = currentThread;
|
||||
rt->gcThread = js_GetCurrentThread(rt);
|
||||
JS_ASSERT(rt->gcThread);
|
||||
|
||||
/* Wait for all other requests to finish. */
|
||||
while (rt->requestCount > 0)
|
||||
@ -2259,7 +2265,7 @@ restart:
|
||||
/* If we were invoked during a request, pay back the temporary debit. */
|
||||
if (requestDebit)
|
||||
rt->requestCount += requestDebit;
|
||||
rt->gcThread = 0;
|
||||
rt->gcThread = NULL;
|
||||
JS_NOTIFY_GC_DONE(rt);
|
||||
if (!(gcflags & GC_ALREADY_LOCKED))
|
||||
JS_UNLOCK_GC(rt);
|
||||
|
@ -45,7 +45,6 @@
|
||||
#include "jsstddef.h"
|
||||
#include <stdlib.h>
|
||||
#include "jspubtd.h"
|
||||
#include "prthread.h"
|
||||
#include "jsutil.h" /* Added by JSIFY */
|
||||
#include "jstypes.h"
|
||||
#include "jsbit.h"
|
||||
@ -201,12 +200,6 @@ js_CompareAndSwap(jsword *w, jsword ov, jsword nv)
|
||||
|
||||
#endif /* !NSPR_LOCK */
|
||||
|
||||
jsword
|
||||
js_CurrentThreadId()
|
||||
{
|
||||
return CurrentThreadId();
|
||||
}
|
||||
|
||||
void
|
||||
js_InitLock(JSThinLock *tl)
|
||||
{
|
||||
@ -354,7 +347,7 @@ ShareScope(JSRuntime *rt, JSScope *scope)
|
||||
* scope->ownercx's transition to null against tests of that member
|
||||
* in ClaimScope.
|
||||
*/
|
||||
scope->lock.owner = scope->ownercx->thread;
|
||||
scope->lock.owner = CX_THINLOCK_ID(scope->ownercx);
|
||||
#ifdef NSPR_LOCK
|
||||
JS_ACQUIRE_LOCK((JSLock*)scope->lock.fat);
|
||||
#endif
|
||||
@ -613,8 +606,8 @@ js_GetSlotThreadSafe(JSContext *cx, JSObject *obj, uint32 slot)
|
||||
|
||||
#ifndef NSPR_LOCK
|
||||
tl = &scope->lock;
|
||||
me = cx->thread;
|
||||
JS_ASSERT(me == CurrentThreadId());
|
||||
me = CX_THINLOCK_ID(cx);
|
||||
JS_ASSERT(CURRENT_THREAD_IS_ME(me));
|
||||
if (js_CompareAndSwap(&tl->owner, 0, me)) {
|
||||
/*
|
||||
* Got the lock with one compare-and-swap. Even so, someone else may
|
||||
@ -704,8 +697,8 @@ js_SetSlotThreadSafe(JSContext *cx, JSObject *obj, uint32 slot, jsval v)
|
||||
|
||||
#ifndef NSPR_LOCK
|
||||
tl = &scope->lock;
|
||||
me = cx->thread;
|
||||
JS_ASSERT(me == CurrentThreadId());
|
||||
me = CX_THINLOCK_ID(cx);
|
||||
JS_ASSERT(CURRENT_THREAD_IS_ME(me));
|
||||
if (js_CompareAndSwap(&tl->owner, 0, me)) {
|
||||
if (scope == OBJ_SCOPE(obj)) {
|
||||
obj->slots[slot] = v;
|
||||
@ -909,13 +902,6 @@ js_CleanupLocks()
|
||||
#endif /* !NSPR_LOCK */
|
||||
}
|
||||
|
||||
void
|
||||
js_InitContextForLocking(JSContext *cx)
|
||||
{
|
||||
cx->thread = CurrentThreadId();
|
||||
JS_ASSERT(Thin_GetWait(cx->thread) == 0);
|
||||
}
|
||||
|
||||
#ifndef NSPR_LOCK
|
||||
|
||||
/*
|
||||
@ -1036,7 +1022,7 @@ js_Dequeue(JSThinLock *tl)
|
||||
JS_INLINE void
|
||||
js_Lock(JSThinLock *tl, jsword me)
|
||||
{
|
||||
JS_ASSERT(me == CurrentThreadId());
|
||||
JS_ASSERT(CURRENT_THREAD_IS_ME(me));
|
||||
if (js_CompareAndSwap(&tl->owner, 0, me))
|
||||
return;
|
||||
if (Thin_RemoveWait(ReadWord(tl->owner)) != me)
|
||||
@ -1050,14 +1036,22 @@ js_Lock(JSThinLock *tl, jsword me)
|
||||
JS_INLINE void
|
||||
js_Unlock(JSThinLock *tl, jsword me)
|
||||
{
|
||||
JS_ASSERT(me == CurrentThreadId());
|
||||
if (js_CompareAndSwap(&tl->owner, me, 0))
|
||||
JS_ASSERT(CURRENT_THREAD_IS_ME(me));
|
||||
|
||||
/*
|
||||
* Only me can hold the lock, no need to use compare and swap atomic
|
||||
* operation for this common case.
|
||||
*/
|
||||
if (tl->owner == me) {
|
||||
tl->owner = 0;
|
||||
return;
|
||||
}
|
||||
JS_ASSERT(Thin_GetWait(tl->owner));
|
||||
if (Thin_RemoveWait(ReadWord(tl->owner)) == me)
|
||||
js_Dequeue(tl);
|
||||
#ifdef DEBUG
|
||||
else
|
||||
JS_ASSERT(0);
|
||||
JS_ASSERT(0); /* unbalanced unlock */
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -1068,7 +1062,7 @@ js_LockRuntime(JSRuntime *rt)
|
||||
{
|
||||
PR_Lock(rt->rtLock);
|
||||
#ifdef DEBUG
|
||||
rt->rtLockOwner = CurrentThreadId();
|
||||
rt->rtLockOwner = js_CurrentThreadId();
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -1084,9 +1078,9 @@ js_UnlockRuntime(JSRuntime *rt)
|
||||
void
|
||||
js_LockScope(JSContext *cx, JSScope *scope)
|
||||
{
|
||||
jsword me = cx->thread;
|
||||
jsword me = CX_THINLOCK_ID(cx);
|
||||
|
||||
JS_ASSERT(me == CurrentThreadId());
|
||||
JS_ASSERT(CURRENT_THREAD_IS_ME(me));
|
||||
JS_ASSERT(scope->ownercx != cx);
|
||||
if (CX_THREAD_IS_RUNNING_GC(cx))
|
||||
return;
|
||||
@ -1109,7 +1103,7 @@ js_LockScope(JSContext *cx, JSScope *scope)
|
||||
void
|
||||
js_UnlockScope(JSContext *cx, JSScope *scope)
|
||||
{
|
||||
jsword me = cx->thread;
|
||||
jsword me = CX_THINLOCK_ID(cx);
|
||||
|
||||
/* We hope compilers use me instead of reloading cx->thread in the macro. */
|
||||
if (CX_THREAD_IS_RUNNING_GC(cx))
|
||||
@ -1221,7 +1215,7 @@ js_TransferScopeLock(JSContext *cx, JSScope *oldscope, JSScope *newscope)
|
||||
LOGIT(oldscope, '0');
|
||||
oldscope->u.count = 0;
|
||||
tl = &oldscope->lock;
|
||||
me = cx->thread;
|
||||
me = CX_THINLOCK_ID(cx);
|
||||
JS_UNLOCK0(tl, me);
|
||||
}
|
||||
|
||||
@ -1262,7 +1256,7 @@ js_UnlockObj(JSContext *cx, JSObject *obj)
|
||||
JSBool
|
||||
js_IsRuntimeLocked(JSRuntime *rt)
|
||||
{
|
||||
return CurrentThreadId() == rt->rtLockOwner;
|
||||
return js_CurrentThreadId() == rt->rtLockOwner;
|
||||
}
|
||||
|
||||
JSBool
|
||||
@ -1292,7 +1286,8 @@ js_IsScopeLocked(JSContext *cx, JSScope *scope)
|
||||
JS_ASSERT(scope->ownercx == cx);
|
||||
return JS_TRUE;
|
||||
}
|
||||
return CurrentThreadId() == Thin_RemoveWait(ReadWord(scope->lock.owner));
|
||||
return js_CurrentThreadId() ==
|
||||
((JSThread *)Thin_RemoveWait(ReadWord(scope->lock.owner)))->id;
|
||||
}
|
||||
|
||||
#endif /* DEBUG */
|
||||
|
@ -45,6 +45,7 @@
|
||||
#include "pratom.h"
|
||||
#include "prlock.h"
|
||||
#include "prcvar.h"
|
||||
#include "prthread.h"
|
||||
|
||||
#include "jsprvtd.h" /* for JSScope, etc. */
|
||||
#include "jspubtd.h" /* for JSRuntime, etc. */
|
||||
@ -68,6 +69,9 @@ typedef struct JSThinLock {
|
||||
JSFatLock *fat;
|
||||
} JSThinLock;
|
||||
|
||||
#define CX_THINLOCK_ID(cx) ((jsword)(cx)->thread)
|
||||
#define CURRENT_THREAD_IS_ME(me) (((JSThread *)me)->id == js_CurrentThreadId())
|
||||
|
||||
typedef PRLock JSLock;
|
||||
|
||||
typedef struct JSFatLockTable {
|
||||
@ -83,8 +87,7 @@ typedef struct JSFatLockTable {
|
||||
#define JS_ATOMIC_DECREMENT(p) PR_AtomicDecrement((PRInt32 *)(p))
|
||||
#define JS_ATOMIC_ADD(p,v) PR_AtomicAdd((PRInt32 *)(p), (PRInt32)(v))
|
||||
|
||||
#define CurrentThreadId() (jsword)PR_GetCurrentThread()
|
||||
#define JS_CurrentThreadId() js_CurrentThreadId()
|
||||
#define js_CurrentThreadId() (jsword)PR_GetCurrentThread()
|
||||
#define JS_NEW_LOCK() PR_NewLock()
|
||||
#define JS_DESTROY_LOCK(l) PR_DestroyLock(l)
|
||||
#define JS_ACQUIRE_LOCK(l) PR_Lock(l)
|
||||
@ -132,7 +135,6 @@ typedef struct JSFatLockTable {
|
||||
#define JS_TRANSFER_SCOPE_LOCK(cx, scope, newscope) \
|
||||
js_TransferScopeLock(cx, scope, newscope)
|
||||
|
||||
extern jsword js_CurrentThreadId();
|
||||
extern void js_LockRuntime(JSRuntime *rt);
|
||||
extern void js_UnlockRuntime(JSRuntime *rt);
|
||||
extern void js_LockObj(JSContext *cx, JSObject *obj);
|
||||
@ -141,7 +143,6 @@ extern void js_LockScope(JSContext *cx, JSScope *scope);
|
||||
extern void js_UnlockScope(JSContext *cx, JSScope *scope);
|
||||
extern int js_SetupLocks(int,int);
|
||||
extern void js_CleanupLocks();
|
||||
extern void js_InitContextForLocking(JSContext *);
|
||||
extern void js_TransferScopeLock(JSContext *, JSScope *, JSScope *);
|
||||
extern JS_FRIEND_API(jsval)
|
||||
js_GetSlotThreadSafe(JSContext *, JSObject *, uint32);
|
||||
@ -256,7 +257,7 @@ extern JS_INLINE void js_Unlock(JSThinLock *tl, jsword me);
|
||||
JS_NO_TIMEOUT)
|
||||
#define JS_NOTIFY_REQUEST_DONE(rt) JS_NOTIFY_CONDVAR((rt)->requestDone)
|
||||
|
||||
#define JS_LOCK(P,CX) JS_LOCK0(P,(CX)->thread)
|
||||
#define JS_UNLOCK(P,CX) JS_UNLOCK0(P,(CX)->thread)
|
||||
#define JS_LOCK(P,CX) JS_LOCK0(P, CX_THINLOCK_ID(CX))
|
||||
#define JS_UNLOCK(P,CX) JS_UNLOCK0(P, CX_THINLOCK_ID(CX))
|
||||
|
||||
#endif /* jslock_h___ */
|
||||
|
@ -96,6 +96,7 @@ typedef struct JSGCRootHashEntry JSGCRootHashEntry;
|
||||
typedef struct JSGCThing JSGCThing;
|
||||
typedef struct JSParseNode JSParseNode;
|
||||
typedef struct JSSharpObjectMap JSSharpObjectMap;
|
||||
typedef struct JSThread JSThread;
|
||||
typedef struct JSToken JSToken;
|
||||
typedef struct JSTokenPos JSTokenPos;
|
||||
typedef struct JSTokenPtr JSTokenPtr;
|
||||
|
Loading…
Reference in New Issue
Block a user