diff --git a/js/public/MemoryMetrics.h b/js/public/MemoryMetrics.h index 7968efbf9995..78a83301d7fe 100644 --- a/js/public/MemoryMetrics.h +++ b/js/public/MemoryMetrics.h @@ -122,10 +122,10 @@ struct IterateData : runtimeObject(0) , runtimeAtomsTable(0) , runtimeContexts(0) - , runtimeThreadsNormal(0) - , runtimeThreadsTemporary(0) - , runtimeThreadsRegexpCode(0) - , runtimeThreadsStackCommitted(0) + , runtimeNormal(0) + , runtimeTemporary(0) + , runtimeRegexpCode(0) + , runtimeStackCommitted(0) , gcHeapChunkTotal(0) , gcHeapChunkCleanUnused(0) , gcHeapChunkDirtyUnused(0) @@ -153,10 +153,10 @@ struct IterateData int64_t runtimeObject; int64_t runtimeAtomsTable; int64_t runtimeContexts; - int64_t runtimeThreadsNormal; - int64_t runtimeThreadsTemporary; - int64_t runtimeThreadsRegexpCode; - int64_t runtimeThreadsStackCommitted; + int64_t runtimeNormal; + int64_t runtimeTemporary; + int64_t runtimeRegexpCode; + int64_t runtimeStackCommitted; int64_t gcHeapChunkTotal; int64_t gcHeapChunkCleanUnused; int64_t gcHeapChunkDirtyUnused; diff --git a/js/src/Makefile.in b/js/src/Makefile.in index cf65992e553c..342695e25096 100644 --- a/js/src/Makefile.in +++ b/js/src/Makefile.in @@ -130,7 +130,6 @@ CPPSRCS = \ jsinfer.cpp \ jsinterp.cpp \ jsiter.cpp \ - jslock.cpp \ jslog2.cpp \ jsmath.cpp \ jsnativestack.cpp \ @@ -619,7 +618,7 @@ check-malloc-function-usage: $(filter-out %jsalloc.h %jscntxt.h %jsutil.h, $(ALL # We desire these numbers to go down, not up. See "User guide to memory # management within SpiderMonkey" in jsutil.h. - $(srcdir)/config/check_source_count.py OffTheBooks:: 59 \ + $(srcdir)/config/check_source_count.py OffTheBooks:: 58 \ "in Makefile.in" "{cx,rt}->{new_,array_new,malloc_,calloc_,realloc_}" $^ # This should go to zero, if possible. $(srcdir)/config/check_source_count.py UnwantedForeground:: 31 \ @@ -700,10 +699,6 @@ DEFINES += -DJS_HAS_CTYPES DEFINES += -DDLL_PREFIX=\"$(DLL_PREFIX)\" -DDLL_SUFFIX=\"$(DLL_SUFFIX)\" endif -ifdef JS_NO_THIN_LOCKS -DEFINES += -DJS_USE_ONLY_NSPR_LOCKS -endif - ifdef JS_VERSION DEFINES += -DJS_VERSION=$(JS_VERSION) endif @@ -781,20 +776,6 @@ EXTRA_LIBS += -lposix4 -ldl -lnsl -lsocket endif endif -ifdef SOLARIS_SUNPRO_CXX -ifeq ($(TARGET_CPU),sparc) -# Sun Studio SPARC doesn't work well with gcc inline asm, use lock_SunOS_sparc*.il -jslock.o: jslock.cpp Makefile.in lock_sparcv8plus.il lock_sparcv9.il - $(REPORT_BUILD) - @$(MAKE_DEPS_AUTO_CXX) -ifeq (sparcv9,$(findstring sparcv9,$(OS_TEST))) - $(CXX) -o $@ -c $(COMPILE_CFLAGS) $(srcdir)/lock_sparcv9.il $< -else - $(CXX) -o $@ -c $(COMPILE_CFLAGS) $(srcdir)/lock_sparcv8plus.il $< -endif # sparcv9 -endif # sparc -endif # SOLARIS_SUNPRO_CXX - # An AIX Optimization bug causes PR_dtoa() & JS_dtoa to produce wrong result. # This suppresses optimization for this single compilation unit. ifeq ($(OS_ARCH),AIX) diff --git a/js/src/MemoryMetrics.cpp b/js/src/MemoryMetrics.cpp index 2c787471f6bf..1881ca13072f 100644 --- a/js/src/MemoryMetrics.cpp +++ b/js/src/MemoryMetrics.cpp @@ -219,37 +219,26 @@ CollectCompartmentStatsForRuntime(JSRuntime *rt, IterateData *data) data->runtimeObject = data->mallocSizeOf(rt, sizeof(JSRuntime)); + size_t normal, temporary, regexpCode, stackCommitted; + rt->sizeOfExcludingThis(data->mallocSizeOf, + &normal, + &temporary, + ®expCode, + &stackCommitted); + + data->runtimeNormal = normal; + data->runtimeTemporary = temporary; + data->runtimeRegexpCode = regexpCode; + data->runtimeStackCommitted = stackCommitted; + // Nb: we use sizeOfExcludingThis() because atomState.atoms is within // JSRuntime, and so counted when JSRuntime is counted. data->runtimeAtomsTable = rt->atomState.atoms.sizeOfExcludingThis(data->mallocSizeOf); - { - // Need the GC lock to call JS_ContextIteratorUnlocked() and to - // access rt->threads. - AutoLockGC lock(rt); - - JSContext *acx, *iter = NULL; - while ((acx = JS_ContextIteratorUnlocked(rt, &iter)) != NULL) { - data->runtimeContexts += - acx->sizeOfIncludingThis(data->mallocSizeOf); - } - - for (JSThread::Map::Range r = rt->threads.all(); !r.empty(); r.popFront()) { - JSThread *thread = r.front().value; - size_t normal, temporary, regexpCode, stackCommitted; - thread->sizeOfIncludingThis(data->mallocSizeOf, - &normal, - &temporary, - ®expCode, - &stackCommitted); - - data->runtimeThreadsNormal += normal; - data->runtimeThreadsTemporary += temporary; - data->runtimeThreadsRegexpCode += regexpCode; - data->runtimeThreadsStackCommitted += stackCommitted; - } - } + JSContext *acx, *iter = NULL; + while ((acx = JS_ContextIteratorUnlocked(rt, &iter)) != NULL) + data->runtimeContexts += acx->sizeOfIncludingThis(data->mallocSizeOf); } JS_DestroyContextNoGC(cx); @@ -346,26 +335,17 @@ GetExplicitNonHeapForRuntime(JSRuntime *rt, int64_t *amount, IterateCompartments(cx, &n, ExplicitNonHeapCompartmentCallback); *amount += n; - { - // Need the GC lock to call JS_ContextIteratorUnlocked() and to - // access rt->threads. - AutoLockGC lock(rt); + // explicit/runtime/regexp-code + // explicit/runtime/stack-committed + size_t regexpCode, stackCommitted; + rt->sizeOfExcludingThis(mallocSizeOf, + NULL, + NULL, + ®expCode, + &stackCommitted); - // explicit/runtime/threads/regexp-code - // explicit/runtime/threads/stack-committed - for (JSThread::Map::Range r = rt->threads.all(); !r.empty(); r.popFront()) { - JSThread *thread = r.front().value; - size_t regexpCode, stackCommitted; - thread->sizeOfIncludingThis(mallocSizeOf, - NULL, - NULL, - ®expCode, - &stackCommitted); - - *amount += regexpCode; - *amount += stackCommitted; - } - } + *amount += regexpCode; + *amount += stackCommitted; } JS_DestroyContextNoGC(cx); diff --git a/js/src/ctypes/CTypes.cpp b/js/src/ctypes/CTypes.cpp index 121e993bc92c..7e5295af432b 100644 --- a/js/src/ctypes/CTypes.cpp +++ b/js/src/ctypes/CTypes.cpp @@ -61,19 +61,6 @@ using namespace std; namespace js { namespace ctypes { -/******************************************************************************* -** Helper classes -*******************************************************************************/ - -class ScopedContextThread -{ -public: - ScopedContextThread(JSContext* cx) : mCx(cx) { JS_SetContextThread(cx); } - ~ScopedContextThread() { JS_ClearContextThread(mCx); } -private: - JSContext* mCx; -}; - /******************************************************************************* ** JSAPI function prototypes *******************************************************************************/ @@ -2791,7 +2778,6 @@ CType::FinalizeProtoClass(JSContext* cx, JSObject* obj) return; JSContext* closureCx = static_cast(JSVAL_TO_PRIVATE(slot)); - JS_SetContextThread(closureCx); JS_DestroyContextNoGC(closureCx); } @@ -5383,15 +5369,8 @@ CClosure::Create(JSContext* cx, JS_DestroyContextNoGC(cinfo->cx); return NULL; } - - JS_ClearContextThread(cinfo->cx); } -#ifdef DEBUG - // We want *this* context's thread here so use cx instead of cinfo->cx. - cinfo->cxThread = JS_GetContextThread(cx); -#endif - // Prepare the error sentinel value. It's important to do this now, because // we might be unable to convert the value to the proper type. If so, we want // the caller to know about it _now_, rather than some uncertain time in the @@ -5505,11 +5484,6 @@ CClosure::ClosureStub(ffi_cif* cif, void* result, void** args, void* userData) JSObject* thisObj = cinfo->thisObj; JSObject* jsfnObj = cinfo->jsfnObj; - ScopedContextThread scopedThread(cx); - - // Assert that we're on the thread we were created from. - JS_ASSERT(cinfo->cxThread == JS_GetContextThread(cx)); - JS_AbortIfWrongThread(JS_GetRuntime(cx)); JSAutoRequest ar(cx); diff --git a/js/src/ctypes/CTypes.h b/js/src/ctypes/CTypes.h index a8ad18485424..950b892033ec 100644 --- a/js/src/ctypes/CTypes.h +++ b/js/src/ctypes/CTypes.h @@ -331,9 +331,6 @@ struct ClosureInfo JSObject* jsfnObj; // JS function void* errResult; // Result that will be returned if the closure throws ffi_closure* closure; // The C closure itself -#ifdef DEBUG - intptr_t cxThread; // The thread on which the context may be used -#endif // Anything conditionally freed in the destructor should be initialized to // NULL here. diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index e2496939e262..bea809ec64b9 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -60,6 +60,7 @@ #include "jscntxt.h" #include "jsversion.h" #include "jsdate.h" +#include "jsdtoa.h" #include "jsexn.h" #include "jsfun.h" #include "jsgc.h" @@ -68,6 +69,7 @@ #include "jsiter.h" #include "jslock.h" #include "jsmath.h" +#include "jsnativestack.h" #include "jsnum.h" #include "json.h" #include "jsobj.h" @@ -88,7 +90,8 @@ #include "frontend/BytecodeCompiler.h" #include "frontend/BytecodeEmitter.h" #include "js/MemoryMetrics.h" -#include "mozilla/Util.h" // DebugOnly +#include "mozilla/Util.h" +#include "yarr/BumpPointerAllocator.h" #include "jsatominlines.h" #include "jsinferinlines.h" @@ -692,11 +695,27 @@ JS_IsBuiltinFunctionConstructor(JSFunction *fun) static JSBool js_NewRuntimeWasCalled = JS_FALSE; JSRuntime::JSRuntime() - : atomsCompartment(NULL), + : interrupt(0), + atomsCompartment(NULL), +#ifdef JS_THREADSAFE + ownerThread_(NULL), +#endif + tempLifoAlloc(TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE), + execAlloc_(NULL), + bumpAlloc_(NULL), + repCache_(NULL), + interpreterFrames(NULL), cxCallback(NULL), compartmentCallback(NULL), activityCallback(NULL), activityCallbackArg(NULL), +#ifdef JS_THREADSAFE + suspendCount(0), + requestDepth(0), +# ifdef DEBUG + checkRequestDepth(0), +# endif +#endif gcSystemAvailableChunkListHead(NULL), gcUserAvailableChunkListHead(NULL), gcKeepAtoms(0), @@ -746,10 +765,7 @@ JSRuntime::JSRuntime() data(NULL), #ifdef JS_THREADSAFE gcLock(NULL), - gcDone(NULL), - requestDone(NULL), requestCount(0), - gcThread(NULL), gcHelperThread(thisFromCtor()), #endif debuggerMutations(0), @@ -757,24 +773,21 @@ JSRuntime::JSRuntime() structuredCloneCallbacks(NULL), telemetryCallback(NULL), propertyRemovals(0), - scriptFilenameTable(NULL), -#ifdef JS_THREADSAFE - scriptFilenameTableLock(NULL), -#endif thousandsSeparator(0), decimalSeparator(0), numGrouping(0), anynameObject(NULL), functionNamespaceObject(NULL), -#ifdef JS_THREADSAFE - interruptCounter(0), -#else - threadData(thisFromCtor()), -#endif + waiveGCQuota(false), + dtoaState(NULL), + pendingProxyOperation(NULL), trustedPrincipals_(NULL), - wrapObjectCallback(NULL), + wrapObjectCallback(TransparentObjectWrapper), preWrapObjectCallback(NULL), preserveWrapperCallback(NULL), +#ifdef DEBUG + noGCOrAllocationCheck(0), +#endif inOOMReport(0) { /* Initialize infallibly first, so we can goto bad and JS_DestroyRuntime. */ @@ -789,7 +802,7 @@ bool JSRuntime::init(uint32_t maxbytes) { #ifdef JS_THREADSAFE - ownerThread_ = js_CurrentThreadId(); + ownerThread_ = PR_GetCurrentThread(); #endif #ifdef JS_METHODJIT_SPEW @@ -812,25 +825,28 @@ JSRuntime::init(uint32_t maxbytes) if (!js_InitAtomState(this)) return false; - wrapObjectCallback = js::TransparentObjectWrapper; - -#ifdef JS_THREADSAFE - /* this is asymmetric with JS_ShutDown: */ - if (!js_SetupLocks(8, 16)) - return false; -#endif - - debugMode = false; - if (!js_InitThreads(this)) - return false; if (!InitRuntimeNumberState(this)) return false; + dtoaState = js_NewDtoaState(); + if (!dtoaState) + return false; + + if (!stackSpace.init()) + return false; + + conservativeGC.nativeStackBase = GetNativeStackBase(); return true; } JSRuntime::~JSRuntime() { + JS_ASSERT(onOwnerThread()); + + delete_(execAlloc_); + delete_(bumpAlloc_); + JS_ASSERT(!repCache_); + #ifdef DEBUG /* Don't hurt everyone in leaky ol' Mozilla with a fatal JS_ASSERT! */ if (!JS_CLIST_IS_EMPTY(&contextList)) { @@ -849,17 +865,15 @@ JSRuntime::~JSRuntime() #endif FinishRuntimeNumberState(this); - js_FinishThreads(this); js_FinishAtomState(this); + if (dtoaState) + js_DestroyDtoaState(dtoaState); + js_FinishGC(this); #ifdef JS_THREADSAFE if (gcLock) - JS_DESTROY_LOCK(gcLock); - if (gcDone) - JS_DESTROY_CONDVAR(gcDone); - if (requestDone) - JS_DESTROY_CONDVAR(requestDone); + PR_DestroyLock(gcLock); #endif } @@ -867,23 +881,27 @@ JSRuntime::~JSRuntime() void JSRuntime::setOwnerThread() { - JS_ASSERT(ownerThread_ == (void *)-1); - ownerThread_ = js_CurrentThreadId(); + JS_ASSERT(ownerThread_ == (void *)0xc1ea12); /* "clear" */ + JS_ASSERT(requestDepth == 0); + ownerThread_ = PR_GetCurrentThread(); + conservativeGC.nativeStackBase = GetNativeStackBase(); } void JSRuntime::clearOwnerThread() { JS_ASSERT(onOwnerThread()); - ownerThread_ = (void *)-1; + JS_ASSERT(requestDepth == 0); + ownerThread_ = (void *)0xc1ea12; /* "clear" */ + conservativeGC.nativeStackBase = 0; } JS_FRIEND_API(bool) JSRuntime::onOwnerThread() const { - return ownerThread_ == js_CurrentThreadId(); + return ownerThread_ == PR_GetCurrentThread(); } -#endif +#endif /* JS_THREADSAFE */ JS_PUBLIC_API(JSRuntime *) JS_NewRuntime(uint32_t maxbytes) @@ -943,10 +961,6 @@ JS_PUBLIC_API(void) JS_ShutDown(void) { Probes::shutdown(); - -#ifdef JS_THREADSAFE - js_CleanupLocks(); -#endif PRMJ_NowShutdown(); } @@ -990,31 +1004,17 @@ JS::UserCompartmentCount(const JSRuntime *rt) static void StartRequest(JSContext *cx) { - JSThread *t = cx->thread(); - JS_ASSERT(CURRENT_THREAD_IS_ME(t)); + JSRuntime *rt = cx->runtime; + JS_ASSERT(rt->onOwnerThread()); - if (t->data.requestDepth) { - t->data.requestDepth++; + if (rt->requestDepth) { + rt->requestDepth++; } else { - JSRuntime *rt = cx->runtime; AutoLockGC lock(rt); - /* Wait until the GC is finished. */ - if (rt->gcThread != cx->thread()) { - while (rt->gcThread) - JS_AWAIT_GC_DONE(rt); - } - /* Indicate that a request is running. */ rt->requestCount++; - t->data.requestDepth = 1; - - /* - * Adjust rt->interruptCounter to reflect any interrupts added while the - * thread was suspended. - */ - if (t->data.interruptFlags) - JS_ATOMIC_INCREMENT(&rt->interruptCounter); + rt->requestDepth = 1; if (rt->requestCount == 1 && rt->activityCallback) rt->activityCallback(rt->activityCallbackArg, true); @@ -1024,32 +1024,23 @@ StartRequest(JSContext *cx) static void StopRequest(JSContext *cx) { - JSThread *t = cx->thread(); - JS_ASSERT(CURRENT_THREAD_IS_ME(t)); - JS_ASSERT(t->data.requestDepth != 0); - if (t->data.requestDepth != 1) { - t->data.requestDepth--; + JSRuntime *rt = cx->runtime; + JS_ASSERT(rt->onOwnerThread()); + JS_ASSERT(rt->requestDepth != 0); + if (rt->requestDepth != 1) { + rt->requestDepth--; } else { - t->data.conservativeGC.updateForRequestEnd(t->suspendCount); + rt->conservativeGC.updateForRequestEnd(rt->suspendCount); /* Lock before clearing to interlock with ClaimScope, in jslock.c. */ - JSRuntime *rt = cx->runtime; AutoLockGC lock(rt); - t->data.requestDepth = 0; - - /* - * Adjust rt->interruptCounter to reflect any interrupts added while the - * thread still had active requests. - */ - if (t->data.interruptFlags) - JS_ATOMIC_DECREMENT(&rt->interruptCounter); + rt->requestDepth = 0; /* Give the GC a chance to run if this was the last request running. */ JS_ASSERT(rt->requestCount > 0); rt->requestCount--; if (rt->requestCount == 0) { - JS_NOTIFY_REQUEST_DONE(rt); if (rt->activityCallback) rt->activityCallback(rt->activityCallbackArg, false); } @@ -1090,15 +1081,15 @@ JS_PUBLIC_API(jsrefcount) JS_SuspendRequest(JSContext *cx) { #ifdef JS_THREADSAFE - JSThread *t = cx->thread(); - JS_ASSERT(CURRENT_THREAD_IS_ME(t)); + JSRuntime *rt = cx->runtime; + JS_ASSERT(rt->onOwnerThread()); - jsrefcount saveDepth = t->data.requestDepth; + jsrefcount saveDepth = rt->requestDepth; if (!saveDepth) return 0; - t->suspendCount++; - t->data.requestDepth = 1; + rt->suspendCount++; + rt->requestDepth = 1; StopRequest(cx); return saveDepth; #else @@ -1110,25 +1101,36 @@ JS_PUBLIC_API(void) JS_ResumeRequest(JSContext *cx, jsrefcount saveDepth) { #ifdef JS_THREADSAFE - JSThread *t = cx->thread(); - JS_ASSERT(CURRENT_THREAD_IS_ME(t)); + JSRuntime *rt = cx->runtime; + JS_ASSERT(rt->onOwnerThread()); if (saveDepth == 0) return; JS_ASSERT(saveDepth >= 1); - JS_ASSERT(!t->data.requestDepth); - JS_ASSERT(t->suspendCount); + JS_ASSERT(!rt->requestDepth); + JS_ASSERT(rt->suspendCount); StartRequest(cx); - t->data.requestDepth = saveDepth; - t->suspendCount--; + rt->requestDepth = saveDepth; + rt->suspendCount--; #endif } JS_PUBLIC_API(JSBool) -JS_IsInRequest(JSContext *cx) +JS_IsInRequest(JSRuntime *rt) { #ifdef JS_THREADSAFE - JS_ASSERT(CURRENT_THREAD_IS_ME(cx->thread())); - return JS_THREAD_DATA(cx)->requestDepth != 0; + JS_ASSERT(rt->onOwnerThread()); + return rt->requestDepth != 0; +#else + return false; +#endif +} + +JS_PUBLIC_API(JSBool) +JS_IsInSuspendedRequest(JSRuntime *rt) +{ +#ifdef JS_THREADSAFE + JS_ASSERT(rt->onOwnerThread()); + return rt->suspendCount != 0; #else return false; #endif @@ -3019,15 +3021,11 @@ JS_SetThreadStackLimit(JSContext *cx, uintptr_t limitAddr) JS_PUBLIC_API(void) JS_SetNativeStackQuota(JSContext *cx, size_t stackSize) { -#ifdef JS_THREADSAFE - JS_ASSERT(cx->thread()); -#endif - #if JS_STACK_GROWTH_DIRECTION > 0 if (stackSize == 0) { cx->stackLimit = UINTPTR_MAX; } else { - uintptr_t stackBase = reinterpret_cast(JS_THREAD_DATA(cx)->nativeStackBase); + uintptr_t stackBase = cx->runtime->nativeStackBase; JS_ASSERT(stackBase <= size_t(-1) - stackSize); cx->stackLimit = stackBase + stackSize - 1; } @@ -3035,7 +3033,7 @@ JS_SetNativeStackQuota(JSContext *cx, size_t stackSize) if (stackSize == 0) { cx->stackLimit = 0; } else { - uintptr_t stackBase = reinterpret_cast(JS_THREAD_DATA(cx)->nativeStackBase); + uintptr_t stackBase = uintptr_t(cx->runtime->conservativeGC.nativeStackBase); JS_ASSERT(stackBase >= stackSize); cx->stackLimit = stackBase - (stackSize - 1); } @@ -5518,9 +5516,6 @@ JS_New(JSContext *cx, JSObject *ctor, uintN argc, jsval *argv) JS_PUBLIC_API(JSOperationCallback) JS_SetOperationCallback(JSContext *cx, JSOperationCallback callback) { -#ifdef JS_THREADSAFE - JS_ASSERT(CURRENT_THREAD_IS_ME(cx->thread())); -#endif JSOperationCallback old = cx->operationCallback; cx->operationCallback = callback; return old; @@ -5538,26 +5533,21 @@ JS_TriggerOperationCallback(JSContext *cx) #ifdef JS_THREADSAFE AutoLockGC lock(cx->runtime); #endif - TriggerOperationCallback(cx); + cx->runtime->triggerOperationCallback(); } JS_PUBLIC_API(void) -JS_TriggerAllOperationCallbacks(JSRuntime *rt) +JS_TriggerRuntimeOperationCallback(JSRuntime *rt) { #ifdef JS_THREADSAFE AutoLockGC lock(rt); #endif - TriggerAllOperationCallbacks(rt); + rt->triggerOperationCallback(); } JS_PUBLIC_API(JSBool) JS_IsRunning(JSContext *cx) { -#ifdef JS_THREADSAFE - if (!cx->thread()) - return false; -#endif - StackFrame *fp = cx->maybefp(); while (fp && fp->isDummyFrame()) fp = fp->prev(); @@ -6589,51 +6579,14 @@ JS_ThrowStopIteration(JSContext *cx) JS_PUBLIC_API(intptr_t) JS_GetCurrentThread() -{ - return reinterpret_cast(js_CurrentThreadId()); -} - -/* - * Get the owning thread id of a context. Returns 0 if the context is not - * owned by any thread. - */ -JS_PUBLIC_API(intptr_t) -JS_GetContextThread(JSContext *cx) { #ifdef JS_THREADSAFE - return cx->thread() ? reinterpret_cast(cx->thread()->id) : 0; + return reinterpret_cast(PR_GetCurrentThread()); #else return 0; #endif } -/* - * 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(intptr_t) -JS_SetContextThread(JSContext *cx) -{ - /* This function can be called by a finalizer. */ - JS_AbortIfWrongThread(cx->runtime); - -#ifdef JS_THREADSAFE - JS_ASSERT(!cx->outstandingRequests); - if (cx->thread()) { - JS_ASSERT(CURRENT_THREAD_IS_ME(cx->thread())); - return reinterpret_cast(cx->thread()->id); - } - - if (!js_InitContextThreadAndLockGC(cx)) { - js_ReportOutOfMemory(cx); - return -1; - } - - JS_UNLOCK_GC(cx->runtime); -#endif - return 0; -} - extern JS_PUBLIC_API(void) JS_ClearRuntimeThread(JSRuntime *rt) { @@ -6661,44 +6614,6 @@ JS_AbortIfWrongThread(JSRuntime *rt) #endif } -JS_PUBLIC_API(intptr_t) -JS_ClearContextThread(JSContext *cx) -{ - JS_AbortIfWrongThread(cx->runtime); - AssertNoGC(cx); - -#ifdef JS_THREADSAFE - /* - * cx must have exited all requests it entered and, if cx is associated - * with a thread, this must be called only from that thread. If not, this - * is a harmless no-op. - */ - JS_ASSERT(cx->outstandingRequests == 0); - JSThread *t = cx->thread(); - if (!t) - return 0; - JS_ASSERT(CURRENT_THREAD_IS_ME(t)); - - /* - * We must not race with a GC that accesses cx->thread for all threads, - * see bug 476934. - */ - JSRuntime *rt = cx->runtime; - AutoLockGC lock(rt); - js_WaitForGC(rt); - js_ClearContextThread(cx); - JS_ASSERT_IF(JS_CLIST_IS_EMPTY(&t->contextList), !t->data.requestDepth); - - /* - * We can access t->id as long as the GC lock is held and we cannot race - * with the GC that may delete t. - */ - return reinterpret_cast(t->id); -#else - return 0; -#endif -} - #ifdef JS_GC_ZEAL JS_PUBLIC_API(void) JS_SetGCZeal(JSContext *cx, uint8_t zeal, uint32_t frequency, JSBool compartment) diff --git a/js/src/jsapi.h b/js/src/jsapi.h index c563528cdbff..b01be90df713 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -2323,6 +2323,9 @@ JS_ShutDown(void); JS_PUBLIC_API(void *) JS_GetRuntimePrivate(JSRuntime *rt); +extern JS_PUBLIC_API(JSRuntime *) +JS_GetRuntime(JSContext *cx); + JS_PUBLIC_API(void) JS_SetRuntimePrivate(JSRuntime *rt, void *data); @@ -2343,7 +2346,10 @@ extern JS_PUBLIC_API(void) JS_ResumeRequest(JSContext *cx, jsrefcount saveDepth); extern JS_PUBLIC_API(JSBool) -JS_IsInRequest(JSContext *cx); +JS_IsInRequest(JSRuntime *rt); + +extern JS_PUBLIC_API(JSBool) +JS_IsInSuspendedRequest(JSRuntime *rt); #ifdef __cplusplus JS_END_EXTERN_C @@ -2425,14 +2431,14 @@ class JSAutoCheckRequest { JSAutoCheckRequest(JSContext *cx JS_GUARD_OBJECT_NOTIFIER_PARAM) { #if defined JS_THREADSAFE && defined DEBUG mContext = cx; - JS_ASSERT(JS_IsInRequest(cx)); + JS_ASSERT(JS_IsInRequest(JS_GetRuntime(cx))); #endif JS_GUARD_OBJECT_NOTIFIER_INIT; } ~JSAutoCheckRequest() { #if defined JS_THREADSAFE && defined DEBUG - JS_ASSERT(JS_IsInRequest(mContext)); + JS_ASSERT(JS_IsInRequest(JS_GetRuntime(mContext))); #endif } @@ -4489,7 +4495,7 @@ extern JS_PUBLIC_API(void) JS_TriggerOperationCallback(JSContext *cx); extern JS_PUBLIC_API(void) -JS_TriggerAllOperationCallbacks(JSRuntime *rt); +JS_TriggerRuntimeOperationCallback(JSRuntime *rt); extern JS_PUBLIC_API(JSBool) JS_IsRunning(JSContext *cx); @@ -5261,24 +5267,6 @@ JS_ThrowStopIteration(JSContext *cx); extern JS_PUBLIC_API(intptr_t) JS_GetCurrentThread(); -/* - * Associate the current thread with the given context. This is done - * implicitly by JS_NewContext. - * - * Returns the old thread id for this context, which should be treated as - * an opaque value. This value is provided for comparison to 0, which - * indicates that ClearContextThread has been called on this context - * since the last SetContextThread, or non-0, which indicates the opposite. - */ -extern JS_PUBLIC_API(intptr_t) -JS_GetContextThread(JSContext *cx); - -extern JS_PUBLIC_API(intptr_t) -JS_SetContextThread(JSContext *cx); - -extern JS_PUBLIC_API(intptr_t) -JS_ClearContextThread(JSContext *cx); - /* * A JS runtime always has an "owner thread". The owner thread is set when the * runtime is created (to the current thread) and practically all entry points diff --git a/js/src/jsatom.cpp b/js/src/jsatom.cpp index eea0d2020427..5cc64d3cb06c 100644 --- a/js/src/jsatom.cpp +++ b/js/src/jsatom.cpp @@ -333,11 +333,8 @@ js_InitAtomState(JSRuntime *rt) if (!state->atoms.init(JS_STRING_HASH_COUNT)) return false; -#ifdef JS_THREADSAFE - js_InitLock(&state->lock); -#endif JS_ASSERT(state->atoms.initialized()); - return JS_TRUE; + return true; } void @@ -355,10 +352,6 @@ js_FinishAtomState(JSRuntime *rt) for (AtomSet::Range r = state->atoms.all(); !r.empty(); r.popFront()) r.front().asPtr()->finalize(rt); - -#ifdef JS_THREADSAFE - js_FinishLock(&state->lock); -#endif } bool @@ -433,7 +426,6 @@ AtomIsInterned(JSContext *cx, JSAtom *atom) if (StaticStrings::isStatic(atom)) return true; - AutoLockAtomsCompartment lock(cx); AtomSet::Ptr p = cx->runtime->atomState.atoms.lookup(atom); if (!p) return false; @@ -462,8 +454,6 @@ AtomizeInline(JSContext *cx, const jschar **pchars, size_t length, if (JSAtom *s = cx->runtime->staticStrings.lookup(chars, length)) return s; - AutoLockAtomsCompartment lock(cx); - AtomSet &atoms = cx->runtime->atomState.atoms; AtomSet::AddPtr p = atoms.lookupForAdd(AtomHasher::Lookup(chars, length)); @@ -522,9 +512,6 @@ js_AtomizeString(JSContext *cx, JSString *str, InternBehavior ib) if (ib != InternAtom || js::StaticStrings::isStatic(&atom)) return &atom; - /* Here we have to check whether the atom is already interned. */ - AutoLockAtomsCompartment lock(cx); - AtomSet &atoms = cx->runtime->atomState.atoms; AtomSet::Ptr p = atoms.lookup(AtomHasher::Lookup(&atom)); JS_ASSERT(p); /* Non-static atom must exist in atom state set. */ @@ -604,9 +591,9 @@ js_GetExistingStringAtom(JSContext *cx, const jschar *chars, size_t length) { if (JSAtom *atom = cx->runtime->staticStrings.lookup(chars, length)) return atom; - AutoLockAtomsCompartment lock(cx); - AtomSet::Ptr p = cx->runtime->atomState.atoms.lookup(AtomHasher::Lookup(chars, length)); - return p ? p->asPtr() : NULL; + if (AtomSet::Ptr p = cx->runtime->atomState.atoms.lookup(AtomHasher::Lookup(chars, length))) + return p->asPtr(); + return NULL; } #ifdef DEBUG diff --git a/js/src/jsatom.h b/js/src/jsatom.h index fa9983cc0dac..1a4bea0ce902 100644 --- a/js/src/jsatom.h +++ b/js/src/jsatom.h @@ -268,10 +268,6 @@ struct JSAtomState { js::AtomSet atoms; -#ifdef JS_THREADSAFE - JSThinLock lock; -#endif - /* * From this point until the end of struct definition the struct must * contain only js::PropertyName fields. We use this to access the storage diff --git a/js/src/jscntxt.cpp b/js/src/jscntxt.cpp index ce4bf46f0e1c..816a426e7793 100644 --- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -64,7 +64,6 @@ #include "jscntxt.h" #include "jsversion.h" #include "jsdbgapi.h" -#include "jsdtoa.h" #include "jsexn.h" #include "jsfun.h" #include "jsgc.h" @@ -72,7 +71,6 @@ #include "jsiter.h" #include "jslock.h" #include "jsmath.h" -#include "jsnativestack.h" #include "jsnum.h" #include "jsobj.h" #include "jsopcode.h" @@ -96,57 +94,10 @@ using namespace js; using namespace js::gc; -namespace js { - -ThreadData::ThreadData(JSRuntime *rt) - : rt(rt), - interruptFlags(0), -#ifdef JS_THREADSAFE - requestDepth(0), -#endif - tempLifoAlloc(TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE), - execAlloc(NULL), - bumpAlloc(NULL), - repCache(NULL), - dtoaState(NULL), - nativeStackBase(GetNativeStackBase()), - pendingProxyOperation(NULL), - interpreterFrames(NULL) -{ -#ifdef DEBUG - noGCOrAllocationCheck = 0; -#endif -} - -ThreadData::~ThreadData() -{ - JS_ASSERT(!repCache); - - rt->delete_(execAlloc); - rt->delete_(bumpAlloc); - - if (dtoaState) - js_DestroyDtoaState(dtoaState); -} - -bool -ThreadData::init() -{ - JS_ASSERT(!repCache); - return stackSpace.init() && !!(dtoaState = js_NewDtoaState()); -} - -#ifdef JS_THREADSAFE void -ThreadData::sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf, size_t *normal, size_t *temporary, - size_t *regexpCode, size_t *stackCommitted) +JSRuntime::sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf, size_t *normal, size_t *temporary, + size_t *regexpCode, size_t *stackCommitted) { - /* - * There are other ThreadData members that could be measured; the ones - * below have been seen by DMD to be worth measuring. More stuff may be - * added later. - */ - /* * The computedSize is 0 because sizeof(DtoaState) isn't available here and * it's not worth making it available. @@ -159,8 +110,8 @@ ThreadData::sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf, size_t *normal, if (regexpCode) { size_t method = 0, regexp = 0, unused = 0; - if (execAlloc) - execAlloc->sizeOfCode(&method, ®exp, &unused); + if (execAlloc_) + execAlloc_->sizeOfCode(&method, ®exp, &unused); JS_ASSERT(method == 0); /* this execAlloc is only used for regexp code */ *regexpCode = regexp + unused; } @@ -168,261 +119,65 @@ ThreadData::sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf, size_t *normal, if (stackCommitted) *stackCommitted = stackSpace.sizeOfCommitted(); } -#endif -void -ThreadData::triggerOperationCallback(JSRuntime *rt) +JS_FRIEND_API(void) +JSRuntime::triggerOperationCallback() { - JS_ASSERT(rt == this->rt); - /* - * Use JS_ATOMIC_SET and JS_ATOMIC_INCREMENT in the hope that it ensures - * the write will become immediately visible to other processors polling - * the flag. Note that we only care about visibility here, not read/write - * ordering: this field can only be written with the GC lock held. + * Use JS_ATOMIC_SET in the hope that it ensures the write will become + * immediately visible to other processors polling the flag. */ - if (interruptFlags) - return; - JS_ATOMIC_SET(&interruptFlags, 1); - -#ifdef JS_THREADSAFE - /* rt->interruptCounter does not reflect suspended threads. */ - if (requestDepth != 0) - JS_ATOMIC_INCREMENT(&rt->interruptCounter); -#endif + JS_ATOMIC_SET(&interrupt, 1); } JSC::ExecutableAllocator * -ThreadData::createExecutableAllocator(JSContext *cx) +JSRuntime::createExecutableAllocator(JSContext *cx) { - JS_ASSERT(!execAlloc); - JS_ASSERT(cx->runtime == rt); + JS_ASSERT(!execAlloc_); + JS_ASSERT(cx->runtime == this); - execAlloc = rt->new_(); - if (!execAlloc) + execAlloc_ = new_(); + if (!execAlloc_) js_ReportOutOfMemory(cx); - return execAlloc; + return execAlloc_; } WTF::BumpPointerAllocator * -ThreadData::createBumpPointerAllocator(JSContext *cx) +JSRuntime::createBumpPointerAllocator(JSContext *cx) { - JS_ASSERT(!bumpAlloc); - JS_ASSERT(cx->runtime == rt); + JS_ASSERT(!bumpAlloc_); + JS_ASSERT(cx->runtime == this); - bumpAlloc = rt->new_(); - if (!bumpAlloc) + bumpAlloc_ = new_(); + if (!bumpAlloc_) js_ReportOutOfMemory(cx); - return bumpAlloc; + return bumpAlloc_; } RegExpPrivateCache * -ThreadData::createRegExpPrivateCache(JSContext *cx) +JSRuntime::createRegExpPrivateCache(JSContext *cx) { - JS_ASSERT(!repCache); - JS_ASSERT(cx->runtime == rt); + JS_ASSERT(!repCache_); + JS_ASSERT(cx->runtime == this); - RegExpPrivateCache *newCache = rt->new_(rt); + RegExpPrivateCache *newCache = new_(this); if (!newCache || !newCache->init()) { js_ReportOutOfMemory(cx); - rt->delete_(newCache); + delete_(newCache); return NULL; } - repCache = newCache; - return repCache; + repCache_ = newCache; + return repCache_; } -void -ThreadData::purgeRegExpPrivateCache() -{ - rt->delete_(repCache); - repCache = NULL; -} - -} /* namespace js */ - JSScript * js_GetCurrentScript(JSContext *cx) { return cx->hasfp() ? cx->fp()->maybeScript() : NULL; } - -#ifdef JS_THREADSAFE - -void -JSThread::sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf, size_t *normal, size_t *temporary, - size_t *regexpCode, size_t *stackCommitted) -{ - data.sizeOfExcludingThis(mallocSizeOf, normal, temporary, regexpCode, stackCommitted); - if (normal) - *normal += mallocSizeOf(this, sizeof(JSThread)); -} - -JSThread * -js_CurrentThreadAndLockGC(JSRuntime *rt) -{ - void *id = js_CurrentThreadId(); - JS_LOCK_GC(rt); - - /* - * We must not race with a GC that accesses cx->thread for JSContext - * instances on all threads, see bug 476934. - */ - js_WaitForGC(rt); - - JSThread *thread; - JSThread::Map::AddPtr p = rt->threads.lookupForAdd(id); - if (p) { - thread = p->value; - - /* - * If thread has no contexts, it might be left over from a previous - * thread with the same id but a different stack address. - */ - if (JS_CLIST_IS_EMPTY(&thread->contextList)) - thread->data.nativeStackBase = GetNativeStackBase(); - } else { - JS_UNLOCK_GC(rt); - - thread = OffTheBooks::new_(rt, id); - if (!thread || !thread->init()) { - Foreground::delete_(thread); - return NULL; - } - JS_LOCK_GC(rt); - js_WaitForGC(rt); - if (!rt->threads.relookupOrAdd(p, id, thread)) { - JS_UNLOCK_GC(rt); - Foreground::delete_(thread); - return NULL; - } - - /* Another thread cannot add an entry for the current thread id. */ - JS_ASSERT(p->value == thread); - } - JS_ASSERT(thread->id == id); - - /* - * We skip the assert under glibc due to an apparent bug there, see - * bug 608526. - */ -#ifndef __GLIBC__ - JS_ASSERT(GetNativeStackBase() == thread->data.nativeStackBase); -#endif - - return thread; -} - -JSBool -js_InitContextThreadAndLockGC(JSContext *cx) -{ - JSThread *thread = js_CurrentThreadAndLockGC(cx->runtime); - if (!thread) - return false; - - JS_APPEND_LINK(&cx->threadLinks, &thread->contextList); - cx->setThread(thread); - return true; -} - -void -JSContext::setThread(JSThread *thread) -{ - thread_ = thread; - stack.threadReset(); -} - -void -js_ClearContextThread(JSContext *cx) -{ - JS_ASSERT(CURRENT_THREAD_IS_ME(cx->thread())); - JS_REMOVE_AND_INIT_LINK(&cx->threadLinks); - cx->setThread(NULL); -} - -#endif /* JS_THREADSAFE */ - -ThreadData * -js_CurrentThreadData(JSRuntime *rt) -{ -#ifdef JS_THREADSAFE - JSThread *thread = js_CurrentThreadAndLockGC(rt); - if (!thread) - return NULL; - - return &thread->data; -#else - return &rt->threadData; -#endif -} - -JSBool -js_InitThreads(JSRuntime *rt) -{ -#ifdef JS_THREADSAFE - return rt->threads.init(4); -#else - return rt->threadData.init(); -#endif -} - -void -js_FinishThreads(JSRuntime *rt) -{ -#ifdef JS_THREADSAFE - if (!rt->threads.initialized()) - return; - for (JSThread::Map::Range r = rt->threads.all(); !r.empty(); r.popFront()) { - JSThread *thread = r.front().value; - Foreground::delete_(thread); - } - rt->threads.clear(); -#endif -} - -void -js_PurgeThreads(JSContext *cx) -{ -#ifdef JS_THREADSAFE - 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); - Foreground::delete_(thread); - e.removeFront(); - } else { - thread->data.purge(cx); - } - } -#else - cx->runtime->threadData.purge(cx); -#endif -} - -void -js_PurgeThreads_PostGlobalSweep(JSContext *cx) -{ -#ifdef JS_THREADSAFE - for (JSThread::Map::Enum e(cx->runtime->threads); - !e.empty(); - e.popFront()) - { - JSThread *thread = e.front().value; - - JS_ASSERT(!JS_CLIST_IS_EMPTY(&thread->contextList)); - thread->data.purgeRegExpPrivateCache(); - } -#else - cx->runtime->threadData.purgeRegExpPrivateCache(); -#endif -} - JSContext * js_NewContext(JSRuntime *rt, size_t stackChunkSize) { @@ -444,20 +199,12 @@ js_NewContext(JSRuntime *rt, size_t stackChunkSize) return NULL; } -#ifdef JS_THREADSAFE - if (!js_InitContextThreadAndLockGC(cx)) { - Foreground::delete_(cx); - return NULL; - } -#endif - /* * Here the GC lock is still held after js_InitContextThreadAndLockGC took it and * the GC is not running on another thread. */ bool first = JS_CLIST_IS_EMPTY(&rt->contextList); JS_APPEND_LINK(&cx->link, &rt->contextList); - JS_UNLOCK_GC(rt); js_InitRandom(cx); @@ -501,32 +248,20 @@ js_DestroyContext(JSContext *cx, JSDestroyContextMode mode) JSRuntime *rt = cx->runtime; JS_AbortIfWrongThread(rt); - JSContextCallback cxCallback; - JS_ASSERT(!cx->enumerators); #ifdef JS_THREADSAFE - /* - * For API compatibility we allow to destroy contexts without a thread in - * optimized builds. We assume that the embedding knows that an OOM error - * cannot happen in JS_SetContextThread. - */ - JS_ASSERT(cx->thread() && CURRENT_THREAD_IS_ME(cx->thread())); - if (!cx->thread()) - JS_SetContextThread(cx); - /* * For API compatibility we support destroying contexts with non-zero * cx->outstandingRequests but we assume that all JS_BeginRequest calls * on this cx contributes to cx->thread->data.requestDepth and there is no * JS_SuspendRequest calls that set aside the counter. */ - JS_ASSERT(cx->outstandingRequests <= cx->thread()->data.requestDepth); + JS_ASSERT(cx->outstandingRequests <= cx->runtime->requestDepth); #endif if (mode != JSDCM_NEW_FAILED) { - cxCallback = rt->cxCallback; - if (cxCallback) { + if (JSContextCallback cxCallback = rt->cxCallback) { /* * JSCONTEXT_DESTROY callback is not allowed to fail and must * return true. @@ -537,14 +272,6 @@ js_DestroyContext(JSContext *cx, JSDestroyContextMode mode) } JS_LOCK_GC(rt); -#ifdef JS_THREADSAFE - /* - * Typically we are called outside a request, so ensure that the GC is not - * running before removing the context from rt->contextList, see bug 477021. - */ - if (cx->thread()->data.requestDepth == 0) - js_WaitForGC(rt); -#endif JS_REMOVE_LINK(&cx->link); bool last = !rt->hasContexts(); if (last || mode == JSDCM_FORCE_GC || mode == JSDCM_MAYBE_GC @@ -566,7 +293,7 @@ js_DestroyContext(JSContext *cx, JSDestroyContextMode mode) * that we wait for any racing GC started on a not-last context to * finish, before we plow ahead and unpin atoms. */ - if (cx->thread()->data.requestDepth == 0) + if (cx->runtime->requestDepth == 0) JS_BeginRequest(cx); #endif @@ -607,16 +334,8 @@ js_DestroyContext(JSContext *cx, JSDestroyContextMode mode) JS_MaybeGC(cx); JS_LOCK_GC(rt); - js_WaitForGC(rt); } } -#ifdef JS_THREADSAFE -#ifdef DEBUG - JSThread *t = cx->thread(); -#endif - js_ClearContextThread(cx); - JS_ASSERT_IF(JS_CLIST_IS_EMPTY(&t->contextList), !t->data.requestDepth); -#endif #ifdef JS_THREADSAFE rt->gcHelperThread.waitBackgroundSweepEnd(); #endif @@ -645,7 +364,7 @@ js_NextActiveContext(JSRuntime *rt, JSContext *cx) JSContext *iter = cx; #ifdef JS_THREADSAFE while ((cx = js_ContextIterator(rt, JS_FALSE, &iter)) != NULL) { - if (cx->outstandingRequests && cx->thread()->data.requestDepth) + if (cx->outstandingRequests && cx->runtime->requestDepth) break; } return cx; @@ -738,9 +457,6 @@ js_ReportOutOfMemory(JSContext *cx) { cx->runtime->hadOutOfMemory = true; - /* AtomizeInline can call this indirectly when it creates the string. */ - AutoUnlockAtomsCompartmentWhenLocked unlockAtomsCompartment(cx); - JSErrorReport report; JSErrorReporter onError = cx->errorReporter; @@ -1206,23 +922,17 @@ js_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber) JSBool js_InvokeOperationCallback(JSContext *cx) { - JSRuntime *rt = cx->runtime; - ThreadData *td = JS_THREAD_DATA(cx); - JS_ASSERT_REQUEST_DEPTH(cx); - JS_ASSERT(td->interruptFlags != 0); + + JSRuntime *rt = cx->runtime; + JS_ASSERT(rt->interrupt != 0); /* * Reset the callback counter first, then run GC and yield. If another * thread is racing us here we will accumulate another callback request * which will be serviced at the next opportunity. */ - JS_LOCK_GC(rt); - td->interruptFlags = 0; -#ifdef JS_THREADSAFE - JS_ATOMIC_DECREMENT(&rt->interruptCounter); -#endif - JS_UNLOCK_GC(rt); + JS_ATOMIC_SET(&rt->interrupt, 0); if (rt->gcIsNeeded) js_GC(cx, rt->gcTriggerCompartment, GC_NORMAL, rt->gcTriggerReason); @@ -1256,43 +966,11 @@ JSBool js_HandleExecutionInterrupt(JSContext *cx) { JSBool result = JS_TRUE; - if (JS_THREAD_DATA(cx)->interruptFlags) + if (cx->runtime->interrupt) result = js_InvokeOperationCallback(cx) && result; return result; } -namespace js { - -void -TriggerOperationCallback(JSContext *cx) -{ - /* - * We allow for cx to come from another thread. Thus we must deal with - * possible JS_ClearContextThread calls when accessing cx->thread. But we - * assume that the calling thread is in a request so JSThread cannot be - * GC-ed. - */ - ThreadData *td; -#ifdef JS_THREADSAFE - JSThread *thread = cx->thread(); - if (!thread) - return; - td = &thread->data; -#else - td = JS_THREAD_DATA(cx); -#endif - td->triggerOperationCallback(cx->runtime); -} - -void -TriggerAllOperationCallbacks(JSRuntime *rt) -{ - for (ThreadDataIter i(rt); !i.empty(); i.popFront()) - i.threadData()->triggerOperationCallback(rt); -} - -} /* namespace js */ - StackFrame * js_GetScriptedCaller(JSContext *cx, StackFrame *fp) { @@ -1354,9 +1032,6 @@ JSContext::JSContext(JSRuntime *rt) #endif runtime(rt), compartment(NULL), -#ifdef JS_THREADSAFE - thread_(NULL), -#endif stack(thisDuringConstruction()), /* depends on cx->thread_ */ parseMapPool_(NULL), globalObject(NULL), @@ -1367,7 +1042,6 @@ JSContext::JSContext(JSRuntime *rt) data(NULL), data2(NULL), #ifdef JS_THREADSAFE - atomsCompartmentIsLocked(false), outstandingRequests(0), #endif autoGCRooters(NULL), @@ -1407,10 +1081,6 @@ JSContext::JSContext(JSRuntime *rt) JSContext::~JSContext() { -#ifdef JS_THREADSAFE - JS_ASSERT(!thread_); -#endif - /* Free the stuff hanging off of cx. */ if (parseMapPool_) Foreground::delete_(parseMapPool_); @@ -1509,15 +1179,6 @@ JSContext::runningWithTrustedPrincipals() const JS_FRIEND_API(void) JSRuntime::onTooMuchMalloc() { -#ifdef JS_THREADSAFE - AutoLockGC lock(this); - - /* - * We can be called outside a request and can race against a GC that - * mutates the JSThread set during the sweeping phase. - */ - js_WaitForGC(this); -#endif TriggerGC(this, gcstats::TOOMUCHMALLOC); } @@ -1548,6 +1209,19 @@ JSRuntime::onOutOfMemory(void *p, size_t nbytes, JSContext *cx) return NULL; } +void +JSRuntime::purge(JSContext *cx) +{ + tempLifoAlloc.freeUnused(); + gsnCache.purge(); + + /* FIXME: bug 506341 */ + propertyCache.purge(cx); + + delete_(repCache_); + repCache_ = NULL; +} + void JSContext::purge() { @@ -1657,16 +1331,15 @@ namespace JS { AutoCheckRequestDepth::AutoCheckRequestDepth(JSContext *cx) : cx(cx) { - JS_ASSERT(cx->thread()); - JS_ASSERT(cx->thread()->data.requestDepth || cx->thread() == cx->runtime->gcThread); + JS_ASSERT(cx->runtime->requestDepth || cx->runtime->gcRunning); JS_ASSERT(cx->runtime->onOwnerThread()); - cx->thread()->checkRequestDepth++; + cx->runtime->checkRequestDepth++; } AutoCheckRequestDepth::~AutoCheckRequestDepth() { - JS_ASSERT(cx->thread()->checkRequestDepth != 0); - cx->thread()->checkRequestDepth--; + JS_ASSERT(cx->runtime->checkRequestDepth != 0); + cx->runtime->checkRequestDepth--; } #endif diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 3917892433cb..73896ec5dd67 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -123,221 +123,71 @@ struct PendingProxyOperation { JSObject *object; }; -struct ThreadData { - JSRuntime *rt; - - /* - * If non-zero, we were been asked to call the operation callback as soon - * as possible. If the thread has an active request, this contributes - * towards rt->interruptCounter. - */ - volatile int32_t interruptFlags; - -#ifdef JS_THREADSAFE - /* The request depth for this thread. */ - unsigned requestDepth; -#endif - - /* Keeper of the contiguous stack used by all contexts in this thread. */ - StackSpace stackSpace; - - /* Temporary arena pool used while compiling and decompiling. */ - static const size_t TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 1 << 12; - LifoAlloc tempLifoAlloc; - - private: - /* - * Both of these allocators are used for regular expression code which is shared at the - * thread-data level. - */ - JSC::ExecutableAllocator *execAlloc; - WTF::BumpPointerAllocator *bumpAlloc; - js::RegExpPrivateCache *repCache; - - JSC::ExecutableAllocator *createExecutableAllocator(JSContext *cx); - WTF::BumpPointerAllocator *createBumpPointerAllocator(JSContext *cx); - js::RegExpPrivateCache *createRegExpPrivateCache(JSContext *cx); - - public: - JSC::ExecutableAllocator *getOrCreateExecutableAllocator(JSContext *cx) { - if (execAlloc) - return execAlloc; - - return createExecutableAllocator(cx); - } - - WTF::BumpPointerAllocator *getOrCreateBumpPointerAllocator(JSContext *cx) { - if (bumpAlloc) - return bumpAlloc; - - return createBumpPointerAllocator(cx); - } - - js::RegExpPrivateCache *getRegExpPrivateCache() { - return repCache; - } - js::RegExpPrivateCache *getOrCreateRegExpPrivateCache(JSContext *cx) { - if (repCache) - return repCache; - - return createRegExpPrivateCache(cx); - } - - /* Called at the end of the global GC sweep phase to deallocate repCache memory. */ - void purgeRegExpPrivateCache(); - - /* - * The GSN cache is per thread since even multi-cx-per-thread embeddings - * do not interleave js_GetSrcNote calls. - */ - GSNCache gsnCache; - - /* Property cache for faster call/get/set invocation. */ - PropertyCache propertyCache; - - /* State used by jsdtoa.cpp. */ - DtoaState *dtoaState; +typedef Vector ScriptOpcodeCountsVector; +struct ConservativeGCData +{ /* Base address of the native stack for the current thread. */ uintptr_t *nativeStackBase; - /* List of currently pending operations on proxies. */ - PendingProxyOperation *pendingProxyOperation; + /* + * The GC scans conservatively between ThreadData::nativeStackBase and + * nativeStackTop unless the latter is NULL. + */ + uintptr_t *nativeStackTop; - ConservativeGCThreadData conservativeGC; - -#ifdef DEBUG - size_t noGCOrAllocationCheck; -#endif - - ThreadData(JSRuntime *rt); - ~ThreadData(); - - bool init(); - - void mark(JSTracer *trc) { - stackSpace.mark(trc); - } - - void purge(JSContext *cx) { - tempLifoAlloc.freeUnused(); - gsnCache.purge(); - - /* FIXME: bug 506341. */ - propertyCache.purge(cx); - } - -#ifdef JS_THREADSAFE - void sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf, size_t *normal, size_t *temporary, - size_t *regexpCode, size_t *stackCommitted); -#endif - - /* This must be called with the GC lock held. */ - void triggerOperationCallback(JSRuntime *rt); + union { + jmp_buf jmpbuf; + uintptr_t words[JS_HOWMANY(sizeof(jmp_buf), sizeof(uintptr_t))]; + } registerSnapshot; /* - * Frames currently running in js::Interpret. See InterpreterFrames for - * details. + * Cycle collector uses this to communicate that the native stack of the + * GC thread should be scanned only if the thread have more than the given + * threshold of requests. */ - InterpreterFrames *interpreterFrames; + unsigned requestThreshold; + + ConservativeGCData() + : nativeStackBase(NULL), nativeStackTop(NULL), requestThreshold(0) + {} + + ~ConservativeGCData() { +#ifdef JS_THREADSAFE + /* + * The conservative GC scanner should be disabled when the thread leaves + * the last request. + */ + JS_ASSERT(!hasStackToScan()); +#endif + } + + JS_NEVER_INLINE void recordStackTop(); + +#ifdef JS_THREADSAFE + void updateForRequestEnd(unsigned suspendCount) { + if (suspendCount) + recordStackTop(); + else + nativeStackTop = NULL; + } +#endif + + bool hasStackToScan() const { + return !!nativeStackTop; + } }; } /* namespace js */ -#ifdef JS_THREADSAFE - -/* - * Structure uniquely representing a thread. It holds thread-private data - * 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(). */ - void *id; - - /* Number of JS_SuspendRequest calls without JS_ResumeRequest. */ - unsigned suspendCount; - -# ifdef DEBUG - unsigned checkRequestDepth; -# endif - - /* Factored out of JSThread for !JS_THREADSAFE embedding in JSRuntime. */ - js::ThreadData data; - - JSThread(JSRuntime *rt, void *id) - : id(id), - suspendCount(0), -# ifdef DEBUG - checkRequestDepth(0), -# endif - data(rt) - { - JS_INIT_CLIST(&contextList); - } - - ~JSThread() { - /* The thread must have zero contexts. */ - JS_ASSERT(JS_CLIST_IS_EMPTY(&contextList)); - } - - bool init() { - return data.init(); - } - - JS_FRIEND_API(void) sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf, size_t *normal, - size_t *temporary, size_t *regexpCode, - size_t *stackCommitted); -}; - -#define JS_THREAD_DATA(cx) (&(cx)->thread()->data) - -extern JSThread * -js_CurrentThreadAndLockGC(JSRuntime *rt); - -/* - * 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_InitContextThreadAndLockGC(JSContext *cx); - -/* - * On entrance the GC lock must be held and it will be held on exit. - */ -extern void -js_ClearContextThread(JSContext *cx); - -#endif /* JS_THREADSAFE */ - -typedef enum JSDestroyContextMode { - JSDCM_NO_GC, - JSDCM_MAYBE_GC, - JSDCM_FORCE_GC, - JSDCM_NEW_FAILED -} JSDestroyContextMode; - -typedef struct JSPropertyTreeEntry { - JSDHashEntryHdr hdr; - js::Shape *child; -} JSPropertyTreeEntry; - -namespace js { - -typedef Vector ScriptOpcodeCountsVector; - -} - struct JSRuntime { + /* + * If non-zero, we were been asked to call the operation callback as soon + * as possible. + */ + volatile int32_t interrupt; + /* Default compartment. */ JSCompartment *atomsCompartment; @@ -347,6 +197,7 @@ struct JSRuntime /* See comment for JS_AbortIfWrongThread in jsapi.h. */ #ifdef JS_THREADSAFE public: + void *ownerThread() const { return ownerThread_; } void clearOwnerThread(); void setOwnerThread(); JS_FRIEND_API(bool) onOwnerThread() const; @@ -358,6 +209,46 @@ struct JSRuntime bool onOwnerThread() const { return true; } #endif + /* Keeper of the contiguous stack used by all contexts in this thread. */ + js::StackSpace stackSpace; + + /* Temporary arena pool used while compiling and decompiling. */ + static const size_t TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 1 << 12; + js::LifoAlloc tempLifoAlloc; + + private: + /* + * Both of these allocators are used for regular expression code which is shared at the + * thread-data level. + */ + JSC::ExecutableAllocator *execAlloc_; + WTF::BumpPointerAllocator *bumpAlloc_; + js::RegExpPrivateCache *repCache_; + + JSC::ExecutableAllocator *createExecutableAllocator(JSContext *cx); + WTF::BumpPointerAllocator *createBumpPointerAllocator(JSContext *cx); + js::RegExpPrivateCache *createRegExpPrivateCache(JSContext *cx); + + public: + JSC::ExecutableAllocator *getExecutableAllocator(JSContext *cx) { + return execAlloc_ ? execAlloc_ : createExecutableAllocator(cx); + } + WTF::BumpPointerAllocator *getBumpPointerAllocator(JSContext *cx) { + return bumpAlloc_ ? bumpAlloc_ : createBumpPointerAllocator(cx); + } + js::RegExpPrivateCache *maybeRegExpPrivateCache() { + return repCache_; + } + js::RegExpPrivateCache *getRegExpPrivateCache(JSContext *cx) { + return repCache_ ? repCache_ : createRegExpPrivateCache(cx); + } + + /* + * Frames currently running in js::Interpret. See InterpreterFrames for + * details. + */ + js::InterpreterFrames *interpreterFrames; + /* Context create/destroy callback. */ JSContextCallback cxCallback; @@ -367,6 +258,18 @@ struct JSRuntime js::ActivityCallback activityCallback; void *activityCallbackArg; +#ifdef JS_THREADSAFE + /* Number of JS_SuspendRequest calls withot JS_ResumeRequest. */ + unsigned suspendCount; + + /* The request depth for this thread. */ + unsigned requestDepth; + +# ifdef DEBUG + unsigned checkRequestDepth; +# endif +#endif + /* Garbage collector state, used by jsgc.c. */ /* @@ -519,7 +422,7 @@ struct JSRuntime JSAtom *emptyString; - /* List of active contexts sharing this runtime; protected by gcLock. */ + /* List of active contexts sharing this runtime. */ JSCList contextList; bool hasContexts() const { @@ -550,20 +453,9 @@ struct JSRuntime #ifdef JS_THREADSAFE /* These combine to interlock the GC and new requests. */ PRLock *gcLock; - PRCondVar *gcDone; - PRCondVar *requestDone; uint32_t requestCount; - JSThread *gcThread; js::GCHelperThread gcHelperThread; - - /* - * Mapping from NSPR thread identifiers to JSThreads. - * - * This map can be accessed by the GC thread; or by the thread that holds - * gcLock, if GC is not running. - */ - JSThread::Map threads; #endif /* JS_THREADSAFE */ uint32_t debuggerMutations; @@ -587,12 +479,6 @@ struct JSRuntime */ int32_t propertyRemovals; - /* Script filename table. */ - struct JSHashTable *scriptFilenameTable; -#ifdef JS_THREADSAFE - PRLock *scriptFilenameTableLock; -#endif - /* Number localization, used by jsnum.c */ const char *thousandsSeparator; const char *decimalSeparator; @@ -608,14 +494,28 @@ struct JSRuntime JSObject *anynameObject; JSObject *functionNamespaceObject; -#ifdef JS_THREADSAFE - /* Number of threads with active requests and unhandled interrupts. */ - volatile int32_t interruptCounter; -#else - js::ThreadData threadData; + /* + * Flag indicating that we are waiving any soft limits on the GC heap + * because we want allocations to be infallible (except when we hit OOM). + */ + bool waiveGCQuota; -#define JS_THREAD_DATA(cx) (&(cx)->runtime->threadData) -#endif + /* + * The GSN cache is per thread since even multi-cx-per-thread embeddings + * do not interleave js_GetSrcNote calls. + */ + js::GSNCache gsnCache; + + /* Property cache for faster call/get/set invocation. */ + js::PropertyCache propertyCache; + + /* State used by jsdtoa.cpp. */ + DtoaState *dtoaState; + + /* List of currently pending operations on proxies. */ + js::PendingProxyOperation *pendingProxyOperation; + + js::ConservativeGCData conservativeGC; private: JSPrincipals *trustedPrincipals_; @@ -633,6 +533,10 @@ struct JSRuntime JSPreWrapCallback preWrapObjectCallback; js::PreserveWrapperCallback preserveWrapperCallback; +#ifdef DEBUG + size_t noGCOrAllocationCheck; +#endif + /* * To ensure that cx->malloc does not cause a GC, we set this flag during * OOM reporting (in js_ReportOutOfMemory). If a GC is requested while @@ -739,13 +643,20 @@ struct JSRuntime * The function must be called outside the GC lock. */ JS_FRIEND_API(void *) onOutOfMemory(void *p, size_t nbytes, JSContext *cx); + + JS_FRIEND_API(void) triggerOperationCallback(); + + void sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf, size_t *normal, size_t *temporary, + size_t *regexpCode, size_t *stackCommitted); + + void purge(JSContext *cx); }; -/* Common macros to access thread-local caches in JSThread or JSRuntime. */ -#define JS_PROPERTY_CACHE(cx) (JS_THREAD_DATA(cx)->propertyCache) +/* Common macros to access thread-local caches in JSRuntime. */ +#define JS_PROPERTY_CACHE(cx) (cx->runtime->propertyCache) -#define JS_KEEP_ATOMS(rt) JS_ATOMIC_INCREMENT(&(rt)->gcKeepAtoms); -#define JS_UNKEEP_ATOMS(rt) JS_ATOMIC_DECREMENT(&(rt)->gcKeepAtoms); +#define JS_KEEP_ATOMS(rt) (rt)->gcKeepAtoms++; +#define JS_UNKEEP_ATOMS(rt) (rt)->gcKeepAtoms--; #ifdef JS_ARGUMENT_FORMATTER_DEFINED /* @@ -899,7 +810,7 @@ struct JSContext /* Limit pointer for checking native stack consumption during recursion. */ uintptr_t stackLimit; - /* Data shared by threads in an address space. */ + /* Data shared by contexts and compartments in an address space. */ JSRuntime *const runtime; /* GC heap compartment. */ @@ -907,16 +818,6 @@ struct JSContext inline void setCompartment(JSCompartment *compartment); -#ifdef JS_THREADSAFE - private: - JSThread *thread_; - public: - JSThread *thread() const { return thread_; } - - void setThread(JSThread *thread); - static const size_t threadOffset() { return offsetof(JSContext, thread_); } -#endif - /* Current execution stack. */ js::ContextStack stack; @@ -1044,17 +945,10 @@ struct JSContext bool hasAtLineOption() const { return hasRunOption(JSOPTION_ATLINE); } bool hasJITHardeningOption() const { return !hasRunOption(JSOPTION_SOFTEN); } - js::LifoAlloc &tempLifoAlloc() { return JS_THREAD_DATA(this)->tempLifoAlloc; } + js::LifoAlloc &tempLifoAlloc() { return runtime->tempLifoAlloc; } inline js::LifoAlloc &typeLifoAlloc(); #ifdef JS_THREADSAFE - /* - * AtomizeInline uses this flag to tell RunLastDitchGC and - * js_ReportOutOfMemory that they should temporarily unlock the atoms - * compartment. - */ - bool atomsCompartmentIsLocked; - unsigned outstandingRequests;/* number of JS_BeginRequest calls without the corresponding JS_EndRequest. */ @@ -1165,8 +1059,6 @@ struct JSContext js::GCHelperThread *gcBackgroundFree; #endif - js::ThreadData *threadData() { return JS_THREAD_DATA(this); } - inline void* malloc_(size_t bytes) { return runtime->malloc_(bytes, this); } @@ -1251,13 +1143,6 @@ struct JSContext return reinterpret_cast(uintptr_t(link) - offsetof(JSContext, link)); } -#ifdef JS_THREADSAFE - static inline JSContext *fromThreadLinks(JSCList *link) { - JS_ASSERT(link); - return reinterpret_cast(uintptr_t(link) - offsetof(JSContext, threadLinks)); - } -#endif - private: /* * The allocation code calls the function to indicate either OOM failure @@ -1325,6 +1210,14 @@ class AutoXMLRooter : private AutoGCRooter { }; #endif /* JS_HAS_XML_SUPPORT */ +#ifdef JS_THREADSAFE +# define JS_LOCK_GC(rt) PR_Lock((rt)->gcLock) +# define JS_UNLOCK_GC(rt) PR_Unlock((rt)->gcLock) +#else +# define JS_LOCK_GC(rt) +# define JS_UNLOCK_GC(rt) +#endif + class AutoUnlockGC { private: JSRuntime *rt; @@ -1341,63 +1234,6 @@ class AutoUnlockGC { ~AutoUnlockGC() { JS_LOCK_GC(rt); } }; -class AutoLockAtomsCompartment { - private: - JSContext *cx; - JS_DECL_USE_GUARD_OBJECT_NOTIFIER - - public: - AutoLockAtomsCompartment(JSContext *cx - JS_GUARD_OBJECT_NOTIFIER_PARAM) - : cx(cx) - { - JS_GUARD_OBJECT_NOTIFIER_INIT; -#ifdef JS_THREADSAFE - JS_ASSERT(!cx->atomsCompartmentIsLocked); - JS_LOCK(cx, &cx->runtime->atomState.lock); - cx->atomsCompartmentIsLocked = true; -#endif - } - - ~AutoLockAtomsCompartment() { -#ifdef JS_THREADSAFE - JS_ASSERT(cx->atomsCompartmentIsLocked); - cx->atomsCompartmentIsLocked = false; - JS_UNLOCK(cx, &cx->runtime->atomState.lock); -#endif - } -}; - -class AutoUnlockAtomsCompartmentWhenLocked { - JSContext *cx; - JS_DECL_USE_GUARD_OBJECT_NOTIFIER - - public: - AutoUnlockAtomsCompartmentWhenLocked(JSContext *cx - JS_GUARD_OBJECT_NOTIFIER_PARAM) - : cx(NULL) - { - JS_GUARD_OBJECT_NOTIFIER_INIT; - #ifdef JS_THREADSAFE - if (cx->atomsCompartmentIsLocked) { - this->cx = cx; - cx->atomsCompartmentIsLocked = false; - JS_UNLOCK(cx, &cx->runtime->atomState.lock); - } -#endif - } - - ~AutoUnlockAtomsCompartmentWhenLocked() { -#ifdef JS_THREADSAFE - if (cx) { - JS_ASSERT(!cx->atomsCompartmentIsLocked); - JS_LOCK(cx, &cx->runtime->atomState.lock); - cx->atomsCompartmentIsLocked = true; - } -#endif - } -}; - class AutoKeepAtoms { JSRuntime *rt; JS_DECL_USE_GUARD_OBJECT_NOTIFIER @@ -1478,62 +1314,8 @@ class JSAutoResolveFlags JS_DECL_USE_GUARD_OBJECT_NOTIFIER }; -extern js::ThreadData * -js_CurrentThreadData(JSRuntime *rt); - -extern JSBool -js_InitThreads(JSRuntime *rt); - -extern void -js_FinishThreads(JSRuntime *rt); - -extern void -js_PurgeThreads(JSContext *cx); - -extern void -js_PurgeThreads_PostGlobalSweep(JSContext *cx); - namespace js { -#ifdef JS_THREADSAFE - -/* Iterator over ThreadData from all JSThread instances. */ -class ThreadDataIter : public JSThread::Map::Range -{ - public: - ThreadDataIter(JSRuntime *rt) : JSThread::Map::Range(rt->threads.all()) {} - - ThreadData *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; - } - - ThreadData *threadData() const { - JS_ASSERT(!done); - return &runtime->threadData; - } -}; - -#endif /* !JS_THREADSAFE */ - /* * Enumerate all contexts in a runtime that are in the same thread as a given * context. @@ -1544,11 +1326,7 @@ class ThreadContextRange { public: explicit ThreadContextRange(JSContext *cx) { -#ifdef JS_THREADSAFE - end = &cx->thread()->contextList; -#else end = &cx->runtime->contextList; -#endif begin = end->next; } @@ -1556,11 +1334,7 @@ public: void popFront() { JS_ASSERT(!empty()); begin = begin->next; } JSContext *front() const { -#ifdef JS_THREADSAFE - return JSContext::fromThreadLinks(begin); -#else return JSContext::fromLinkField(begin); -#endif } }; @@ -1573,6 +1347,13 @@ public: extern JSContext * js_NewContext(JSRuntime *rt, size_t stackChunkSize); +typedef enum JSDestroyContextMode { + JSDCM_NO_GC, + JSDCM_MAYBE_GC, + JSDCM_FORCE_GC, + JSDCM_NEW_FAILED +} JSDestroyContextMode; + extern void js_DestroyContext(JSContext *cx, JSDestroyContextMode mode); @@ -1658,8 +1439,7 @@ js_ReportValueErrorFlags(JSContext *cx, uintN flags, const uintN errorNumber, extern JSErrorFormatString js_ErrorFormatString[JSErr_Limit]; #ifdef JS_THREADSAFE -# define JS_ASSERT_REQUEST_DEPTH(cx) (JS_ASSERT((cx)->thread()), \ - JS_ASSERT((cx)->thread()->data.requestDepth >= 1)) +# define JS_ASSERT_REQUEST_DEPTH(cx) JS_ASSERT((cx)->runtime->requestDepth >= 1) #else # define JS_ASSERT_REQUEST_DEPTH(cx) ((void) 0) #endif @@ -1671,7 +1451,7 @@ extern JSErrorFormatString js_ErrorFormatString[JSErr_Limit]; */ #define JS_CHECK_OPERATION_LIMIT(cx) \ (JS_ASSERT_REQUEST_DEPTH(cx), \ - (!JS_THREAD_DATA(cx)->interruptFlags || js_InvokeOperationCallback(cx))) + (!cx->runtime->interrupt || js_InvokeOperationCallback(cx))) /* * Invoke the operation callback and return false if the current execution @@ -1683,18 +1463,6 @@ js_InvokeOperationCallback(JSContext *cx); extern JSBool js_HandleExecutionInterrupt(JSContext *cx); -namespace js { - -/* These must be called with GC lock taken. */ - -void -TriggerOperationCallback(JSContext *cx); - -void -TriggerAllOperationCallbacks(JSRuntime *rt); - -} /* namespace js */ - /* * Get the topmost scripted frame in a context. Note: if the topmost frame is * in the middle of an inline call, that call will be expanded. To avoid this, diff --git a/js/src/jscntxtinlines.h b/js/src/jscntxtinlines.h index e644800c0126..9ca6a86219d1 100644 --- a/js/src/jscntxtinlines.h +++ b/js/src/jscntxtinlines.h @@ -87,7 +87,7 @@ GetGlobalForScopeChain(JSContext *cx) inline GSNCache * GetGSNCache(JSContext *cx) { - return &JS_THREAD_DATA(cx)->gsnCache; + return &cx->runtime->gsnCache; } class AutoNamespaceArray : protected AutoGCRooter { diff --git a/js/src/jsfriendapi.cpp b/js/src/jsfriendapi.cpp index c428e2d1b4bc..3f92643de0ba 100644 --- a/js/src/jsfriendapi.cpp +++ b/js/src/jsfriendapi.cpp @@ -561,10 +561,10 @@ CallContextDebugHandler(JSContext *cx, JSScript *script, jsbytecode *bc, Value * } #ifdef JS_THREADSAFE -JSThread * -GetContextThread(const JSContext *cx) +void * +GetOwnerThread(const JSContext *cx) { - return cx->thread(); + return cx->runtime->ownerThread(); } JS_FRIEND_API(unsigned) @@ -585,18 +585,18 @@ AutoSkipConservativeScan::AutoSkipConservativeScan(JSContext *cx { MOZ_GUARD_OBJECT_NOTIFIER_INIT; - ThreadData &threadData = context->thread()->data; - JS_ASSERT(threadData.requestDepth >= 1); - JS_ASSERT(!threadData.conservativeGC.requestThreshold); - if (threadData.requestDepth == 1) - threadData.conservativeGC.requestThreshold = 1; + JSRuntime *rt = context->runtime; + JS_ASSERT(rt->requestDepth >= 1); + JS_ASSERT(!rt->conservativeGC.requestThreshold); + if (rt->requestDepth == 1) + rt->conservativeGC.requestThreshold = 1; } AutoSkipConservativeScan::~AutoSkipConservativeScan() { - ThreadData &threadData = context->thread()->data; - if (threadData.requestDepth == 1) - threadData.conservativeGC.requestThreshold = 0; + JSRuntime *rt = context->runtime; + if (rt->requestDepth == 1) + rt->conservativeGC.requestThreshold = 0; } #endif @@ -626,12 +626,9 @@ IsContextRunningJS(JSContext *cx) } JS_FRIEND_API(void) -TriggerOperationCallbacksForActiveContexts(JSRuntime *rt) +TriggerOperationCallback(JSRuntime *rt) { - JSContext* cx = NULL; - while ((cx = js_NextActiveContext(rt, cx))) { - TriggerOperationCallback(cx); - } + rt->triggerOperationCallback(); } JS_FRIEND_API(const CompartmentVector&) diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index e47d52743e80..903e561d7cd7 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -475,8 +475,8 @@ JS_FRIEND_API(JSString *) GetPCCountScriptContents(JSContext *cx, size_t script); #ifdef JS_THREADSAFE -JS_FRIEND_API(JSThread *) -GetContextThread(const JSContext *cx); +JS_FRIEND_API(void *) +GetOwnerThread(const JSContext *cx); JS_FRIEND_API(unsigned) GetContextOutstandingRequests(const JSContext *cx); @@ -562,7 +562,7 @@ IsContextRunningJS(JSContext *cx); /* Must be called with GC lock taken. */ extern JS_FRIEND_API(void) -TriggerOperationCallbacksForActiveContexts(JSRuntime *rt); +TriggerOperationCallback(JSRuntime *rt); class SystemAllocPolicy; typedef Vector CompartmentVector; diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 95b305863e6a..176ce34ccd6c 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -106,6 +106,12 @@ # include #endif +#ifdef XP_WIN +# include "jswin.h" +#else +# include +#endif + using namespace mozilla; using namespace js; using namespace js::gc; @@ -875,15 +881,9 @@ js_InitGC(JSRuntime *rt, uint32_t maxbytes) return false; #ifdef JS_THREADSAFE - rt->gcLock = JS_NEW_LOCK(); + rt->gcLock = PR_NewLock(); if (!rt->gcLock) return false; - rt->gcDone = JS_NEW_CONDVAR(rt->gcLock); - if (!rt->gcDone) - return false; - rt->requestDone = JS_NEW_CONDVAR(rt->gcLock); - if (!rt->requestDone) - return false; if (!rt->gcHelperThread.init()) return false; #endif @@ -1079,23 +1079,31 @@ MarkRangeConservatively(JSTracer *trc, const uintptr_t *begin, const uintptr_t * MarkWordConservatively(trc, *i); } -static void -MarkThreadDataConservatively(JSTracer *trc, ThreadData *td) +static JS_NEVER_INLINE void +MarkConservativeStackRoots(JSTracer *trc, JSRuntime *rt) { - ConservativeGCThreadData *ctd = &td->conservativeGC; - JS_ASSERT(ctd->hasStackToScan()); + ConservativeGCData *cgcd = &rt->conservativeGC; + if (!cgcd->hasStackToScan()) { +#ifdef JS_THREADSAFE + JS_ASSERT(!rt->suspendCount); + JS_ASSERT(rt->requestDepth <= cgcd->requestThreshold); +#endif + return; + } + uintptr_t *stackMin, *stackEnd; #if JS_STACK_GROWTH_DIRECTION > 0 - stackMin = td->nativeStackBase; - stackEnd = ctd->nativeStackTop; + stackMin = rt->conservativeGC.nativeStackBase; + stackEnd = cgcd->nativeStackTop; #else - stackMin = ctd->nativeStackTop + 1; - stackEnd = td->nativeStackBase; + stackMin = cgcd->nativeStackTop + 1; + stackEnd = rt->conservativeGC.nativeStackBase; #endif + JS_ASSERT(stackMin <= stackEnd); MarkRangeConservatively(trc, stackMin, stackEnd); - MarkRangeConservatively(trc, ctd->registerSnapshot.words, - ArrayEnd(ctd->registerSnapshot.words)); + MarkRangeConservatively(trc, cgcd->registerSnapshot.words, + ArrayEnd(cgcd->registerSnapshot.words)); } void @@ -1134,36 +1142,16 @@ MarkStackRangeConservatively(JSTracer *trc, Value *beginv, Value *endv) #endif } -void -MarkConservativeStackRoots(JSTracer *trc) -{ -#ifdef JS_THREADSAFE - for (JSThread::Map::Range r = trc->runtime->threads.all(); !r.empty(); r.popFront()) { - JSThread *thread = r.front().value; - ConservativeGCThreadData *ctd = &thread->data.conservativeGC; - if (ctd->hasStackToScan()) { - JS_ASSERT_IF(!thread->data.requestDepth, thread->suspendCount); - MarkThreadDataConservatively(trc, &thread->data); - } else { - JS_ASSERT(!thread->suspendCount); - JS_ASSERT(thread->data.requestDepth <= ctd->requestThreshold); - } - } -#else - MarkThreadDataConservatively(trc, &trc->runtime->threadData); -#endif -} - JS_NEVER_INLINE void -ConservativeGCThreadData::recordStackTop() +ConservativeGCData::recordStackTop() { /* Update the native stack pointer if it points to a bigger stack. */ uintptr_t dummy; nativeStackTop = &dummy; /* - * To record and update the register snapshot for the conservative - * scanning with the latest values we use setjmp. + * To record and update the register snapshot for the conservative scanning + * with the latest values we use setjmp. */ #if defined(_MSC_VER) # pragma warning(push) @@ -1175,18 +1163,18 @@ ConservativeGCThreadData::recordStackTop() #endif } -static inline void +void RecordNativeStackTopForGC(JSContext *cx) { - ConservativeGCThreadData *ctd = &JS_THREAD_DATA(cx)->conservativeGC; + ConservativeGCData *cgcd = &cx->runtime->conservativeGC; #ifdef JS_THREADSAFE /* Record the stack top here only if we are called from a request. */ - JS_ASSERT(cx->thread()->data.requestDepth >= ctd->requestThreshold); - if (cx->thread()->data.requestDepth == ctd->requestThreshold) + JS_ASSERT(cx->runtime->requestDepth >= cgcd->requestThreshold); + if (cx->runtime->requestDepth == cgcd->requestThreshold) return; #endif - ctd->recordStackTop(); + cgcd->recordStackTop(); } } /* namespace js */ @@ -1264,7 +1252,6 @@ js_AddRootRT(JSRuntime *rt, jsval *vp, const char *name) * rt->gcLock across the mark phase (including the root hashtable mark). */ AutoLockGC lock(rt); - js_WaitForGC(rt); return !!rt->gcRootsHash.put((void *)vp, RootInfo(name, JS_GC_ROOT_VALUE_PTR)); @@ -1281,7 +1268,6 @@ js_AddGCThingRootRT(JSRuntime *rt, void **rp, const char *name) * rt->gcLock across the mark phase (including the root hashtable mark). */ AutoLockGC lock(rt); - js_WaitForGC(rt); return !!rt->gcRootsHash.put((void *)rp, RootInfo(name, JS_GC_ROOT_GCTHING_PTR)); @@ -1295,7 +1281,6 @@ js_RemoveRoot(JSRuntime *rt, void *rp) * Same synchronization drill as above in js_AddRoot. */ AutoLockGC lock(rt); - js_WaitForGC(rt); rt->gcRootsHash.remove(rp); rt->gcPoke = JS_TRUE; return JS_TRUE; @@ -1660,9 +1645,6 @@ RunLastDitchGC(JSContext *cx) { JSRuntime *rt = cx->runtime; - /* The atoms are locked when we create a string in AtomizeInline. */ - AutoUnlockAtomsCompartmentWhenLocked unlockAtomsCompartment(cx); - /* The last ditch GC preserves all atoms. */ AutoKeepAtoms keep(rt); js_GC(cx, rt->gcTriggerCompartment, GC_NORMAL, gcstats::LASTDITCH); @@ -2100,7 +2082,7 @@ MarkRuntime(JSTracer *trc) JSRuntime *rt = trc->runtime; if (rt->hasContexts()) - MarkConservativeStackRoots(trc); + MarkConservativeStackRoots(trc, rt); for (RootRange r = rt->gcRootsHash.all(); !r.empty(); r.popFront()) gc_root_traversal(trc, r.front()); @@ -2141,8 +2123,7 @@ MarkRuntime(JSTracer *trc) } } - for (ThreadDataIter i(rt); !i.empty(); i.popFront()) - i.threadData()->mark(trc); + rt->stackSpace.mark(trc); /* The embedding can register additional roots here. */ if (JSTraceDataOp op = rt->gcBlackRootsTraceOp) @@ -2158,17 +2139,16 @@ MarkRuntime(JSTracer *trc) void TriggerGC(JSRuntime *rt, gcstats::Reason reason) { + JS_ASSERT(rt->onOwnerThread()); + if (rt->gcRunning || rt->gcIsNeeded) return; - /* - * Trigger the GC when it is safe to call an operation callback on any - * thread. - */ + /* Trigger the GC when it is safe to call an operation callback. */ rt->gcIsNeeded = true; rt->gcTriggerCompartment = NULL; rt->gcTriggerReason = reason; - TriggerAllOperationCallbacks(rt); + rt->triggerOperationCallback(); } void @@ -2208,7 +2188,7 @@ TriggerCompartmentGC(JSCompartment *comp, gcstats::Reason reason) rt->gcIsNeeded = true; rt->gcTriggerCompartment = comp; rt->gcTriggerReason = reason; - TriggerAllOperationCallbacks(comp->rt); + comp->rt->triggerOperationCallback(); } void @@ -2381,6 +2361,23 @@ ExpireChunksAndArenas(JSRuntime *rt, bool shouldShrink) #ifdef JS_THREADSAFE +static unsigned +GetCPUCount() +{ + static unsigned ncpus = 0; + if (ncpus == 0) { +# ifdef XP_WIN + SYSTEM_INFO sysinfo; + GetSystemInfo(&sysinfo); + ncpus = unsigned(sysinfo.dwNumberOfProcessors); +# else + long n = sysconf(_SC_NPROCESSORS_ONLN); + ncpus = (n > 0) ? unsigned(n) : 1; +# endif + } + return ncpus; +} + bool GCHelperThread::init() { @@ -2394,7 +2391,7 @@ GCHelperThread::init() if (!thread) return false; - backgroundAllocation = (js_GetCPUCount() >= 2); + backgroundAllocation = (GetCPUCount() >= 2); return true; } @@ -2680,7 +2677,7 @@ BeginMarkPhase(JSContext *cx, GCMarker *gcmarker, JSGCInvocationKind gckind) for (GCCompartmentsIter c(rt); !c.done(); c.next()) c->purge(cx); - js_PurgeThreads(cx); + rt->purge(cx); { JSContext *iter = NULL; @@ -2844,13 +2841,7 @@ SweepPhase(JSContext *cx, GCMarker *gcmarker, JSGCInvocationKind gckind) } } -/* - * Perform mark-and-sweep GC. - * - * In a JS_THREADSAFE build, the calling thread must be rt->gcThread and each - * other thread must be either outside all requests or blocked waiting for GC - * to finish. The caller must hold rt->gcLock. - */ +/* Perform mark-and-sweep GC. If comp is set, we perform a single-compartment GC. */ static void MarkAndSweep(JSContext *cx, JSGCInvocationKind gckind) { @@ -2880,74 +2871,6 @@ MarkAndSweep(JSContext *cx, JSGCInvocationKind gckind) SweepPhase(cx, &gcmarker, gckind); } -#ifdef JS_THREADSAFE - -/* - * If the GC is running and we're called on another thread, wait for this GC - * activation to finish. We can safely wait here without fear of deadlock (in - * the case where we are called within a request on another thread's context) - * because the GC doesn't set rt->gcRunning until after it has waited for all - * active requests to end. - * - * We call here js_CurrentThreadId() after checking for rt->gcState to avoid - * an expensive call when the GC is not running. - */ -void -js_WaitForGC(JSRuntime *rt) -{ - if (rt->gcRunning && rt->gcThread->id != js_CurrentThreadId()) { - do { - JS_AWAIT_GC_DONE(rt); - } while (rt->gcRunning); - } -} - -/* - * GC is running on another thread. Temporarily suspend all requests running - * on the current thread and wait until the GC is done. - */ -static void -LetOtherGCFinish(JSContext *cx) -{ - JSRuntime *rt = cx->runtime; - JS_ASSERT(rt->gcThread); - JS_ASSERT(cx->thread() != rt->gcThread); - - size_t requestDebit = cx->thread()->data.requestDepth ? 1 : 0; - JS_ASSERT(requestDebit <= rt->requestCount); - if (requestDebit != 0) { - rt->requestCount -= requestDebit; - if (rt->requestCount == 0) - JS_NOTIFY_REQUEST_DONE(rt); - - /* - * Update the native stack before we wait so the GC thread see the - * correct stack bounds. - */ - RecordNativeStackTopForGC(cx); - } - - /* - * Check that we did not release the GC lock above and let the GC to - * finish before we wait. - */ - JS_ASSERT(rt->gcThread); - - /* - * Wait for GC to finish on the other thread, even if requestDebit is 0 - * and even if GC has not started yet because the gcThread is waiting in - * AutoGCSession. This ensures that js_GC never returns without a full GC - * cycle happening. - */ - do { - JS_AWAIT_GC_DONE(rt); - } while (rt->gcThread); - - rt->requestCount += requestDebit; -} - -#endif - class AutoGCSession { public: explicit AutoGCSession(JSContext *cx); @@ -2960,88 +2883,27 @@ class AutoGCSession { void operator=(const AutoGCSession&) MOZ_DELETE; }; -/* - * Start a new GC session. Together with LetOtherGCFinish this function - * contains the rendezvous algorithm by which we stop the world for GC. - * - * This thread becomes the GC thread. Wait for all other threads to quiesce. - * Then set rt->gcRunning and return. - */ +/* Start a new GC session. */ AutoGCSession::AutoGCSession(JSContext *cx) : context(cx) { - JS_ASSERT(!JS_THREAD_DATA(cx)->noGCOrAllocationCheck); + JS_ASSERT(!cx->runtime->noGCOrAllocationCheck); JSRuntime *rt = cx->runtime; - -#ifdef JS_THREADSAFE - if (rt->gcThread && rt->gcThread != cx->thread()) - LetOtherGCFinish(cx); -#endif - JS_ASSERT(!rt->gcRunning); - -#ifdef JS_THREADSAFE - /* No other thread is in GC, so indicate that we're now in GC. */ - JS_ASSERT(!rt->gcThread); - rt->gcThread = cx->thread(); - - /* - * Notify operation callbacks on other threads, which will give them a - * chance to yield their requests. Threads without requests perform their - * callback at some later point, which then will be unnecessary, but - * harmless. - */ - for (JSThread::Map::Range r = rt->threads.all(); !r.empty(); r.popFront()) { - JSThread *thread = r.front().value; - if (thread != cx->thread()) - thread->data.triggerOperationCallback(rt); - } - - /* - * Discount the request on the current thread from contributing to - * rt->requestCount before we wait for all other requests to finish. - * JS_NOTIFY_REQUEST_DONE, which will wake us up, is only called on - * rt->requestCount transitions to 0. - */ - size_t requestDebit = cx->thread()->data.requestDepth ? 1 : 0; - JS_ASSERT(requestDebit <= rt->requestCount); - if (requestDebit != rt->requestCount) { - rt->requestCount -= requestDebit; - - do { - JS_AWAIT_REQUEST_DONE(rt); - } while (rt->requestCount > 0); - rt->requestCount += requestDebit; - } - -#endif /* JS_THREADSAFE */ - - /* - * Set rt->gcRunning here within the GC lock, and after waiting for any - * active requests to end. This way js_WaitForGC called outside a request - * would not block on the GC that is waiting for other requests to finish - * with rt->gcThread set while JS_BeginRequest would do such wait. - */ rt->gcRunning = true; } -/* End the current GC session and allow other threads to proceed. */ AutoGCSession::~AutoGCSession() { JSRuntime *rt = context->runtime; rt->gcRunning = false; -#ifdef JS_THREADSAFE - JS_ASSERT(rt->gcThread == context->thread()); - rt->gcThread = NULL; - JS_NOTIFY_GC_DONE(rt); -#endif } /* * GC, repeatedly if necessary, until we think we have not created any new - * garbage and no other threads are demanding more GC. We disable inlining - * to ensure that the bottom of the stack with possible GC roots recorded in - * js_GC excludes any pointers we use during the marking implementation. + * garbage. We disable inlining to ensure that the bottom of the stack with + * possible GC roots recorded in js_GC excludes any pointers we use during the + * marking implementation. */ static JS_NEVER_INLINE void GCCycle(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind) @@ -3051,29 +2913,13 @@ GCCycle(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind) JS_ASSERT_IF(comp, comp != rt->atomsCompartment); JS_ASSERT_IF(comp, rt->gcMode == JSGC_MODE_COMPARTMENT); - /* - * Recursive GC is no-op and a call from another thread waits the started - * GC cycle to finish. - */ - if (rt->gcMarkAndSweep) { -#ifdef JS_THREADSAFE - JS_ASSERT(rt->gcThread); - if (rt->gcThread != cx->thread()) { - /* We do not return until another GC finishes. */ - LetOtherGCFinish(cx); - } -#endif + /* Recursive GC is no-op. */ + if (rt->gcMarkAndSweep) return; - } AutoGCSession gcsession(cx); - /* - * Don't GC if any thread is reporting an OOM. We check the flag after we - * have set up the GC session and know that the thread that reported OOM - * is either the current thread or waits for the GC to complete on this - * thread. - */ + /* Don't GC if we are reporting an OOM. */ if (rt->inOOMReport) return; @@ -3103,9 +2949,6 @@ GCCycle(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind) MarkAndSweep(cx, gckind); - if (!comp) - js_PurgeThreads_PostGlobalSweep(cx); - #ifdef JS_THREADSAFE if (cx->gcBackgroundFree) { JS_ASSERT(cx->gcBackgroundFree == &rt->gcHelperThread); @@ -3216,7 +3059,7 @@ TraceRuntime(JSTracer *trc) { JSContext *cx = trc->context; JSRuntime *rt = cx->runtime; - if (rt->gcThread != cx->thread()) { + if (!rt->gcRunning) { AutoLockGC lock(rt); AutoGCSession gcsession(cx); diff --git a/js/src/jsgc.h b/js/src/jsgc.h index f320090bf173..594c1f628a6c 100644 --- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -1320,11 +1320,6 @@ typedef HashMap WrapperMap; } /* namespace js */ -#ifdef DEBUG -extern bool -CheckAllocation(JSContext *cx); -#endif - extern JS_FRIEND_API(JSGCTraceKind) js_GetGCThingTraceKind(void *thing); @@ -1416,22 +1411,6 @@ typedef enum JSGCInvocationKind { extern void js_GC(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind, js::gcstats::Reason r); -#ifdef JS_THREADSAFE -/* - * This is a helper for code at can potentially run outside JS request to - * ensure that the GC is not running when the function returns. - * - * This function must be called with the GC lock held. - */ -extern void -js_WaitForGC(JSRuntime *rt); - -#else /* !JS_THREADSAFE */ - -# define js_WaitForGC(rt) ((void) 0) - -#endif - namespace js { #ifdef JS_THREADSAFE @@ -1585,57 +1564,6 @@ struct GCChunkHasher { typedef HashSet GCChunkSet; -struct ConservativeGCThreadData { - - /* - * The GC scans conservatively between ThreadData::nativeStackBase and - * nativeStackTop unless the latter is NULL. - */ - uintptr_t *nativeStackTop; - - union { - jmp_buf jmpbuf; - uintptr_t words[JS_HOWMANY(sizeof(jmp_buf), sizeof(uintptr_t))]; - } registerSnapshot; - - /* - * Cycle collector uses this to communicate that the native stack of the - * GC thread should be scanned only if the thread have more than the given - * threshold of requests. - */ - unsigned requestThreshold; - - ConservativeGCThreadData() - : nativeStackTop(NULL), requestThreshold(0) - { - } - - ~ConservativeGCThreadData() { -#ifdef JS_THREADSAFE - /* - * The conservative GC scanner should be disabled when the thread leaves - * the last request. - */ - JS_ASSERT(!hasStackToScan()); -#endif - } - - JS_NEVER_INLINE void recordStackTop(); - -#ifdef JS_THREADSAFE - void updateForRequestEnd(unsigned suspendCount) { - if (suspendCount) - recordStackTop(); - else - nativeStackTop = NULL; - } -#endif - - bool hasStackToScan() const { - return !!nativeStackTop; - } -}; - template struct MarkStack { T *stack; diff --git a/js/src/jsgcinlines.h b/js/src/jsgcinlines.h index fff318b22e7e..870301eb033b 100644 --- a/js/src/jsgcinlines.h +++ b/js/src/jsgcinlines.h @@ -346,7 +346,7 @@ class CellIter: public CellIterImpl lists->copyFreeListToArena(kind); } #ifdef DEBUG - counter = &JS_THREAD_DATA(cx)->noGCOrAllocationCheck; + counter = &cx->runtime->noGCOrAllocationCheck; ++*counter; #endif init(comp, kind); @@ -384,7 +384,7 @@ NewGCThing(JSContext *cx, js::gc::AllocKind kind, size_t thingSize) kind == js::gc::FINALIZE_STRING || kind == js::gc::FINALIZE_SHORT_STRING); #endif JS_ASSERT(!cx->runtime->gcRunning); - JS_ASSERT(!JS_THREAD_DATA(cx)->noGCOrAllocationCheck); + JS_ASSERT(!cx->runtime->noGCOrAllocationCheck); #ifdef JS_GC_ZEAL if (cx->runtime->needZealousGC()) @@ -411,7 +411,7 @@ TryNewGCThing(JSContext *cx, js::gc::AllocKind kind, size_t thingSize) kind == js::gc::FINALIZE_STRING || kind == js::gc::FINALIZE_SHORT_STRING); #endif JS_ASSERT(!cx->runtime->gcRunning); - JS_ASSERT(!JS_THREAD_DATA(cx)->noGCOrAllocationCheck); + JS_ASSERT(!cx->runtime->noGCOrAllocationCheck); #ifdef JS_GC_ZEAL if (cx->runtime->needZealousGC()) diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index 04cdf1bd6d98..1b50998863e9 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -1258,13 +1258,13 @@ inline InterpreterFrames::InterpreterFrames(JSContext *cx, FrameRegs *regs, const InterruptEnablerBase &enabler) : context(cx), regs(regs), enabler(enabler) { - older = JS_THREAD_DATA(cx)->interpreterFrames; - JS_THREAD_DATA(cx)->interpreterFrames = this; + older = cx->runtime->interpreterFrames; + cx->runtime->interpreterFrames = this; } inline InterpreterFrames::~InterpreterFrames() { - JS_THREAD_DATA(context)->interpreterFrames = older; + context->runtime->interpreterFrames = older; } #if defined(DEBUG) && !defined(JS_THREADSAFE) @@ -1554,7 +1554,7 @@ js::Interpret(JSContext *cx, StackFrame *entryFrame, InterpMode interpMode) */ #define CHECK_BRANCH() \ JS_BEGIN_MACRO \ - if (JS_THREAD_DATA(cx)->interruptFlags && !js_HandleExecutionInterrupt(cx)) \ + if (cx->runtime->interrupt && !js_HandleExecutionInterrupt(cx)) \ goto error; \ JS_END_MACRO diff --git a/js/src/jslock.cpp b/js/src/jslock.cpp deleted file mode 100644 index f4acb8f973fc..000000000000 --- a/js/src/jslock.cpp +++ /dev/null @@ -1,743 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifdef JS_THREADSAFE - -/* - * JS locking stubs. - */ -#include -#include - -#ifdef XP_WIN -# include "jswin.h" -#else -# include -#endif - -#include "jspubtd.h" -#include "jstypes.h" -#include "jsutil.h" -#include "jsstdint.h" -#include "jscntxt.h" -#include "jsgc.h" -#include "jslock.h" -#include "jsscope.h" -#include "jsstr.h" - -#include "jsscopeinlines.h" - -using namespace js; - -#define ReadWord(W) (W) - -#if !defined(__GNUC__) -# define __asm__ asm -# define __volatile__ volatile -#endif - -/* Implement NativeCompareAndSwap. */ - -#if defined(_MSC_VER) && defined(_M_IX86) -// TODO: Bug 716204 - undo this pragma. -#pragma warning( disable : 4035 ) -JS_BEGIN_EXTERN_C -extern long __cdecl -_InterlockedCompareExchange(long *volatile dest, long exchange, long comp); -JS_END_EXTERN_C -#pragma intrinsic(_InterlockedCompareExchange) -JS_STATIC_ASSERT(sizeof(intptr_t) == sizeof(long)); - -static JS_ALWAYS_INLINE int -NativeCompareAndSwapHelper(volatile intptr_t *w, intptr_t ov, intptr_t nv) -{ - _InterlockedCompareExchange((long*) w, nv, ov); - __asm { - sete al - } -} - -static JS_ALWAYS_INLINE int -NativeCompareAndSwap(volatile intptr_t *w, intptr_t ov, intptr_t nv) -{ - return (NativeCompareAndSwapHelper(w, ov, nv) & 1); -} - -#elif defined(_MSC_VER) && (defined(_M_AMD64) || defined(_M_X64)) -/* - * Compared with the _InterlockedCompareExchange in the 32 bit case above MSVC - * declares _InterlockedCompareExchange64 through . - */ -#pragma intrinsic(_InterlockedCompareExchange64) -JS_STATIC_ASSERT(sizeof(intptr_t) == sizeof(long long)); - -static JS_ALWAYS_INLINE int -NativeCompareAndSwap(volatile intptr_t *w, intptr_t ov, intptr_t nv) -{ - return _InterlockedCompareExchange64((long long *volatile)w, nv, ov) == ov; -} - -#elif defined(XP_MACOSX) || defined(DARWIN) - -#include - -static JS_ALWAYS_INLINE int -NativeCompareAndSwap(volatile intptr_t *w, intptr_t ov, intptr_t nv) -{ - /* Details on these functions available in the manpage for atomic */ - return OSAtomicCompareAndSwapPtrBarrier(reinterpret_cast(ov), - reinterpret_cast(nv), - reinterpret_cast(w)); -} - -#elif defined(__i386) && (defined(__GNUC__) || defined(__SUNPRO_CC)) - -/* Note: This fails on 386 cpus, cmpxchgl is a >= 486 instruction */ -static JS_ALWAYS_INLINE int -NativeCompareAndSwap(volatile intptr_t *w, intptr_t ov, intptr_t nv) -{ - unsigned int res; - - __asm__ __volatile__ ( - "lock\n" - "cmpxchgl %2, (%1)\n" - "sete %%al\n" - "andl $1, %%eax\n" - : "=a" (res) -#ifdef __SUNPRO_CC -/* Different code for Sun Studio because of a bug of SS12U1 */ - : "c" (w), "d" (nv), "a" (ov) -#else - : "r" (w), "r" (nv), "a" (ov) -#endif - : "cc", "memory"); - return (int)res; -} -#elif defined(__x86_64) && (defined(__GNUC__) || defined(__SUNPRO_CC)) - -static JS_ALWAYS_INLINE int -NativeCompareAndSwap(volatile intptr_t *w, intptr_t ov, intptr_t nv) -{ - unsigned int res; - - __asm__ __volatile__ ( - "lock\n" - "cmpxchgq %2, (%1)\n" - "sete %%al\n" - "movzbl %%al, %%eax\n" - : "=a" (res) - : "r" (w), "r" (nv), "a" (ov) - : "cc", "memory"); - return (int)res; -} - -#elif defined(__sparc) -#if defined(__GNUC__) - -static JS_ALWAYS_INLINE int -NativeCompareAndSwap(volatile intptr_t *w, intptr_t ov, intptr_t nv) -{ - unsigned int res; - - __asm__ __volatile__ ( - "membar #StoreLoad | #LoadLoad\n" -#if JS_BITS_PER_WORD == 32 - "cas [%1],%2,%3\n" -#else - "casx [%1],%2,%3\n" -#endif - "membar #StoreLoad | #LoadLoad\n" - "cmp %2,%3\n" - "be,a 1f\n" - "mov 1,%0\n" - "mov 0,%0\n" - "1:" - : "=r" (res) - : "r" (w), "r" (ov), "r" (nv)); - return (int)res; -} - -#elif defined(__SUNPRO_CC) - -/* Implementation in lock_sparc*.il */ -extern "C" int -NativeCompareAndSwap(volatile intptr_t *w, intptr_t ov, intptr_t nv); - -#endif - -#elif defined(AIX) - -#include - -static JS_ALWAYS_INLINE int -NativeCompareAndSwap(volatile intptr_t *w, intptr_t ov, intptr_t nv) -{ - int res; - JS_STATIC_ASSERT(sizeof(intptr_t) == sizeof(long)); - - res = compare_and_swaplp((atomic_l)w, &ov, nv); - if (res) - __asm__("isync"); - return res; -} - -#elif defined(__arm__) && defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4) - -JS_STATIC_ASSERT(sizeof(intptr_t) == sizeof(int)); - -static JS_ALWAYS_INLINE int -NativeCompareAndSwap(volatile intptr_t *w, intptr_t ov, intptr_t nv) -{ - return __sync_bool_compare_and_swap(w, ov, nv); -} - -#elif defined(USE_ARM_KUSER) - -/* See https://bugzilla.mozilla.org/show_bug.cgi?id=429387 for a - * description of this ABI; this is a function provided at a fixed - * location by the kernel in the memory space of each process. - */ -typedef int (__kernel_cmpxchg_t)(int oldval, int newval, volatile int *ptr); -#define __kernel_cmpxchg (*(__kernel_cmpxchg_t *)0xffff0fc0) - -JS_STATIC_ASSERT(sizeof(intptr_t) == sizeof(int)); - -static JS_ALWAYS_INLINE int -NativeCompareAndSwap(volatile intptr_t *w, intptr_t ov, intptr_t nv) -{ - volatile int *vp = (volatile int *) w; - PRInt32 failed = 1; - - /* Loop until a __kernel_cmpxchg succeeds. See bug 446169 */ - do { - failed = __kernel_cmpxchg(ov, nv, vp); - } while (failed && *vp == ov); - return !failed; -} - -#elif JS_HAS_NATIVE_COMPARE_AND_SWAP - -#error "JS_HAS_NATIVE_COMPARE_AND_SWAP should be 0 if your platform lacks a compare-and-swap instruction." - -#endif /* arch-tests */ - -#if JS_HAS_NATIVE_COMPARE_AND_SWAP - -JSBool -js_CompareAndSwap(volatile intptr_t *w, intptr_t ov, intptr_t nv) -{ - return !!NativeCompareAndSwap(w, ov, nv); -} - -#elif defined(NSPR_LOCK) - -# ifdef __GNUC__ -# warning "js_CompareAndSwap is implemented using NSPR lock" -# endif - -JSBool -js_CompareAndSwap(volatile intptr_t *w, intptr_t ov, intptr_t nv) -{ - int result; - static PRLock *CompareAndSwapLock = JS_NEW_LOCK(); - - JS_ACQUIRE_LOCK(CompareAndSwapLock); - result = (*w == ov); - if (result) - *w = nv; - JS_RELEASE_LOCK(CompareAndSwapLock); - return result; -} - -#else /* !defined(NSPR_LOCK) */ - -#error "NSPR_LOCK should be on when the platform lacks native compare-and-swap." - -#endif - -void -js_AtomicSetMask(volatile intptr_t *w, intptr_t mask) -{ - intptr_t ov, nv; - - do { - ov = *w; - nv = ov | mask; - } while (!js_CompareAndSwap(w, ov, nv)); -} - -void -js_AtomicClearMask(volatile intptr_t *w, intptr_t mask) -{ - intptr_t ov, nv; - - do { - ov = *w; - nv = ov & ~mask; - } while (!js_CompareAndSwap(w, ov, nv)); -} - -unsigned -js_GetCPUCount() -{ - static unsigned ncpus = 0; - if (ncpus == 0) { -# ifdef XP_WIN - SYSTEM_INFO sysinfo; - GetSystemInfo(&sysinfo); - ncpus = unsigned(sysinfo.dwNumberOfProcessors); -# else - long n = sysconf(_SC_NPROCESSORS_ONLN); - ncpus = (n > 0) ? unsigned(n) : 1; -# endif - } - return ncpus; -} - -#ifndef NSPR_LOCK - -struct JSFatLock { - int susp; - PRLock *slock; - PRCondVar *svar; - JSFatLock *next; - JSFatLock **prevp; -}; - -typedef struct JSFatLockTable { - JSFatLock *free_; - JSFatLock *taken; -} JSFatLockTable; - -#define GLOBAL_LOCK_INDEX(id) (((uint32_t)(uintptr_t)(id)>>2) & global_locks_mask) - -static void -js_Dequeue(JSThinLock *); - -static PRLock **global_locks; -static uint32_t global_lock_count = 1; -static uint32_t global_locks_log2 = 0; -static uint32_t global_locks_mask = 0; - -static void -js_LockGlobal(void *id) -{ - uint32_t i = GLOBAL_LOCK_INDEX(id); - PR_Lock(global_locks[i]); -} - -static void -js_UnlockGlobal(void *id) -{ - uint32_t i = GLOBAL_LOCK_INDEX(id); - PR_Unlock(global_locks[i]); -} - -#endif /* !NSPR_LOCK */ - -void -js_InitLock(JSThinLock *tl) -{ -#ifdef NSPR_LOCK - tl->owner = 0; - tl->fat = (JSFatLock*)JS_NEW_LOCK(); -#else - PodZero(tl); -#endif -} - -void -js_FinishLock(JSThinLock *tl) -{ -#ifdef NSPR_LOCK - tl->owner = 0xdeadbeef; - if (tl->fat) - JS_DESTROY_LOCK(((JSLock*)tl->fat)); -#else - JS_ASSERT(tl->owner == 0); - JS_ASSERT(tl->fat == NULL); -#endif -} - -#ifndef NSPR_LOCK - -static JSFatLock * -NewFatlock() -{ - JSFatLock *fl = (JSFatLock *) OffTheBooks::malloc_(sizeof(JSFatLock)); /* for now */ - if (!fl) return NULL; - fl->susp = 0; - fl->next = NULL; - fl->prevp = NULL; - fl->slock = PR_NewLock(); - fl->svar = PR_NewCondVar(fl->slock); - return fl; -} - -static void -DestroyFatlock(JSFatLock *fl) -{ - PR_DestroyLock(fl->slock); - PR_DestroyCondVar(fl->svar); - UnwantedForeground::free_(fl); -} - -static JSFatLock * -ListOfFatlocks(int listc) -{ - JSFatLock *m; - JSFatLock *m0; - int i; - - JS_ASSERT(listc>0); - m0 = m = NewFatlock(); - for (i=1; inext = NewFatlock(); - m = m->next; - } - return m0; -} - -static void -DeleteListOfFatlocks(JSFatLock *m) -{ - JSFatLock *m0; - for (; m; m=m0) { - m0 = m->next; - DestroyFatlock(m); - } -} - -static JSFatLockTable *fl_list_table = NULL; -static uint32_t fl_list_table_len = 0; -static uint32_t fl_list_chunk_len = 0; - -static JSFatLock * -GetFatlock(void *id) -{ - JSFatLock *m; - - uint32_t i = GLOBAL_LOCK_INDEX(id); - if (fl_list_table[i].free_ == NULL) { -#ifdef DEBUG - if (fl_list_table[i].taken) - printf("Ran out of fat locks!\n"); -#endif - fl_list_table[i].free_ = ListOfFatlocks(fl_list_chunk_len); - } - m = fl_list_table[i].free_; - fl_list_table[i].free_ = m->next; - m->susp = 0; - m->next = fl_list_table[i].taken; - m->prevp = &fl_list_table[i].taken; - if (fl_list_table[i].taken) - fl_list_table[i].taken->prevp = &m->next; - fl_list_table[i].taken = m; - return m; -} - -static void -PutFatlock(JSFatLock *m, void *id) -{ - uint32_t i; - if (m == NULL) - return; - - /* Unlink m from fl_list_table[i].taken. */ - *m->prevp = m->next; - if (m->next) - m->next->prevp = m->prevp; - - /* Insert m in fl_list_table[i].free. */ - i = GLOBAL_LOCK_INDEX(id); - m->next = fl_list_table[i].free_; - fl_list_table[i].free_ = m; -} - -#endif /* !NSPR_LOCK */ - -JSBool -js_SetupLocks(int listc, int globc) -{ -#ifndef NSPR_LOCK - uint32_t i; - - if (global_locks) - return JS_TRUE; -#ifdef DEBUG - if (listc > 10000 || listc < 0) /* listc == fat lock list chunk length */ - printf("Bad number %d in js_SetupLocks()!\n", listc); - if (globc > 100 || globc < 0) /* globc == number of global locks */ - printf("Bad number %d in js_SetupLocks()!\n", listc); -#endif - global_locks_log2 = JS_CEILING_LOG2W(globc); - global_locks_mask = JS_BITMASK(global_locks_log2); - global_lock_count = JS_BIT(global_locks_log2); - global_locks = (PRLock **) OffTheBooks::malloc_(global_lock_count * sizeof(PRLock*)); - if (!global_locks) - return JS_FALSE; - for (i = 0; i < global_lock_count; i++) { - global_locks[i] = PR_NewLock(); - if (!global_locks[i]) { - global_lock_count = i; - js_CleanupLocks(); - return JS_FALSE; - } - } - fl_list_table = (JSFatLockTable *) OffTheBooks::malloc_(i * sizeof(JSFatLockTable)); - if (!fl_list_table) { - js_CleanupLocks(); - return JS_FALSE; - } - fl_list_table_len = global_lock_count; - for (i = 0; i < global_lock_count; i++) - fl_list_table[i].free_ = fl_list_table[i].taken = NULL; - fl_list_chunk_len = listc; -#endif /* !NSPR_LOCK */ - return JS_TRUE; -} - -void -js_CleanupLocks() -{ -#ifndef NSPR_LOCK - uint32_t i; - - if (global_locks) { - for (i = 0; i < global_lock_count; i++) - PR_DestroyLock(global_locks[i]); - UnwantedForeground::free_(global_locks); - global_locks = NULL; - global_lock_count = 1; - global_locks_log2 = 0; - global_locks_mask = 0; - } - if (fl_list_table) { - for (i = 0; i < fl_list_table_len; i++) { - DeleteListOfFatlocks(fl_list_table[i].free_); - fl_list_table[i].free_ = NULL; - DeleteListOfFatlocks(fl_list_table[i].taken); - fl_list_table[i].taken = NULL; - } - UnwantedForeground::free_(fl_list_table); - fl_list_table = NULL; - fl_list_table_len = 0; - } -#endif /* !NSPR_LOCK */ -} - -#ifdef NSPR_LOCK - -static JS_ALWAYS_INLINE void -ThinLock(JSThinLock *tl, intptr_t me) -{ - JS_ACQUIRE_LOCK((JSLock *) tl->fat); - tl->owner = me; -} - -static JS_ALWAYS_INLINE void -ThinUnlock(JSThinLock *tl, intptr_t /*me*/) -{ - tl->owner = 0; - JS_RELEASE_LOCK((JSLock *) tl->fat); -} - -#else - -/* - * Fast locking and unlocking is implemented by delaying the allocation of a - * system lock (fat lock) until contention. As long as a locking thread A - * runs uncontended, the lock is represented solely by storing A's identity in - * the object being locked. - * - * If another thread B tries to lock the object currently locked by A, B is - * enqueued into a fat lock structure (which might have to be allocated and - * pointed to by the object), and suspended using NSPR conditional variables - * (wait). A wait bit (Bacon bit) is set in the lock word of the object, - * signalling to A that when releasing the lock, B must be dequeued and - * notified. - * - * The basic operation of the locking primitives (js_Lock, js_Unlock, - * js_Enqueue, and js_Dequeue) is compare-and-swap. Hence, when locking into - * the word pointed at by p, compare-and-swap(p, 0, A) success implies that p - * is unlocked. Similarly, when unlocking p, if compare-and-swap(p, A, 0) - * succeeds this implies that p is uncontended (no one is waiting because the - * wait bit is not set). - * - * When dequeueing, the lock is released, and one of the threads suspended on - * the lock is notified. If other threads still are waiting, the wait bit is - * kept (in js_Enqueue), and if not, the fat lock is deallocated. - * - * The functions js_Enqueue, js_Dequeue, js_SuspendThread, and js_ResumeThread - * are serialized using a global lock. For scalability, a hashtable of global - * locks is used, which is indexed modulo the thin lock pointer. - */ - -/* - * Invariants: - * (i) global lock is held - * (ii) fl->susp >= 0 - */ -static int -js_SuspendThread(JSThinLock *tl) -{ - JSFatLock *fl; - if (tl->fat == NULL) - fl = tl->fat = GetFatlock(tl); - else - fl = tl->fat; - JS_ASSERT(fl->susp >= 0); - fl->susp++; - PR_Lock(fl->slock); - js_UnlockGlobal(tl); - DebugOnly stat = PR_WaitCondVar(fl->svar, PR_INTERVAL_NO_TIMEOUT); - JS_ASSERT(stat != PR_FAILURE); - PR_Unlock(fl->slock); - js_LockGlobal(tl); - fl->susp--; - if (fl->susp == 0) { - PutFatlock(fl, tl); - tl->fat = NULL; - } - return tl->fat == NULL; -} - -/* - * (i) global lock is held - * (ii) fl->susp > 0 - */ -static void -js_ResumeThread(JSThinLock *tl) -{ - JSFatLock *fl = tl->fat; - JS_ASSERT(fl != NULL); - JS_ASSERT(fl->susp > 0); - PR_Lock(fl->slock); - js_UnlockGlobal(tl); - DebugOnly stat = PR_NotifyCondVar(fl->svar); - JS_ASSERT(stat != PR_FAILURE); - PR_Unlock(fl->slock); -} - -static void -js_Enqueue(JSThinLock *tl, intptr_t me) -{ - intptr_t o, n; - - js_LockGlobal(tl); - for (;;) { - o = ReadWord(tl->owner); - n = Thin_SetWait(o); - if (o != 0 && NativeCompareAndSwap(&tl->owner, o, n)) { - if (js_SuspendThread(tl)) - me = Thin_RemoveWait(me); - else - me = Thin_SetWait(me); - } - else if (NativeCompareAndSwap(&tl->owner, 0, me)) { - js_UnlockGlobal(tl); - return; - } - } -} - -static void -js_Dequeue(JSThinLock *tl) -{ - intptr_t o; - - js_LockGlobal(tl); - o = ReadWord(tl->owner); - JS_ASSERT(Thin_GetWait(o) != 0); - JS_ASSERT(tl->fat != NULL); - if (!NativeCompareAndSwap(&tl->owner, o, 0)) /* release it */ - JS_ASSERT(0); - js_ResumeThread(tl); -} - -static JS_ALWAYS_INLINE void -ThinLock(JSThinLock *tl, intptr_t me) -{ - JS_ASSERT(CURRENT_THREAD_IS_ME(me)); - if (NativeCompareAndSwap(&tl->owner, 0, me)) - return; - if (Thin_RemoveWait(ReadWord(tl->owner)) != me) - js_Enqueue(tl, me); -#ifdef DEBUG - else - JS_ASSERT(0); -#endif -} - -static JS_ALWAYS_INLINE void -ThinUnlock(JSThinLock *tl, intptr_t me) -{ - JS_ASSERT(CURRENT_THREAD_IS_ME(me)); - - /* - * Since we can race with the NativeCompareAndSwap in js_Enqueue, we need - * to use a C_A_S here as well -- Arjan van de Ven 30/1/08 - */ - if (NativeCompareAndSwap(&tl->owner, me, 0)) - return; - - JS_ASSERT(Thin_GetWait(tl->owner)); - if (Thin_RemoveWait(ReadWord(tl->owner)) == me) - js_Dequeue(tl); -#ifdef DEBUG - else - JS_ASSERT(0); /* unbalanced unlock */ -#endif -} - -#endif /* !NSPR_LOCK */ - -void -js_Lock(JSContext *cx, JSThinLock *tl) -{ - ThinLock(tl, CX_THINLOCK_ID(cx)); -} - -void -js_Unlock(JSContext *cx, JSThinLock *tl) -{ - ThinUnlock(tl, CX_THINLOCK_ID(cx)); -} - -#endif /* JS_THREADSAFE */ diff --git a/js/src/jslock.h b/js/src/jslock.h index 62a326d8f1ca..adbddd9d2ba1 100644 --- a/js/src/jslock.h +++ b/js/src/jslock.h @@ -39,185 +39,34 @@ #ifndef jslock_h__ #define jslock_h__ -#include "jstypes.h" #include "jsapi.h" -#include "jsprvtd.h" #ifdef JS_THREADSAFE + # include "pratom.h" # include "prlock.h" # include "prcvar.h" # include "prthread.h" # include "prinit.h" -#endif -#ifdef JS_THREADSAFE +# define JS_ATOMIC_INCREMENT(p) PR_ATOMIC_INCREMENT((PRInt32 *)(p)) +# define JS_ATOMIC_DECREMENT(p) PR_ATOMIC_DECREMENT((PRInt32 *)(p)) +# define JS_ATOMIC_ADD(p,v) PR_ATOMIC_ADD((PRInt32 *)(p), (PRInt32)(v)) +# define JS_ATOMIC_SET(p,v) PR_ATOMIC_SET((PRInt32 *)(p), (PRInt32)(v)) -#if (defined(_WIN32) && defined(_M_IX86)) || \ - (defined(_WIN64) && (defined(_M_AMD64) || defined(_M_X64))) || \ - (defined(__i386) && (defined(__GNUC__) || defined(__SUNPRO_CC))) || \ - (defined(__x86_64) && (defined(__GNUC__) || defined(__SUNPRO_CC))) || \ - (defined(__sparc) && (defined(__GNUC__) || defined(__SUNPRO_CC))) || \ - (defined(__arm__) && defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4)) || \ - defined(AIX) || \ - defined(USE_ARM_KUSER) -# define JS_HAS_NATIVE_COMPARE_AND_SWAP 1 -#else -# define JS_HAS_NATIVE_COMPARE_AND_SWAP 0 -#endif +#else /* JS_THREADSAFE */ -#if defined(JS_USE_ONLY_NSPR_LOCKS) || !JS_HAS_NATIVE_COMPARE_AND_SWAP -# define NSPR_LOCK 1 -#else -# undef NSPR_LOCK -#endif +# define JS_ATOMIC_INCREMENT(p) (++*(p)) +# define JS_ATOMIC_DECREMENT(p) (--*(p)) +# define JS_ATOMIC_ADD(p,v) (*(p) += (v)) +# define JS_ATOMIC_SET(p,v) (*(p) = (v)) -#define Thin_GetWait(W) ((intptr_t)(W) & 0x1) -#define Thin_SetWait(W) ((intptr_t)(W) | 0x1) -#define Thin_RemoveWait(W) ((intptr_t)(W) & ~0x1) - -typedef struct JSFatLock JSFatLock; - -typedef struct JSThinLock { - intptr_t owner; - JSFatLock *fat; -} JSThinLock; - -#define CX_THINLOCK_ID(cx) ((intptr_t)(cx)->thread()) -#define CURRENT_THREAD_IS_ME(me) (((JSThread *)me)->id == js_CurrentThreadId()) - -typedef PRLock JSLock; - -/* - * Atomic increment and decrement for a reference counter, given jsrefcount *p. - * NB: jsrefcount is int32, aka PRInt32, so that pratom.h functions work. - */ -#define JS_ATOMIC_INCREMENT(p) PR_ATOMIC_INCREMENT((PRInt32 *)(p)) -#define JS_ATOMIC_DECREMENT(p) PR_ATOMIC_DECREMENT((PRInt32 *)(p)) -#define JS_ATOMIC_ADD(p,v) PR_ATOMIC_ADD((PRInt32 *)(p), (PRInt32)(v)) -#define JS_ATOMIC_SET(p,v) PR_ATOMIC_SET((PRInt32 *)(p), (PRInt32)(v)) - -#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) -#define JS_RELEASE_LOCK(l) PR_Unlock(l) - -#define JS_NEW_CONDVAR(l) PR_NewCondVar(l) -#define JS_DESTROY_CONDVAR(cv) PR_DestroyCondVar(cv) -#define JS_WAIT_CONDVAR(cv,to) PR_WaitCondVar(cv,to) -#define JS_NO_TIMEOUT PR_INTERVAL_NO_TIMEOUT -#define JS_NOTIFY_CONDVAR(cv) PR_NotifyCondVar(cv) -#define JS_NOTIFY_ALL_CONDVAR(cv) PR_NotifyAllCondVar(cv) - -#define JS_LOCK(cx, tl) js_Lock(cx, tl) -#define JS_UNLOCK(cx, tl) js_Unlock(cx, tl) - -extern void js_Lock(JSContext *cx, JSThinLock *tl); -extern void js_Unlock(JSContext *cx, JSThinLock *tl); -extern int js_SetupLocks(int,int); -extern void js_CleanupLocks(); -extern void js_InitLock(JSThinLock *); -extern void js_FinishLock(JSThinLock *); - -#else /* !JS_THREADSAFE */ - -#define JS_ATOMIC_INCREMENT(p) (++*(p)) -#define JS_ATOMIC_DECREMENT(p) (--*(p)) -#define JS_ATOMIC_ADD(p,v) (*(p) += (v)) -#define JS_ATOMIC_SET(p,v) (*(p) = (v)) - -#define js_CurrentThreadId() ((void*)NULL) -#define JS_NEW_LOCK() NULL -#define JS_DESTROY_LOCK(l) ((void)0) -#define JS_ACQUIRE_LOCK(l) ((void)0) -#define JS_RELEASE_LOCK(l) ((void)0) -#define JS_LOCK(cx, tl) ((void)0) -#define JS_UNLOCK(cx, tl) ((void)0) - -#define JS_NEW_CONDVAR(l) NULL -#define JS_DESTROY_CONDVAR(cv) ((void)0) -#define JS_WAIT_CONDVAR(cv,to) ((void)0) -#define JS_NOTIFY_CONDVAR(cv) ((void)0) -#define JS_NOTIFY_ALL_CONDVAR(cv) ((void)0) - -#endif /* !JS_THREADSAFE */ - -#define JS_LOCK_GC(rt) JS_ACQUIRE_LOCK((rt)->gcLock) -#define JS_UNLOCK_GC(rt) JS_RELEASE_LOCK((rt)->gcLock) -#define JS_AWAIT_GC_DONE(rt) JS_WAIT_CONDVAR((rt)->gcDone, JS_NO_TIMEOUT) -#define JS_NOTIFY_GC_DONE(rt) JS_NOTIFY_ALL_CONDVAR((rt)->gcDone) -#define JS_AWAIT_REQUEST_DONE(rt) JS_WAIT_CONDVAR((rt)->requestDone, \ - JS_NO_TIMEOUT) -#define JS_NOTIFY_REQUEST_DONE(rt) JS_NOTIFY_CONDVAR((rt)->requestDone) - -#ifndef JS_SET_OBJ_INFO -#define JS_SET_OBJ_INFO(obj,f,l) ((void)0) -#endif -#ifndef JS_SET_TITLE_INFO -#define JS_SET_TITLE_INFO(title,f,l) ((void)0) -#endif - -#ifdef JS_THREADSAFE - -extern JSBool -js_CompareAndSwap(volatile intptr_t *w, intptr_t ov, intptr_t nv); - -/* Atomically bitwise-or the mask into the word *w using compare and swap. */ -extern void -js_AtomicSetMask(volatile intptr_t *w, intptr_t mask); - -/* - * Atomically bitwise-and the complement of the mask into the word *w using - * compare and swap. - */ -extern void -js_AtomicClearMask(volatile intptr_t *w, intptr_t mask); - -#define JS_ATOMIC_SET_MASK(w, mask) js_AtomicSetMask(w, mask) -#define JS_ATOMIC_CLEAR_MASK(w, mask) js_AtomicClearMask(w, mask) - -extern unsigned -js_GetCPUCount(); - -#else - -static inline JSBool -js_CompareAndSwap(intptr_t *w, intptr_t ov, intptr_t nv) -{ - return (*w == ov) ? *w = nv, JS_TRUE : JS_FALSE; -} - -#define JS_ATOMIC_SET_MASK(w, mask) (*(w) |= (mask)) -#define JS_ATOMIC_CLEAR_MASK(w, mask) (*(w) &= ~(mask)) - -static inline unsigned -js_GetCPUCount() -{ - return 1; -} - -#endif - -#ifdef __cplusplus +#endif /* JS_THREADSAFE */ namespace js { -#ifdef JS_THREADSAFE -class AutoLock { - private: - JSLock *lock; - - public: - AutoLock(JSLock *lock) : lock(lock) { JS_ACQUIRE_LOCK(lock); } - ~AutoLock() { JS_RELEASE_LOCK(lock); } -}; -# define JS_AUTO_LOCK_GUARD(name, l) AutoLock name((l)); -#else -# define JS_AUTO_LOCK_GUARD(name, l) -#endif - -class AutoAtomicIncrement { +class AutoAtomicIncrement +{ int32_t *p; JS_DECL_USE_GUARD_OBJECT_NOTIFIER @@ -233,8 +82,6 @@ class AutoAtomicIncrement { } }; -} /* namespace js */ - -#endif +} /* namespace js */ #endif /* jslock_h___ */ diff --git a/js/src/jslocko.asm b/js/src/jslocko.asm deleted file mode 100644 index cf88609cd8d3..000000000000 --- a/js/src/jslocko.asm +++ /dev/null @@ -1,60 +0,0 @@ -; -*- Mode: asm; tab-width: 8; c-basic-offset: 4 -*- - -; ***** BEGIN LICENSE BLOCK ***** -; Version: MPL 1.1/GPL 2.0/LGPL 2.1 -; -; The contents of this file are subject to the Mozilla Public License Version -; 1.1 (the "License"); you may not use this file except in compliance with -; the License. You may obtain a copy of the License at -; http://www.mozilla.org/MPL/ -; -; Software distributed under the License is distributed on an "AS IS" basis, -; WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -; for the specific language governing rights and limitations under the -; License. -; -; The Original Code is an OS/2 implementation of js_CompareAndSwap in assembly. -; -; The Initial Developer of the Original Code is -; IBM Corporation. -; Portions created by the Initial Developer are Copyright (C) 2001 -; the Initial Developer. All Rights Reserved. -; -; Contributor(s): -; -; Alternatively, the contents of this file may be used under the terms of -; either the GNU General Public License Version 2 or later (the "GPL"), or -; the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -; in which case the provisions of the GPL or the LGPL are applicable instead -; of those above. If you wish to allow use of your version of this file only -; under the terms of either the GPL or the LGPL, and not to allow others to -; use your version of this file under the terms of the MPL, indicate your -; decision by deleting the provisions above and replace them with the notice -; and other provisions required by the GPL or the LGPL. If you do not delete -; the provisions above, a recipient may use your version of this file under -; the terms of any one of the MPL, the GPL or the LGPL. -; -; ***** END LICENSE BLOCK ***** - - .486P - .MODEL FLAT, OPTLINK - .STACK - - .CODE - -;;;--------------------------------------------------------------------- -;;; int _Optlink js_CompareAndSwap(intptr_t *w, intptr_t ov, intptr_t nv) -;;;--------------------------------------------------------------------- -js_CompareAndSwap PROC OPTLINK EXPORT - push ebx - mov ebx, eax - mov eax, edx - mov edx, ebx - lock cmpxchg [ebx], ecx - sete al - and eax, 1h - pop ebx - ret -js_CompareAndSwap endp - - END diff --git a/js/src/jsnum.cpp b/js/src/jsnum.cpp index d61071f9bddc..b7457a164165 100644 --- a/js/src/jsnum.cpp +++ b/js/src/jsnum.cpp @@ -108,7 +108,7 @@ ComputeAccurateDecimalInteger(JSContext *cx, const jschar *start, const jschar * char *estr; int err = 0; - *dp = js_strtod_harder(JS_THREAD_DATA(cx)->dtoaState, cstr, &estr, &err); + *dp = js_strtod_harder(cx->runtime->dtoaState, cstr, &estr, &err); if (err == JS_DTOA_ENOMEM) { JS_ReportOutOfMemory(cx); cx->free_(cstr); @@ -816,7 +816,7 @@ num_to(JSContext *cx, Native native, JSDToStrMode zeroArgMode, JSDToStrMode oneA } } - numStr = js_dtostr(JS_THREAD_DATA(cx)->dtoaState, buf, sizeof buf, + numStr = js_dtostr(cx->runtime->dtoaState, buf, sizeof buf, oneArgMode, (jsint)precision + precisionOffset, d); if (!numStr) { JS_ReportOutOfMemory(cx); @@ -1087,10 +1087,10 @@ FracNumberToCString(JSContext *cx, ToCStringBuf *cbuf, jsdouble d, jsint base = */ numStr = v8::internal::DoubleToCString(d, cbuf->sbuf, cbuf->sbufSize); if (!numStr) - numStr = js_dtostr(JS_THREAD_DATA(cx)->dtoaState, cbuf->sbuf, cbuf->sbufSize, + numStr = js_dtostr(cx->runtime->dtoaState, cbuf->sbuf, cbuf->sbufSize, DTOSTR_STANDARD, 0, d); } else { - numStr = cbuf->dbuf = js_dtobasestr(JS_THREAD_DATA(cx)->dtoaState, base, d); + numStr = cbuf->dbuf = js_dtobasestr(cx->runtime->dtoaState, base, d); } return numStr; } @@ -1421,7 +1421,7 @@ js_strtod(JSContext *cx, const jschar *s, const jschar *send, estr = istr + 8; } else { int err; - d = js_strtod_harder(JS_THREAD_DATA(cx)->dtoaState, cstr, &estr, &err); + d = js_strtod_harder(cx->runtime->dtoaState, cstr, &estr, &err); if (d == HUGE_VAL) d = js_PositiveInfinity; else if (d == -HUGE_VAL) diff --git a/js/src/jsobj.h b/js/src/jsobj.h index 79fd36d64779..97ff73b8be2f 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -1519,21 +1519,6 @@ struct JSObject_Slots16 : JSObject { js::Value fslots[16]; }; #define JSSLOT_FREE(clasp) JSCLASS_RESERVED_SLOTS(clasp) -#ifdef JS_THREADSAFE - -/* - * The GC runs only when all threads except the one on which the GC is active - * are suspended at GC-safe points, so calling obj->getSlot() from the GC's - * thread is safe when rt->gcRunning is set. See jsgc.cpp for details. - */ -#define THREAD_IS_RUNNING_GC(rt, thread) \ - ((rt)->gcRunning && (rt)->gcThread == (thread)) - -#define CX_THREAD_IS_RUNNING_GC(cx) \ - THREAD_IS_RUNNING_GC((cx)->runtime, (cx)->thread) - -#endif /* JS_THREADSAFE */ - class JSValueArray { public: jsval *array; diff --git a/js/src/jsopcode.cpp b/js/src/jsopcode.cpp index fddf5702b99f..bbe6f6d505fd 100644 --- a/js/src/jsopcode.cpp +++ b/js/src/jsopcode.cpp @@ -462,7 +462,7 @@ ToDisassemblySource(JSContext *cx, jsval v, JSAutoByteString *bytes) return true; } - if (cx->runtime->gcRunning || JS_THREAD_DATA(cx)->noGCOrAllocationCheck) { + if (cx->runtime->gcRunning || cx->runtime->noGCOrAllocationCheck) { char *source = JS_sprintf_append(NULL, ""); if (!source) return false; diff --git a/js/src/jsproxy.cpp b/js/src/jsproxy.cpp index 4a461875858d..1af029d0cafd 100644 --- a/js/src/jsproxy.cpp +++ b/js/src/jsproxy.cpp @@ -83,7 +83,7 @@ GetFunctionProxyConstruct(JSObject *proxy) static bool OperationInProgress(JSContext *cx, JSObject *proxy) { - PendingProxyOperation *op = JS_THREAD_DATA(cx)->pendingProxyOperation; + PendingProxyOperation *op = cx->runtime->pendingProxyOperation; while (op) { if (op->object == proxy) return true; @@ -710,18 +710,18 @@ ScriptedProxyHandler::iterate(JSContext *cx, JSObject *proxy, uintN flags, Value ScriptedProxyHandler ScriptedProxyHandler::singleton; class AutoPendingProxyOperation { - ThreadData *data; + JSRuntime *rt; PendingProxyOperation op; public: - AutoPendingProxyOperation(JSContext *cx, JSObject *proxy) : data(JS_THREAD_DATA(cx)) { - op.next = data->pendingProxyOperation; + AutoPendingProxyOperation(JSContext *cx, JSObject *proxy) : rt(cx->runtime) { + op.next = rt->pendingProxyOperation; op.object = proxy; - data->pendingProxyOperation = &op; + rt->pendingProxyOperation = &op; } ~AutoPendingProxyOperation() { - JS_ASSERT(data->pendingProxyOperation == &op); - data->pendingProxyOperation = op.next; + JS_ASSERT(rt->pendingProxyOperation == &op); + rt->pendingProxyOperation = op.next; } }; diff --git a/js/src/jsprvtd.h b/js/src/jsprvtd.h index eef713c4f09e..1c98443bafdf 100644 --- a/js/src/jsprvtd.h +++ b/js/src/jsprvtd.h @@ -86,7 +86,6 @@ typedef struct JSGenerator JSGenerator; typedef struct JSNativeEnumerator JSNativeEnumerator; typedef struct JSProperty JSProperty; typedef struct JSSharpObjectMap JSSharpObjectMap; -typedef struct JSThread JSThread; typedef struct JSTryNote JSTryNote; /* Friend "Advanced API" typedefs. */ diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index 4503baf0dc89..cb58c0faa0d9 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -781,7 +781,7 @@ JSScript::initCounts(JSContext *cx) /* Enable interrupts in any interpreter frames running on this script. */ InterpreterFrames *frames; - for (frames = JS_THREAD_DATA(cx)->interpreterFrames; frames; frames = frames->older) + for (frames = cx->runtime->interpreterFrames; frames; frames = frames->older) frames->enableInterruptsIfRunning(this); return true; @@ -1735,7 +1735,7 @@ JSScript::ensureHasDebug(JSContext *cx) * debug state is destroyed. */ InterpreterFrames *frames; - for (frames = JS_THREAD_DATA(cx)->interpreterFrames; frames; frames = frames->older) + for (frames = cx->runtime->interpreterFrames; frames; frames = frames->older) frames->enableInterruptsIfRunning(this); return true; diff --git a/js/src/lock_sparcv8plus.il b/js/src/lock_sparcv8plus.il deleted file mode 100644 index e7b55d5f6ec4..000000000000 --- a/js/src/lock_sparcv8plus.il +++ /dev/null @@ -1,84 +0,0 @@ -! -! ***** BEGIN LICENSE BLOCK ***** -! Version: MPL 1.1/GPL 2.0/LGPL 2.1 -! -! The contents of this file are subject to the Mozilla Public License Version -! 1.1 (the "License"); you may not use this file except in compliance with -! the License. You may obtain a copy of the License at -! http://www.mozilla.org/MPL/ -! -! Software distributed under the License is distributed on an "AS IS" basis, -! WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -! for the specific language governing rights and limitations under the -! License. -! -! The Original Code is Mozilla Communicator client code, released -! March 31, 1998. -! -! The Initial Developer of the Original Code is -! Netscape Communications Corporation. -! Portions created by the Initial Developer are Copyright (C) 1998-1999 -! the Initial Developer. All Rights Reserved. -! -! Contributor(s): -! -! Alternatively, the contents of this file may be used under the terms of -! either the GNU General Public License Version 2 or later (the "GPL"), or -! the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -! in which case the provisions of the GPL or the LGPL are applicable instead -! of those above. If you wish to allow use of your version of this file only -! under the terms of either the GPL or the LGPL, and not to allow others to -! use your version of this file under the terms of the MPL, indicate your -! decision by deleting the provisions above and replace them with the notice -! and other provisions required by the GPL or the LGPL. If you do not delete -! the provisions above, a recipient may use your version of this file under -! the terms of any one of the MPL, the GPL or the LGPL. -! -! ***** END LICENSE BLOCK ***** - -! -! atomic compare-and-swap routines for V8+ (ultrasparc) -! -! ====================================================================== -! -! Perform the sequence *a = b atomically with respect to previous value -! of a (a0). If *a==a0 then assign *a to b, all in one atomic operation. -! Returns 1 if assignment happened, and 0 otherwise. -! -! usage : old_val = compare_and_swap(address, oldval, newval) -! -! ----------------------- -! Note on REGISTER USAGE: -! as this is a LEAF procedure, a new stack frame is not created; -! we use the caller stack frame so what would normally be %i (input) -! registers are actually %o (output registers). Also, we must not -! overwrite the contents of %l (local) registers as they are not -! assumed to be volatile during calls. -! -! So, the registers used are: -! %o0 [input] - the address of the value to increment -! %o1 [input] - the old value to compare with -! %o2 [input] - the new value to set for [%o0] -! %o3 [local] - work register -! ----------------------- -! ====================================================================== -! -! v8plus - - .inline NativeCompareAndSwap,3 - - stbar - cas [%o0],%o1,%o2 ! compare *w with old value and set to new if equal - cmp %o1,%o2 ! did we succeed? - be,a 1f ! yes - mov 1,%o0 ! return true (annulled when no jump) - mov 0,%o0 ! return false -1: - - .end - -! -! end -! -! ====================================================================== -! diff --git a/js/src/lock_sparcv9.il b/js/src/lock_sparcv9.il deleted file mode 100644 index e87e065d76eb..000000000000 --- a/js/src/lock_sparcv9.il +++ /dev/null @@ -1,84 +0,0 @@ -! -! ***** BEGIN LICENSE BLOCK ***** -! Version: MPL 1.1/GPL 2.0/LGPL 2.1 -! -! The contents of this file are subject to the Mozilla Public License Version -! 1.1 (the "License"); you may not use this file except in compliance with -! the License. You may obtain a copy of the License at -! http://www.mozilla.org/MPL/ -! -! Software distributed under the License is distributed on an "AS IS" basis, -! WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -! for the specific language governing rights and limitations under the -! License. -! -! The Original Code is Mozilla Communicator client code, released -! March 31, 1998. -! -! The Initial Developer of the Original Code is -! Netscape Communications Corporation. -! Portions created by the Initial Developer are Copyright (C) 1998-1999 -! the Initial Developer. All Rights Reserved. -! -! Contributor(s): -! -! Alternatively, the contents of this file may be used under the terms of -! either the GNU General Public License Version 2 or later (the "GPL"), or -! the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -! in which case the provisions of the GPL or the LGPL are applicable instead -! of those above. If you wish to allow use of your version of this file only -! under the terms of either the GPL or the LGPL, and not to allow others to -! use your version of this file under the terms of the MPL, indicate your -! decision by deleting the provisions above and replace them with the notice -! and other provisions required by the GPL or the LGPL. If you do not delete -! the provisions above, a recipient may use your version of this file under -! the terms of any one of the MPL, the GPL or the LGPL. -! -! ***** END LICENSE BLOCK ***** - -! -! atomic compare-and-swap routines for V9 (ultrasparc) -! -! ====================================================================== -! -! Perform the sequence *a = b atomically with respect to previous value -! of a (a0). If *a==a0 then assign *a to b, all in one atomic operation. -! Returns 1 if assignment happened, and 0 otherwise. -! -! usage : old_val = compare_and_swap(address, oldval, newval) -! -! ----------------------- -! Note on REGISTER USAGE: -! as this is a LEAF procedure, a new stack frame is not created; -! we use the caller stack frame so what would normally be %i (input) -! registers are actually %o (output registers). Also, we must not -! overwrite the contents of %l (local) registers as they are not -! assumed to be volatile during calls. -! -! So, the registers used are: -! %o0 [input] - the address of the value to increment -! %o1 [input] - the old value to compare with -! %o2 [input] - the new value to set for [%o0] -! %o3 [local] - work register -! ----------------------- -! ====================================================================== -! -! v9 - - .inline NativeCompareAndSwap,3 - - stbar - casx [%o0],%o1,%o2 ! compare *w with old value and set to new if equal - cmp %o1,%o2 ! did we succeed? - be,a 1f ! yes - mov 1,%o0 ! return true (annulled when no jump) - mov 0,%o0 ! return false -1: - - .end - -! -! end -! -! ====================================================================== -! diff --git a/js/src/methodjit/Compiler.cpp b/js/src/methodjit/Compiler.cpp index e9db461bbc30..6af4b1aeea0d 100644 --- a/js/src/methodjit/Compiler.cpp +++ b/js/src/methodjit/Compiler.cpp @@ -3933,19 +3933,7 @@ mjit::Compiler::interruptCheckHelper() /* For barrier verification, always take the interrupt so we can verify. */ jump = masm.jump(); } else { - /* - * Bake in and test the address of the interrupt counter for the runtime. - * This is faster than doing two additional loads for the context's - * thread data, but will cause this thread to run slower if there are - * pending interrupts on some other thread. For non-JS_THREADSAFE builds - * we can skip this, as there is only one flag to poll. - */ -#ifdef JS_THREADSAFE - void *interrupt = (void*) &cx->runtime->interruptCounter; -#else - void *interrupt = (void*) &JS_THREAD_DATA(cx)->interruptFlags; -#endif - + void *interrupt = (void*) &cx->runtime->interrupt; #if defined(JS_CPU_X86) || defined(JS_CPU_ARM) || defined(JS_CPU_MIPS) jump = masm.branch32(Assembler::NotEqual, AbsoluteAddress(interrupt), Imm32(0)); #else diff --git a/js/src/methodjit/StubCalls.cpp b/js/src/methodjit/StubCalls.cpp index 733aba696cc6..5ad8c85e55b5 100644 --- a/js/src/methodjit/StubCalls.cpp +++ b/js/src/methodjit/StubCalls.cpp @@ -1897,7 +1897,7 @@ stubs::Exception(VMFrame &f) { // Check the interrupt flag to allow interrupting deeply nested exception // handling. - if (JS_THREAD_DATA(f.cx)->interruptFlags && !js_HandleExecutionInterrupt(f.cx)) + if (f.cx->runtime->interrupt && !js_HandleExecutionInterrupt(f.cx)) THROW(); f.regs.sp[0] = f.cx->getPendingException(); diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index ff9682b21a68..53abc188fd34 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -3455,7 +3455,7 @@ CancelExecution(JSRuntime *rt) if (gWorkerThreadPool) js::workers::terminateAll(gWorkerThreadPool); #endif - JS_TriggerAllOperationCallbacks(rt); + JS_TriggerRuntimeOperationCallback(rt); static const char msg[] = "Script runs for too long, terminating.\n"; #if defined(XP_UNIX) && !defined(JS_THREADSAFE) diff --git a/js/src/shell/jsworkers.cpp b/js/src/shell/jsworkers.cpp index 5822fd92788f..b309e9783ed1 100644 --- a/js/src/shell/jsworkers.cpp +++ b/js/src/shell/jsworkers.cpp @@ -55,6 +55,16 @@ extern size_t gMaxStackSize; +class AutoLock +{ + private: + PRLock *lock; + + public: + AutoLock(PRLock *lock) : lock(lock) { PR_Lock(lock); } + ~AutoLock() { PR_Unlock(lock); } +}; + /* * JavaScript shell workers. * @@ -150,7 +160,7 @@ class WorkerParent { bool initWorkerParent() { return children.init(8); } public: - virtual JSLock *getLock() = 0; + virtual PRLock *getLock() = 0; virtual ThreadPool *getThreadPool() = 0; virtual bool post(Event *item) = 0; // false on OOM or queue closed virtual void trace(JSTracer *trc) = 0; @@ -177,7 +187,7 @@ class ThreadSafeQueue { protected: Queue queue; - JSLock *lock; + PRLock *lock; PRCondVar *condvar; bool closed; @@ -189,9 +199,9 @@ class ThreadSafeQueue ~ThreadSafeQueue() { if (condvar) - JS_DESTROY_CONDVAR(condvar); + PR_DestroyCondVar(condvar); if (lock) - JS_DESTROY_LOCK(lock); + PR_DestroyLock(lock); } // Called by take() with the lock held. @@ -201,7 +211,7 @@ class ThreadSafeQueue bool initThreadSafeQueue() { JS_ASSERT(!lock); JS_ASSERT(!condvar); - return (lock = JS_NEW_LOCK()) && (condvar = JS_NEW_CONDVAR(lock)); + return (lock = PR_NewLock()) && (condvar = PR_NewCondVar(lock)); } bool post(T t) { @@ -209,7 +219,7 @@ class ThreadSafeQueue if (closed) return false; if (queue.empty()) - JS_NOTIFY_ALL_CONDVAR(condvar); + PR_NotifyAllCondVar(condvar); return queue.push(t); } @@ -217,7 +227,7 @@ class ThreadSafeQueue AutoLock hold(lock); closed = true; queue.clear(); - JS_NOTIFY_ALL_CONDVAR(condvar); + PR_NotifyAllCondVar(condvar); } // The caller must hold the lock. @@ -225,7 +235,7 @@ class ThreadSafeQueue while (queue.empty()) { if (shouldStop()) return false; - JS_WAIT_CONDVAR(condvar, JS_NO_TIMEOUT); + PR_WaitCondVar(condvar, PR_INTERVAL_NO_TIMEOUT); } *t = queue.pop(); busy.append(*t); @@ -253,7 +263,7 @@ class ThreadSafeQueue void wake() { AutoLock hold(lock); - JS_NOTIFY_ALL_CONDVAR(condvar); + PR_NotifyAllCondVar(condvar); } void trace(JSTracer *trc) { @@ -371,7 +381,7 @@ class MainQueue MOZ_FINAL : public EventQueue, public WorkerParent delete this; } - virtual JSLock *getLock() { return lock; } + virtual PRLock *getLock() { return lock; } virtual ThreadPool *getThreadPool() { return threadPool; } protected: @@ -390,7 +400,7 @@ class MainQueue MOZ_FINAL : public EventQueue, public WorkerParent Event *event; while (take(&event)) { - JS_RELEASE_LOCK(lock); + PR_Unlock(lock); Event::Result result; { JSAutoRequest req(cx); @@ -414,7 +424,7 @@ class MainQueue MOZ_FINAL : public EventQueue, public WorkerParent result = Event::ok; } } - JS_ACQUIRE_LOCK(lock); + PR_Lock(lock); drop(event); event->destroy(cx); if (result != Event::ok) @@ -596,7 +606,7 @@ class Worker MOZ_FINAL : public WorkerParent JSObject *object; // Worker object exposed to parent JSRuntime *runtime; JSContext *context; - JSLock *lock; + PRLock *lock; Queue events; // owning pointers to pending events Event *current; bool terminated; @@ -616,7 +626,7 @@ class Worker MOZ_FINAL : public WorkerParent threadPool = parent->getThreadPool(); this->parent = parent; this->object = obj; - lock = JS_NEW_LOCK(); + lock = PR_NewLock(); return lock && createRuntime(parentcx) && createContext(parentcx, parent) && @@ -681,7 +691,6 @@ class Worker MOZ_FINAL : public WorkerParent js::SetFunctionNativeReserved(post, 0, PRIVATE_TO_JSVAL(this)); JS_EndRequest(context); - JS_ClearContextThread(context); return true; bad: @@ -769,13 +778,12 @@ class Worker MOZ_FINAL : public WorkerParent while (!events.empty()) events.pop()->destroy(context); if (lock) { - JS_DESTROY_LOCK(lock); + PR_DestroyLock(lock); lock = NULL; } if (runtime) JS_SetRuntimeThread(runtime); if (context) { - JS_SetContextThread(context); JS_DestroyContextNoGC(context); context = NULL; } @@ -801,7 +809,7 @@ class Worker MOZ_FINAL : public WorkerParent WorkerParent *getParent() { return parent; } - virtual JSLock *getLock() { return lock; } + virtual PRLock *getLock() { return lock; } virtual ThreadPool *getThreadPool() { return threadPool; } @@ -1030,15 +1038,15 @@ WorkerQueue::work() { Worker *w; while (take(&w)) { // can block outside the mutex - JS_RELEASE_LOCK(lock); + PR_Unlock(lock); w->processOneEvent(); // enters request on w->context - JS_ACQUIRE_LOCK(lock); + PR_Lock(lock); drop(w); if (lockedIsIdle()) { - JS_RELEASE_LOCK(lock); + PR_Unlock(lock); main->wake(); - JS_ACQUIRE_LOCK(lock); + PR_Lock(lock); } } } @@ -1136,8 +1144,6 @@ Worker::processOneEvent() } JS_SetRuntimeThread(runtime); - JS_SetContextThread(context); - JS_SetNativeStackQuota(context, gMaxStackSize); Event::Result result; { @@ -1171,7 +1177,6 @@ Worker::processOneEvent() if (event) event->destroy(context); - JS_ClearContextThread(context); JS_ClearRuntimeThread(runtime); { diff --git a/js/src/vm/RegExpObject-inl.h b/js/src/vm/RegExpObject-inl.h index 87dac4256a73..3c134d4f6d10 100644 --- a/js/src/vm/RegExpObject-inl.h +++ b/js/src/vm/RegExpObject-inl.h @@ -263,22 +263,12 @@ RegExpObject::setSticky(bool enabled) /* RegExpPrivate inlines. */ -inline RegExpPrivateCache * -detail::RegExpPrivate::getOrCreateCache(JSContext *cx) -{ - if (RegExpPrivateCache *cache = cx->threadData()->getOrCreateRegExpPrivateCache(cx)) - return cache; - - js_ReportOutOfMemory(cx); - return NULL; -} - inline bool detail::RegExpPrivate::cacheLookup(JSContext *cx, JSAtom *atom, RegExpFlag flags, RegExpPrivateCacheKind targetKind, AlreadyIncRefed *result) { - RegExpPrivateCache *cache = getOrCreateCache(cx); + RegExpPrivateCache *cache = cx->runtime->getRegExpPrivateCache(cx); if (!cache) return false; @@ -307,7 +297,7 @@ detail::RegExpPrivate::cacheInsert(JSContext *cx, JSAtom *atom, RegExpPrivateCac * so we have to re-lookup the cache (and inside the cache) after the * allocation is performed. */ - RegExpPrivateCache *cache = getOrCreateCache(cx); + RegExpPrivateCache *cache = cx->runtime->getRegExpPrivateCache(cx); if (!cache) return false; @@ -401,7 +391,7 @@ detail::RegExpPrivateCode::compile(JSContext *cx, JSLinearString &pattern, Token #ifdef JS_METHODJIT if (isJITRuntimeEnabled(cx) && !yarrPattern.m_containsBackreferences) { - JSC::ExecutableAllocator *execAlloc = cx->threadData()->getOrCreateExecutableAllocator(cx); + JSC::ExecutableAllocator *execAlloc = cx->runtime->getExecutableAllocator(cx); if (!execAlloc) { js_ReportOutOfMemory(cx); return false; @@ -414,7 +404,7 @@ detail::RegExpPrivateCode::compile(JSContext *cx, JSLinearString &pattern, Token } #endif - WTF::BumpPointerAllocator *bumpAlloc = cx->threadData()->getOrCreateBumpPointerAllocator(cx); + WTF::BumpPointerAllocator *bumpAlloc = cx->runtime->getBumpPointerAllocator(cx); if (!bumpAlloc) { js_ReportOutOfMemory(cx); return false; @@ -510,11 +500,13 @@ detail::RegExpPrivate::decref(JSContext *cx) if (--refCount != 0) return; - RegExpPrivateCache *cache; - if (source->isAtom() && (cache = cx->threadData()->getRegExpPrivateCache())) { - RegExpPrivateCache::Ptr ptr = cache->lookup(&source->asAtom()); - if (ptr && ptr->value.rep() == this) - cache->remove(ptr); + if (RegExpPrivateCache *cache = cx->runtime->maybeRegExpPrivateCache()) { + if (source->isAtom()) { + if (RegExpPrivateCache::Ptr p = cache->lookup(&source->asAtom())) { + if (p->value.rep() == this) + cache->remove(p); + } + } } #ifdef DEBUG diff --git a/js/src/vm/RegExpObject.h b/js/src/vm/RegExpObject.h index e90fa5a38d5b..5b92599009c6 100644 --- a/js/src/vm/RegExpObject.h +++ b/js/src/vm/RegExpObject.h @@ -388,7 +388,6 @@ class RegExpPrivate createUncached(JSContext *cx, JSLinearString *source, RegExpFlag flags, TokenStream *tokenStream); - static RegExpPrivateCache *getOrCreateCache(JSContext *cx); static bool cacheLookup(JSContext *cx, JSAtom *atom, RegExpFlag flags, RegExpPrivateCacheKind kind, AlreadyIncRefed *result); static bool cacheInsert(JSContext *cx, JSAtom *atom, diff --git a/js/src/vm/Stack.cpp b/js/src/vm/Stack.cpp index 69770b66ce09..18600d7aa090 100644 --- a/js/src/vm/Stack.cpp +++ b/js/src/vm/Stack.cpp @@ -492,39 +492,15 @@ StackSpace::sizeOfCommitted() ContextStack::ContextStack(JSContext *cx) : seg_(NULL), - space_(&JS_THREAD_DATA(cx)->stackSpace), + space_(&cx->runtime->stackSpace), cx_(cx) -{ - threadReset(); -} +{} ContextStack::~ContextStack() { JS_ASSERT(!seg_); } -void -ContextStack::threadReset() -{ -#ifdef JS_THREADSAFE - if (cx_->thread()) - space_ = &JS_THREAD_DATA(cx_)->stackSpace; - else - space_ = NULL; -#else - space_ = &JS_THREAD_DATA(cx_)->stackSpace; -#endif -} - -#ifdef DEBUG -void -ContextStack::assertSpaceInSync() const -{ - JS_ASSERT(space_); - JS_ASSERT(space_ == &JS_THREAD_DATA(cx_)->stackSpace); -} -#endif - bool ContextStack::onTop() const { diff --git a/js/xpconnect/loader/mozJSComponentLoader.cpp b/js/xpconnect/loader/mozJSComponentLoader.cpp index e736b64fd05e..151905c08e16 100644 --- a/js/xpconnect/loader/mozJSComponentLoader.cpp +++ b/js/xpconnect/loader/mozJSComponentLoader.cpp @@ -317,7 +317,6 @@ public: private: JSContext* mContext; - intN mContextThread; nsIThreadJSContextStack* mContextStack; char* mBuf; @@ -1355,23 +1354,18 @@ mozJSComponentLoader::ModuleEntry::GetFactory(const mozilla::Module& module, //---------------------------------------------------------------------- JSCLContextHelper::JSCLContextHelper(mozJSComponentLoader *loader) - : mContext(loader->mContext), mContextThread(0), + : mContext(loader->mContext), mContextStack(loader->mContextStack), mBuf(nsnull) { mContextStack->Push(mContext); - mContextThread = JS_GetContextThread(mContext); - if (mContextThread) { - JS_BeginRequest(mContext); - } + JS_BeginRequest(mContext); } JSCLContextHelper::~JSCLContextHelper() { if (mContextStack) { - if (mContextThread) { - JS_EndRequest(mContext); - } + JS_EndRequest(mContext); mContextStack->Pop(nsnull); diff --git a/js/xpconnect/src/XPCJSRuntime.cpp b/js/xpconnect/src/XPCJSRuntime.cpp index 1ed04e943d0b..7121f2929f64 100644 --- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -946,7 +946,7 @@ XPCJSRuntime::WatchdogMain(void *arg) #endif PR_WaitCondVar(self->mWatchdogWakeup, sleepInterval); JS_ASSERT(status == PR_SUCCESS); - js::TriggerOperationCallbacksForActiveContexts(self->mJSRuntime); + js::TriggerOperationCallback(self->mJSRuntime); } /* Wake up the main thread waiting for the watchdog to terminate. */ @@ -1043,7 +1043,6 @@ XPCJSRuntime::GetJSCycleCollectionContext() mJSCycleCollectionContext = JS_NewContext(mJSRuntime, 0); if (!mJSCycleCollectionContext) return nsnull; - JS_ClearContextThread(mJSCycleCollectionContext); } return mJSCycleCollectionContext; } @@ -1067,10 +1066,8 @@ XPCJSRuntime::~XPCJSRuntime() mWatchdogWakeup = nsnull; } - if (mJSCycleCollectionContext) { - JS_SetContextThread(mJSCycleCollectionContext); + if (mJSCycleCollectionContext) JS_DestroyContextNoGC(mJSCycleCollectionContext); - } #ifdef XPC_DUMP_AT_SHUTDOWN { @@ -1639,30 +1636,30 @@ ReportJSRuntimeStats(const JS::IterateData &data, const nsACString &pathPrefix, "hanging off them." SLOP_BYTES_STRING, callback, closure); - ReportMemoryBytes(pathPrefix + NS_LITERAL_CSTRING("runtime/threads/normal"), - nsIMemoryReporter::KIND_HEAP, data.runtimeThreadsNormal, - "Memory used by JSThread objects and their data, " + ReportMemoryBytes(pathPrefix + NS_LITERAL_CSTRING("runtime/normal"), + nsIMemoryReporter::KIND_HEAP, data.runtimeNormal, + "Memory used by a JSRuntime, " "excluding memory that is reported by " "other reporters under 'explicit/js/runtime/'." SLOP_BYTES_STRING, callback, closure); - ReportMemoryBytes(pathPrefix + NS_LITERAL_CSTRING("runtime/threads/temporary"), - nsIMemoryReporter::KIND_HEAP, data.runtimeThreadsTemporary, - "Memory held transiently in JSThreads and used during " + ReportMemoryBytes(pathPrefix + NS_LITERAL_CSTRING("runtime/temporary"), + nsIMemoryReporter::KIND_HEAP, data.runtimeTemporary, + "Memory held transiently in JSRuntime and used during " "compilation. It mostly holds parse nodes." SLOP_BYTES_STRING, callback, closure); - ReportMemoryBytes0(pathPrefix + NS_LITERAL_CSTRING("runtime/threads/regexp-code"), - nsIMemoryReporter::KIND_NONHEAP, data.runtimeThreadsRegexpCode, + ReportMemoryBytes0(pathPrefix + NS_LITERAL_CSTRING("runtime/regexp-code"), + nsIMemoryReporter::KIND_NONHEAP, data.runtimeRegexpCode, "Memory used by the regexp JIT to hold generated code.", callback, closure); - ReportMemoryBytes(pathPrefix + NS_LITERAL_CSTRING("runtime/threads/stack-committed"), - nsIMemoryReporter::KIND_NONHEAP, data.runtimeThreadsStackCommitted, - "Memory used for the thread stacks. This is the committed portions " - "of the stacks; any uncommitted portions are not measured because they " - "hardly cost anything.", + ReportMemoryBytes(pathPrefix + NS_LITERAL_CSTRING("runtime/stack-committed"), + nsIMemoryReporter::KIND_NONHEAP, data.runtimeStackCommitted, + "Memory used for the JS call stack. This is the committed portion " + "of the stack; the uncommitted portion is not measured because it " + "hardly costs anything.", callback, closure); ReportGCHeapBytes(pathPrefix + @@ -1965,9 +1962,9 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect) JS_EnumerateDiagnosticMemoryRegions(DiagnosticMemoryCallback); #endif JS_SetAccumulateTelemetryCallback(mJSRuntime, AccumulateTelemetryCallback); - mWatchdogWakeup = JS_NEW_CONDVAR(js::GetRuntimeGCLock(mJSRuntime)); + mWatchdogWakeup = PR_NewCondVar(js::GetRuntimeGCLock(mJSRuntime)); if (!mWatchdogWakeup) - NS_RUNTIMEABORT("JS_NEW_CONDVAR failed."); + NS_RUNTIMEABORT("PR_NewCondVar failed."); js::SetActivityCallback(mJSRuntime, ActivityCallback, this); diff --git a/js/xpconnect/src/XPCThreadContext.cpp b/js/xpconnect/src/XPCThreadContext.cpp index c96385616f14..39963eff8726 100644 --- a/js/xpconnect/src/XPCThreadContext.cpp +++ b/js/xpconnect/src/XPCThreadContext.cpp @@ -55,7 +55,6 @@ using namespace mozilla; XPCJSContextStack::~XPCJSContextStack() { if (mOwnSafeJSContext) { - JS_SetContextThread(mOwnSafeJSContext); JS_DestroyContext(mOwnSafeJSContext); mOwnSafeJSContext = nsnull; } @@ -109,7 +108,6 @@ GetPrincipalFromCx(JSContext *cx) bool XPCJSContextStack::Push(JSContext *cx) { - MOZ_ASSERT_IF(cx, JS_GetContextThread(cx)); if (mStack.Length() == 0) { mStack.AppendElement(cx); return true; @@ -423,7 +421,7 @@ XPCPerThreadData::GetDataImpl(JSContext *cx) } if (cx && !sMainJSThread && NS_IsMainThread()) { - sMainJSThread = js::GetContextThread(cx); + sMainJSThread = js::GetOwnerThread(cx); sMainThreadData = data; diff --git a/js/xpconnect/src/XPCWrappedJSClass.cpp b/js/xpconnect/src/XPCWrappedJSClass.cpp index aa467b60be4d..e01cc2a36321 100644 --- a/js/xpconnect/src/XPCWrappedJSClass.cpp +++ b/js/xpconnect/src/XPCWrappedJSClass.cpp @@ -68,10 +68,8 @@ bool AutoScriptEvaluate::StartEvaluating(JSObject *scope, JSErrorReporter errorR JS_SetErrorReporter(mJSContext, errorReporter); mErrorReporterSet = true; } - mContextHasThread = JS_GetContextThread(mJSContext); - if (mContextHasThread) - JS_BeginRequest(mJSContext); + JS_BeginRequest(mJSContext); if (!mEnterCompartment.enter(mJSContext, scope)) return false; @@ -102,8 +100,7 @@ AutoScriptEvaluate::~AutoScriptEvaluate() else JS_ClearPendingException(mJSContext); - if (mContextHasThread) - JS_EndRequest(mJSContext); + JS_EndRequest(mJSContext); // If this is a JSContext that has a private context that provides a // nsIXPCScriptNotify interface, then notify the object the script has @@ -543,8 +540,8 @@ GetContextFromObject(JSObject *obj) if (xpcc) { JSContext *cx = xpcc->GetJSContext(); - if (JS_GetContextThread(cx) == JS_GetCurrentThread()) - return cx; + JS_AbortIfWrongThread(JS_GetRuntime(cx)); + return cx; } return nsnull; diff --git a/js/xpconnect/src/nsXPConnect.cpp b/js/xpconnect/src/nsXPConnect.cpp index 324fba22cbce..d02af55cebc1 100644 --- a/js/xpconnect/src/nsXPConnect.cpp +++ b/js/xpconnect/src/nsXPConnect.cpp @@ -554,14 +554,10 @@ nsXPConnect::BeginCycleCollection(nsCycleCollectionTraversalCallback &cb, if (!cx) return NS_ERROR_OUT_OF_MEMORY; - // Clear after mCycleCollectionContext is destroyed - JS_SetContextThread(cx); - NS_ASSERTION(!mCycleCollectionContext, "Didn't call FinishTraverse?"); mCycleCollectionContext = new XPCCallContext(NATIVE_CALLER, cx); if (!mCycleCollectionContext->IsValid()) { mCycleCollectionContext = nsnull; - JS_ClearContextThread(cx); return NS_ERROR_FAILURE; } @@ -610,11 +606,15 @@ nsXPConnect::BeginCycleCollection(nsCycleCollectionTraversalCallback &cb, return NS_OK; } -void +bool nsXPConnect::NotifyLeaveMainThread() { NS_ABORT_IF_FALSE(NS_IsMainThread(), "Off main thread"); - JS_ClearRuntimeThread(mRuntime->GetJSRuntime()); + JSRuntime *rt = mRuntime->GetJSRuntime(); + if (JS_IsInRequest(rt) || JS_IsInSuspendedRequest(rt)) + return false; + JS_ClearRuntimeThread(rt); + return true; } void @@ -641,11 +641,8 @@ nsXPConnect::NotifyEnterMainThread() nsresult nsXPConnect::FinishTraverse() { - if (mCycleCollectionContext) { - JSContext *cx = mCycleCollectionContext->GetJSContext(); + if (mCycleCollectionContext) mCycleCollectionContext = nsnull; - JS_ClearContextThread(cx); - } return NS_OK; } diff --git a/js/xpconnect/src/xpcprivate.h b/js/xpconnect/src/xpcprivate.h index cf3882bef0cd..f54f54c24012 100644 --- a/js/xpconnect/src/xpcprivate.h +++ b/js/xpconnect/src/xpcprivate.h @@ -539,7 +539,7 @@ public: nsCycleCollectionTraversalCallback &cb); // nsCycleCollectionLanguageRuntime - virtual void NotifyLeaveMainThread(); + virtual bool NotifyLeaveMainThread(); virtual void NotifyEnterCycleCollectionThread(); virtual void NotifyLeaveCycleCollectionThread(); virtual void NotifyEnterMainThread(); @@ -3665,9 +3665,7 @@ public: JS_Assert("NS_IsMainThread()", __FILE__, __LINE__); if (cx) { - NS_ASSERTION(js::GetContextThread(cx), "Uh, JS context w/o a thread?"); - - if (js::GetContextThread(cx) == sMainJSThread) + if (js::GetOwnerThread(cx) == sMainJSThread) return sMainThreadData; } else if (sMainThreadData && sMainThreadData->mThread == PR_GetCurrentThread()) { return sMainThreadData; @@ -3770,7 +3768,7 @@ public: {sMainJSThread = nsnull; sMainThreadData = nsnull;} static bool IsMainThread(JSContext *cx) - { return js::GetContextThread(cx) == sMainJSThread; } + { return js::GetOwnerThread(cx) == sMainJSThread; } private: XPCPerThreadData(); diff --git a/js/xpconnect/tests/chrome/Makefile.in b/js/xpconnect/tests/chrome/Makefile.in index 93a9be98b8b8..0be137a69d4c 100644 --- a/js/xpconnect/tests/chrome/Makefile.in +++ b/js/xpconnect/tests/chrome/Makefile.in @@ -75,8 +75,6 @@ _CHROME_FILES = \ test_getweakmapkeys.xul \ test_weakmaps.xul \ test_bug706301.xul \ - test_ccbeginfail.xul \ - test_ccdump.xul \ $(NULL) # Disabled until this test gets updated to test the new proxy based diff --git a/js/xpconnect/tests/chrome/test_ccbeginfail.xul b/js/xpconnect/tests/chrome/test_ccbeginfail.xul deleted file mode 100644 index e9f5a639e96f..000000000000 --- a/js/xpconnect/tests/chrome/test_ccbeginfail.xul +++ /dev/null @@ -1,67 +0,0 @@ - - - - - - - diff --git a/js/xpconnect/tests/chrome/test_ccdump.xul b/js/xpconnect/tests/chrome/test_ccdump.xul deleted file mode 100644 index 9f7e697a1260..000000000000 --- a/js/xpconnect/tests/chrome/test_ccdump.xul +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - - diff --git a/xpcom/base/nsCycleCollector.cpp b/xpcom/base/nsCycleCollector.cpp index 81487c1595e0..6fd9b9b3ed78 100644 --- a/xpcom/base/nsCycleCollector.cpp +++ b/xpcom/base/nsCycleCollector.cpp @@ -3748,10 +3748,13 @@ public: aListener = nsnull; mListener = aListener; - GetJSRuntime()->NotifyLeaveMainThread(); - mRequest.Notify(); - mReply.Wait(); - GetJSRuntime()->NotifyEnterMainThread(); + if (GetJSRuntime()->NotifyLeaveMainThread()) { + mRequest.Notify(); + mReply.Wait(); + GetJSRuntime()->NotifyEnterMainThread(); + } else { + mCollected = mCollector->BeginCollection(mListener); + } mListener = nsnull; diff --git a/xpcom/base/nsCycleCollector.h b/xpcom/base/nsCycleCollector.h index 40913fded21f..7dabe60e5dab 100644 --- a/xpcom/base/nsCycleCollector.h +++ b/xpcom/base/nsCycleCollector.h @@ -91,8 +91,11 @@ struct nsCycleCollectionJSRuntime : public nsCycleCollectionLanguageRuntime { /** * Called before/after transitioning to/from the main thread. + * + * NotifyLeaveMainThread may return 'false' to prevent the cycle collector + * from leaving the main thread. */ - virtual void NotifyLeaveMainThread() = 0; + virtual bool NotifyLeaveMainThread() = 0; virtual void NotifyEnterCycleCollectionThread() = 0; virtual void NotifyLeaveCycleCollectionThread() = 0; virtual void NotifyEnterMainThread() = 0;