author | Luke Wagner <luke@mozilla.com> |
Mon, 18 Jul 2011 14:54:48 -0700 | |
changeset 85297 | e517d4c431436772c46802f98618155ea8e37973 |
parent 85296 | bbb9dbfe659b3832146f6887dd59cc41f8fe6515 |
child 85298 | 3c47bcef2ce42579171f459413374af2459d0282 |
push id | 5271 |
push user | lwagner@mozilla.com |
push date | Wed, 25 Jan 2012 03:16:16 +0000 |
treeherder | mozilla-inbound@e517d4c43143 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | igor, mccr8 |
bugs | 675078 |
milestone | 12.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/js/public/MemoryMetrics.h +++ b/js/public/MemoryMetrics.h @@ -117,20 +117,20 @@ struct CompartmentStats struct IterateData { IterateData(JSMallocSizeOfFun mallocSizeOf, GetNameCallback getNameCb, DestroyNameCallback destroyNameCb) : 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) , gcHeapChunkCleanDecommitted(0) , gcHeapChunkDirtyDecommitted(0) , gcHeapArenaUnused(0) , gcHeapChunkAdmin(0) , gcHeapUnusedPercentage(0) @@ -148,20 +148,20 @@ struct IterateData , mallocSizeOf(mallocSizeOf) , getNameCb(getNameCb) , destroyNameCb(destroyNameCb) {} 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; int64_t gcHeapChunkCleanDecommitted; int64_t gcHeapChunkDirtyDecommitted; int64_t gcHeapArenaUnused; int64_t gcHeapChunkAdmin; int64_t gcHeapUnusedPercentage;
--- a/js/src/Makefile.in +++ b/js/src/Makefile.in @@ -125,17 +125,16 @@ CPPSRCS = \ jsgcmark.cpp \ jsgcchunk.cpp \ jsgcstats.cpp \ jscrashreport.cpp \ jshash.cpp \ jsinfer.cpp \ jsinterp.cpp \ jsiter.cpp \ - jslock.cpp \ jslog2.cpp \ jsmath.cpp \ jsnativestack.cpp \ jsnum.cpp \ jsobj.cpp \ json.cpp \ jsonparser.cpp \ jsopcode.cpp \ @@ -614,17 +613,17 @@ check-malloc-function-usage: $(filter-ou "in Makefile.in" "cx->calloc_ or rt->calloc_" $^ $(srcdir)/config/check_source_count.py "\bjs_realloc\b" 0 \ "in Makefile.in" "cx->realloc_ or rt->realloc_" $^ $(srcdir)/config/check_source_count.py "\bjs_free\b" 0 \ "in Makefile.in" "cx->free_" $^ # 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 \ "in Makefile.in" "{cx,rt}->{free_,delete_,array_delete}" $^ ifneq ($(OS_ARCH),WINNT) # FIXME: this should be made work on Windows too. #check:: check-malloc-function-usage FIXME: disable on JM until closer to merge time. endif @@ -695,20 +694,16 @@ ifdef JS_THREADSAFE DEFINES += -DJS_THREADSAFE endif ifdef JS_HAS_CTYPES 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 # We do not want to have obsolete NSPR functionality in threadsafe builds. DEFINES += -DNO_NSPR_10_SUPPORT ifneq ($(findstring -L,$(NSPR_LIBS)),) @@ -776,30 +771,16 @@ endif # GNU_CC endif ifeq ($(OS_RELEASE),4.1) EXTRA_LIBS += -ldl -lnsl else 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) jsatom.o: jsatom.cpp Makefile.in $(REPORT_BUILD) @$(MAKE_DEPS_AUTO_CXX) $(CXX) -o $@ -c $(filter-out $(MOZ_OPTIMIZE_FLAGS), $(COMPILE_CFLAGS)) $< jsdtoa.o: jsdtoa.cpp Makefile.in
--- a/js/src/MemoryMetrics.cpp +++ b/js/src/MemoryMetrics.cpp @@ -214,47 +214,36 @@ CollectCompartmentStatsForRuntime(JSRunt gc::ChunkSize; IterateCompartmentsArenasCells(cx, data, CompartmentMemoryCallback, ArenaCallback, CellCallback); IterateChunks(cx, data, ChunkCallback); 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); // This is initialized to all bytes stored in used chunks, and then we // subtract used space from it each time around the loop. data->gcHeapChunkDirtyUnused = data->gcHeapChunkTotal - data->gcHeapChunkCleanUnused - @@ -341,36 +330,27 @@ GetExplicitNonHeapForRuntime(JSRuntime * { JSAutoRequest ar(cx); // explicit/<compartment>/mjit-code size_t n = 0; 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); return true; } } // namespace JS
--- a/js/src/ctypes/CTypes.cpp +++ b/js/src/ctypes/CTypes.cpp @@ -57,29 +57,16 @@ #endif 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 *******************************************************************************/ static JSBool ConstructAbstract(JSContext* cx, uintN argc, jsval* vp); namespace CType { static JSBool ConstructData(JSContext* cx, uintN argc, jsval* vp); static JSBool ConstructBasic(JSContext* cx, JSObject* obj, uintN argc, jsval* vp); @@ -2786,17 +2773,16 @@ CType::FinalizeProtoClass(JSContext* cx, // for use with FunctionType closures. And if we're here, in this finalizer, // we're guaranteed to not need it anymore. Note that this slot will only // be set for the object (of class CTypeProto) ctypes.FunctionType.prototype. jsval slot; if (!JS_GetReservedSlot(cx, obj, SLOT_CLOSURECX, &slot) || JSVAL_IS_VOID(slot)) return; JSContext* closureCx = static_cast<JSContext*>(JSVAL_TO_PRIVATE(slot)); - JS_SetContextThread(closureCx); JS_DestroyContextNoGC(closureCx); } void CType::Trace(JSTracer* trc, JSObject* obj) { // Make sure our TypeCode slot is legit. If it's not, bail. jsval slot = obj->getSlot(SLOT_TYPECODE); @@ -5378,24 +5364,17 @@ CClosure::Create(JSContext* cx, return NULL; } if (!JS_SetReservedSlot(cx, proto, SLOT_CLOSURECX, PRIVATE_TO_JSVAL(cinfo->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 // future when the error sentinel is actually needed. if (!JSVAL_IS_VOID(errVal)) { // Make sure the callback returns something. @@ -5500,21 +5479,16 @@ CClosure::ClosureStub(ffi_cif* cif, void // Retrieve the essentials from our closure object. ClosureInfo* cinfo = static_cast<ClosureInfo*>(userData); JSContext* cx = cinfo->cx; JSObject* typeObj = cinfo->typeObj; 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); JSAutoEnterCompartment ac; if (!ac.enter(cx, jsfnObj)) return;
--- a/js/src/ctypes/CTypes.h +++ b/js/src/ctypes/CTypes.h @@ -326,19 +326,16 @@ struct ClosureInfo JSRuntime* rt; // Used in the destructor, where cx might have already // been GCed. JSObject* closureObj; // CClosure object JSObject* typeObj; // FunctionType describing the C function JSObject* thisObj; // 'this' object to use for the JS function call 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. ClosureInfo(JSRuntime* runtime) : rt(runtime) , errResult(NULL) , closure(NULL) {}
--- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -55,24 +55,26 @@ #include "jsapi.h" #include "jsarray.h" #include "jsatom.h" #include "jsbool.h" #include "jsclone.h" #include "jscntxt.h" #include "jsversion.h" #include "jsdate.h" +#include "jsdtoa.h" #include "jsexn.h" #include "jsfun.h" #include "jsgc.h" #include "jsgcmark.h" #include "jsinterp.h" #include "jsiter.h" #include "jslock.h" #include "jsmath.h" +#include "jsnativestack.h" #include "jsnum.h" #include "json.h" #include "jsobj.h" #include "jsopcode.h" #include "jsprobes.h" #include "jsproxy.h" #include "jsscope.h" #include "jsscript.h" @@ -83,17 +85,18 @@ #include "jstypedarray.h" #include "ds/LifoAlloc.h" #include "builtin/MapObject.h" #include "builtin/RegExp.h" #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" #include "jsobjinlines.h" #include "jsscopeinlines.h" #include "jsscriptinlines.h" #include "vm/RegExpObject-inl.h" @@ -687,21 +690,37 @@ JS_IsBuiltinFunctionConstructor(JSFuncti /* * Has a new runtime ever been created? This flag is used to detect unsafe * changes to js_CStringsAreUTF8 after a runtime has been created, and to * control things that should happen only once across all runtimes. */ 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), gcBytes(0), gcTriggerBytes(0), gcLastBytes(0), gcMaxBytes(0), gcMaxMallocBytes(0), @@ -741,60 +760,54 @@ JSRuntime::JSRuntime() positiveInfinityValue(UndefinedValue()), emptyString(NULL), debugMode(false), profilingScripts(false), hadOutOfMemory(false), data(NULL), #ifdef JS_THREADSAFE gcLock(NULL), - gcDone(NULL), - requestDone(NULL), requestCount(0), - gcThread(NULL), gcHelperThread(thisFromCtor()), #endif debuggerMutations(0), securityCallbacks(NULL), 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. */ JS_INIT_CLIST(&contextList); JS_INIT_CLIST(&debuggerList); PodZero(&globalDebugHooks); PodZero(&atomState); } bool JSRuntime::init(uint32_t maxbytes) { #ifdef JS_THREADSAFE - ownerThread_ = js_CurrentThreadId(); + ownerThread_ = PR_GetCurrentThread(); #endif #ifdef JS_METHODJIT_SPEW JMCheckLogging(); #endif if (!js_InitGC(this, maxbytes)) return false; @@ -807,35 +820,38 @@ JSRuntime::init(uint32_t maxbytes) } atomsCompartment->isSystemCompartment = true; atomsCompartment->setGCLastBytes(8192, GC_NORMAL); 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_<JSC::ExecutableAllocator>(execAlloc_); + delete_<WTF::BumpPointerAllocator>(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)) { JSContext *cx, *iter = NULL; uintN cxcount = 0; while ((cx = js_ContextIterator(this, JS_TRUE, &iter)) != NULL) { fprintf(stderr, "JS API usage error: found live context at %p\n", @@ -844,51 +860,53 @@ JSRuntime::~JSRuntime() } fprintf(stderr, "JS API usage error: %u context%s left in runtime upon JS_DestroyRuntime.\n", cxcount, (cxcount == 1) ? "" : "s"); } #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 } #ifdef JS_THREADSAFE 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(); -} -#endif + return ownerThread_ == PR_GetCurrentThread(); +} +#endif /* JS_THREADSAFE */ JS_PUBLIC_API(JSRuntime *) JS_NewRuntime(uint32_t maxbytes) { if (!js_NewRuntimeWasCalled) { #ifdef DEBUG /* * This code asserts that the numbers associated with the error names @@ -938,20 +956,16 @@ JS_DestroyRuntime(JSRuntime *rt) Probes::destroyRuntime(rt); Foreground::delete_(rt); } JS_PUBLIC_API(void) JS_ShutDown(void) { Probes::shutdown(); - -#ifdef JS_THREADSAFE - js_CleanupLocks(); -#endif PRMJ_NowShutdown(); } JS_PUBLIC_API(void *) JS_GetRuntimePrivate(JSRuntime *rt) { return rt->data; } @@ -985,76 +999,53 @@ JS::UserCompartmentCount(const JSRuntime } return n; } #ifdef JS_THREADSAFE static void StartRequest(JSContext *cx) { - JSThread *t = cx->thread(); - JS_ASSERT(CURRENT_THREAD_IS_ME(t)); - - if (t->data.requestDepth) { - t->data.requestDepth++; + JSRuntime *rt = cx->runtime; + JS_ASSERT(rt->onOwnerThread()); + + 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); } } 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); } } } #endif /* JS_THREADSAFE */ JS_PUBLIC_API(void) @@ -1085,55 +1076,66 @@ JS_YieldRequest(JSContext *cx) JS_ResumeRequest(cx, JS_SuspendRequest(cx)); #endif } JS_PUBLIC_API(jsrefcount) JS_SuspendRequest(JSContext *cx) { #ifdef JS_THREADSAFE - JSThread *t = cx->thread(); - JS_ASSERT(CURRENT_THREAD_IS_ME(t)); - - jsrefcount saveDepth = t->data.requestDepth; + JSRuntime *rt = cx->runtime; + JS_ASSERT(rt->onOwnerThread()); + + jsrefcount saveDepth = rt->requestDepth; if (!saveDepth) return 0; - t->suspendCount++; - t->data.requestDepth = 1; + rt->suspendCount++; + rt->requestDepth = 1; StopRequest(cx); return saveDepth; #else return 0; #endif } 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 } JS_PUBLIC_API(JSContextCallback) JS_SetContextCallback(JSRuntime *rt, JSContextCallback cxCallback) { @@ -3014,33 +3016,29 @@ JS_SetThreadStackLimit(JSContext *cx, ui limitAddr = UINTPTR_MAX; #endif cx->stackLimit = 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<uintptr_t>(JS_THREAD_DATA(cx)->nativeStackBase); + uintptr_t stackBase = cx->runtime->nativeStackBase; JS_ASSERT(stackBase <= size_t(-1) - stackSize); cx->stackLimit = stackBase + stackSize - 1; } #else if (stackSize == 0) { cx->stackLimit = 0; } else { - uintptr_t stackBase = reinterpret_cast<uintptr_t>(JS_THREAD_DATA(cx)->nativeStackBase); + uintptr_t stackBase = uintptr_t(cx->runtime->conservativeGC.nativeStackBase); JS_ASSERT(stackBase >= stackSize); cx->stackLimit = stackBase - (stackSize - 1); } #endif } /************************************************************************/ @@ -5513,19 +5511,16 @@ JS_New(JSContext *cx, JSObject *ctor, ui } return &args.rval().toObject(); } 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; } JS_PUBLIC_API(JSOperationCallback) JS_GetOperationCallback(JSContext *cx) { @@ -5533,36 +5528,31 @@ JS_GetOperationCallback(JSContext *cx) } JS_PUBLIC_API(void) 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(); return fp != NULL; } JS_PUBLIC_API(JSBool) JS_SaveFrameChain(JSContext *cx) @@ -6585,60 +6575,23 @@ JS_ThrowStopIteration(JSContext *cx) { AssertNoGC(cx); return js_ThrowStopIteration(cx); } JS_PUBLIC_API(intptr_t) JS_GetCurrentThread() { - return reinterpret_cast<intptr_t>(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<intptr_t>(cx->thread()->id) : 0; + return reinterpret_cast<intptr_t>(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<intptr_t>(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) { AssertNoGC(rt); #ifdef JS_THREADSAFE rt->clearOwnerThread(); #endif } @@ -6656,54 +6609,16 @@ extern JS_NEVER_INLINE JS_PUBLIC_API(voi JS_AbortIfWrongThread(JSRuntime *rt) { #ifdef JS_THREADSAFE if (!rt->onOwnerThread()) JS_Assert("rt->onOwnerThread()", __FILE__, __LINE__); #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<intptr_t>(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) { bool schedule = zeal >= js::gc::ZealAllocThreshold && zeal < js::gc::ZealVerifierThreshold; cx->runtime->gcZeal_ = zeal; cx->runtime->gcZealFrequency = frequency; cx->runtime->gcNextScheduled = schedule ? frequency : 0;
--- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -2318,16 +2318,19 @@ extern JS_PUBLIC_API(void) JS_DestroyRuntime(JSRuntime *rt); extern JS_PUBLIC_API(void) 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); extern JS_PUBLIC_API(void) JS_BeginRequest(JSContext *cx); extern JS_PUBLIC_API(void) JS_EndRequest(JSContext *cx); @@ -2338,17 +2341,20 @@ JS_YieldRequest(JSContext *cx); extern JS_PUBLIC_API(jsrefcount) JS_SuspendRequest(JSContext *cx); 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 inline bool IsPoisonedId(jsid iden) { if (JSID_IS_STRING(iden)) @@ -2420,24 +2426,24 @@ class JSAutoSuspendRequest { #endif }; class JSAutoCheckRequest { public: 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 } private: #if defined JS_THREADSAFE && defined DEBUG JSContext *mContext; #endif @@ -4484,17 +4490,17 @@ JS_SetOperationCallback(JSContext *cx, J extern JS_PUBLIC_API(JSOperationCallback) JS_GetOperationCallback(JSContext *cx); 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); /* * Saving and restoring frame chains. * * These two functions are used to set aside cx's call stack while that stack @@ -5257,34 +5263,16 @@ JS_ThrowReportedError(JSContext *cx, con */ extern JS_PUBLIC_API(JSBool) 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 * into the JS engine check that a runtime (or anything contained in the * runtime: context, compartment, object, etc) is only touched by its owner * thread. Embeddings may check this invariant outside the JS engine by calling * JS_AbortIfWrongThread (which will abort if not on the owner thread, even for * non-debug builds). *
--- a/js/src/jsatom.cpp +++ b/js/src/jsatom.cpp @@ -328,21 +328,18 @@ JSBool js_InitAtomState(JSRuntime *rt) { JSAtomState *state = &rt->atomState; JS_ASSERT(!state->atoms.initialized()); 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 js_FinishAtomState(JSRuntime *rt) { JSAtomState *state = &rt->atomState; if (!state->atoms.initialized()) { @@ -350,20 +347,16 @@ js_FinishAtomState(JSRuntime *rt) * We are called with uninitialized state when JS_NewRuntime fails and * calls JS_DestroyRuntime on a partially initialized runtime. */ return; } 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 js_InitCommonAtoms(JSContext *cx) { JSAtomState *state = &cx->runtime->atomState; JSAtom **atoms = state->commonAtomsStart(); for (size_t i = 0; i < ArrayLength(js_common_atom_names); i++, atoms++) { @@ -428,17 +421,16 @@ js_SweepAtomState(JSContext *cx) bool AtomIsInterned(JSContext *cx, JSAtom *atom) { /* We treat static strings as interned because they're never collected. */ if (StaticStrings::isStatic(atom)) return true; - AutoLockAtomsCompartment lock(cx); AtomSet::Ptr p = cx->runtime->atomState.atoms.lookup(atom); if (!p) return false; return p->isTagged(); } enum OwnCharsBehavior @@ -457,18 +449,16 @@ static JSAtom * AtomizeInline(JSContext *cx, const jschar **pchars, size_t length, InternBehavior ib, OwnCharsBehavior ocb = CopyChars) { const jschar *chars = *pchars; 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)); if (p) { JSAtom *atom = p->asPtr(); p->setTagged(bool(ib)); return atom; } @@ -517,19 +507,16 @@ JSAtom * js_AtomizeString(JSContext *cx, JSString *str, InternBehavior ib) { if (str->isAtom()) { JSAtom &atom = str->asAtom(); /* N.B. static atoms are effectively always interned. */ 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. */ JS_ASSERT(p->asPtr() == &atom); JS_ASSERT(ib == InternAtom); p->setTagged(bool(ib)); return &atom; } @@ -599,19 +586,19 @@ js_AtomizeChars(JSContext *cx, const jsc return AtomizeInline(cx, &chars, length, ib); } JSAtom * 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 JS_FRIEND_API(void) js_DumpAtoms(JSContext *cx, FILE *fp) { JSAtomState *state = &cx->runtime->atomState;
--- a/js/src/jsatom.h +++ b/js/src/jsatom.h @@ -263,20 +263,16 @@ enum FlationCoding }; } /* namespace js */ 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 * occupied by the common atoms in js_FinishCommonAtoms. * * js_common_atom_names defined in jsatom.cpp contains C strings for atoms * in the order of atom fields here. Therefore you must update that array * if you change member order here.
--- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -59,25 +59,23 @@ #include "jstypes.h" #include "jsutil.h" #include "jsclist.h" #include "jsprf.h" #include "jsatom.h" #include "jscntxt.h" #include "jsversion.h" #include "jsdbgapi.h" -#include "jsdtoa.h" #include "jsexn.h" #include "jsfun.h" #include "jsgc.h" #include "jsgcmark.h" #include "jsiter.h" #include "jslock.h" #include "jsmath.h" -#include "jsnativestack.h" #include "jsnum.h" #include "jsobj.h" #include "jsopcode.h" #include "jspubtd.h" #include "jsscope.h" #include "jsscript.h" #include "jsstr.h" @@ -91,343 +89,100 @@ #include "jsatominlines.h" #include "jscntxtinlines.h" #include "jscompartment.h" #include "jsobjinlines.h" 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() +void +JSRuntime::sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf, size_t *normal, size_t *temporary, + size_t *regexpCode, size_t *stackCommitted) { - JS_ASSERT(!repCache); - - rt->delete_<JSC::ExecutableAllocator>(execAlloc); - rt->delete_<WTF::BumpPointerAllocator>(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) -{ - /* - * 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. */ if (normal) *normal = mallocSizeOf(dtoaState, /* sizeof(DtoaState) */0); if (temporary) *temporary = tempLifoAlloc.sizeOfExcludingThis(mallocSizeOf); 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; } 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_<JSC::ExecutableAllocator>(); - if (!execAlloc) + execAlloc_ = new_<JSC::ExecutableAllocator>(); + 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_<WTF::BumpPointerAllocator>(); - if (!bumpAlloc) + bumpAlloc_ = new_<WTF::BumpPointerAllocator>(); + 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_<RegExpPrivateCache>(rt); + RegExpPrivateCache *newCache = new_<RegExpPrivateCache>(this); if (!newCache || !newCache->init()) { js_ReportOutOfMemory(cx); - rt->delete_<RegExpPrivateCache>(newCache); + delete_<RegExpPrivateCache>(newCache); return NULL; } - repCache = newCache; - return repCache; + repCache_ = newCache; + return repCache_; } -void -ThreadData::purgeRegExpPrivateCache() -{ - rt->delete_<RegExpPrivateCache>(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_<JSThread>(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) { JS_AbortIfWrongThread(rt); /* * We need to initialize the new context fully before adding it to the * runtime list. After that it can be accessed from another thread via @@ -439,30 +194,22 @@ js_NewContext(JSRuntime *rt, size_t stac JS_ASSERT(cx->findVersion() == JSVERSION_DEFAULT); if (!cx->busyArrays.init()) { Foreground::delete_(cx); 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); /* * If cx is the first context on this runtime, initialize well-known atoms, * keywords, numbers, and strings. If one of these steps should fail, the * runtime will be left in a partially initialized state, with zeroes and * nulls stored in the default-initialized remainder of the struct. We'll @@ -496,60 +243,40 @@ js_NewContext(JSRuntime *rt, size_t stac } void 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. */ DebugOnly<JSBool> callbackStatus = cxCallback(cx, JSCONTEXT_DESTROY); JS_ASSERT(callbackStatus); } } 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 #ifdef JS_THREADSAFE || cx->outstandingRequests != 0 #endif ) { JS_ASSERT(!rt->gcRunning); @@ -561,17 +288,17 @@ js_DestroyContext(JSContext *cx, JSDestr if (last) { #ifdef JS_THREADSAFE /* * If this thread is not in a request already, begin one now so * that we wait for any racing GC started on a not-last context to * finish, before we plow ahead and unpin atoms. */ - if (cx->thread()->data.requestDepth == 0) + if (cx->runtime->requestDepth == 0) JS_BeginRequest(cx); #endif /* * Dump remaining type inference results first. This printing * depends on atoms still existing. */ { @@ -602,27 +329,19 @@ js_DestroyContext(JSContext *cx, JSDestr JS_LOCK_GC(rt); } else { if (mode == JSDCM_FORCE_GC) js_GC(cx, NULL, GC_NORMAL, gcstats::DESTROYCONTEXT); else if (mode == JSDCM_MAYBE_GC) 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 JS_UNLOCK_GC(rt); Foreground::delete_(cx); } JSContext * js_ContextIterator(JSRuntime *rt, JSBool unlocked, JSContext **iterp) @@ -640,17 +359,17 @@ js_ContextIterator(JSRuntime *rt, JSBool } JS_FRIEND_API(JSContext *) 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; #else return js_ContextIterator(rt, JS_FALSE, &iter); #endif } @@ -733,19 +452,16 @@ PopulateReportBlame(JSContext *cx, JSErr * Instead we just invoke the errorReporter with an "Out Of Memory" * type message, and then hope the process ends swiftly. */ void 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; /* Get the message for this error, but we won't expand any arguments. */ const JSErrorFormatString *efs = js_GetLocalizedErrorMessage(cx, NULL, NULL, JSMSG_OUT_OF_MEMORY); const char *msg = efs ? efs->format : "Out of memory"; @@ -1201,33 +917,27 @@ js_GetErrorMessage(void *userRef, const if ((errorNumber > 0) && (errorNumber < JSErr_Limit)) return &js_ErrorFormatString[errorNumber]; return NULL; } JSBool js_InvokeOperationCallback(JSContext *cx) { - JSRuntime *rt = cx->runtime; - ThreadData *td = JS_THREAD_DATA(cx); + JS_ASSERT_REQUEST_DEPTH(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); #ifdef JS_THREADSAFE /* * We automatically yield the current context every time the operation * callback is hit since we might be called as a result of an impending @@ -1251,53 +961,21 @@ js_InvokeOperationCallback(JSContext *cx return !cb || cb(cx); } 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) { if (!fp) fp = js_GetTopStackFrame(cx, FRAME_EXPAND_ALL); while (fp && fp->isDummyFrame()) fp = fp->prev(); JS_ASSERT_IF(fp, fp->isScriptFrame()); @@ -1349,30 +1027,26 @@ JSContext::JSContext(JSRuntime *rt) generatingError(false), #if JS_STACK_GROWTH_DIRECTION > 0 stackLimit(UINTPTR_MAX), #else stackLimit(0), #endif runtime(rt), compartment(NULL), -#ifdef JS_THREADSAFE - thread_(NULL), -#endif stack(thisDuringConstruction()), /* depends on cx->thread_ */ parseMapPool_(NULL), globalObject(NULL), argumentFormatMap(NULL), lastMessage(NULL), errorReporter(NULL), operationCallback(NULL), data(NULL), data2(NULL), #ifdef JS_THREADSAFE - atomsCompartmentIsLocked(false), outstandingRequests(0), #endif autoGCRooters(NULL), debugHooks(&rt->globalDebugHooks), securityCallbacks(NULL), resolveFlags(0), rngSeed(0), iterValue(MagicValue(JS_NO_ITER_VALUE)), @@ -1402,20 +1076,16 @@ JSContext::JSContext(JSRuntime *rt) #ifdef DEBUG checkGCRooters = NULL; #endif #endif } JSContext::~JSContext() { -#ifdef JS_THREADSAFE - JS_ASSERT(!thread_); -#endif - /* Free the stuff hanging off of cx. */ if (parseMapPool_) Foreground::delete_<ParseMapPool>(parseMapPool_); if (lastMessage) Foreground::free_(lastMessage); /* Remove any argument formatters. */ @@ -1504,25 +1174,16 @@ bool JSContext::runningWithTrustedPrincipals() const { return !compartment || compartment->principals == runtime->trustedPrincipals(); } 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); } JS_FRIEND_API(void *) JSRuntime::onOutOfMemory(void *p, size_t nbytes, JSContext *cx) { /* * Retry when we are done with the background sweeping and have stopped @@ -1544,16 +1205,29 @@ JSRuntime::onOutOfMemory(void *p, size_t if (p) return p; if (cx) js_ReportOutOfMemory(cx); return NULL; } void +JSRuntime::purge(JSContext *cx) +{ + tempLifoAlloc.freeUnused(); + gsnCache.purge(); + + /* FIXME: bug 506341 */ + propertyCache.purge(cx); + + delete_<RegExpPrivateCache>(repCache_); + repCache_ = NULL; +} + +void JSContext::purge() { if (!activeCompilations) { Foreground::delete_<ParseMapPool>(parseMapPool_); parseMapPool_ = NULL; } } @@ -1652,23 +1326,22 @@ JSContext::sizeOfIncludingThis(JSMallocS namespace JS { #if defined JS_THREADSAFE && defined DEBUG 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 } // namespace JS
--- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -118,260 +118,163 @@ struct GSNCache { inline GSNCache * GetGSNCache(JSContext *cx); struct PendingProxyOperation { PendingProxyOperation *next; 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); +typedef Vector<ScriptOpcodeCountsPair, 0, SystemAllocPolicy> ScriptOpcodeCountsVector; - 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; - +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; + + union { + jmp_buf jmpbuf; + uintptr_t words[JS_HOWMANY(sizeof(jmp_buf), sizeof(uintptr_t))]; + } registerSnapshot; - ConservativeGCThreadData conservativeGC; + /* + * 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; -#ifdef DEBUG - size_t noGCOrAllocationCheck; + 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 - - 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); - } + JS_NEVER_INLINE void recordStackTop(); #ifdef JS_THREADSAFE - void sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf, size_t *normal, size_t *temporary, - size_t *regexpCode, size_t *stackCommitted); + void updateForRequestEnd(unsigned suspendCount) { + if (suspendCount) + recordStackTop(); + else + nativeStackTop = NULL; + } #endif - /* This must be called with the GC lock held. */ - void triggerOperationCallback(JSRuntime *rt); - - /* - * Frames currently running in js::Interpret. See InterpreterFrames for - * details. - */ - InterpreterFrames *interpreterFrames; + 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<void *, - JSThread *, - js::DefaultHasher<void *>, - js::SystemAllocPolicy> Map; - - /* Linked list of all contexts in use on this thread. */ - JSCList contextList; - - /* Opaque thread-id, from NSPR's PR_GetCurrentThread(). */ - 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<ScriptOpcodeCountsPair, 0, SystemAllocPolicy> 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; /* List of compartments (protected by the GC lock). */ js::CompartmentVector compartments; /* 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; private: void *ownerThread_; public: #else public: 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; /* Compartment create/destroy callback. */ JSCompartmentCallback compartmentCallback; 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. */ /* * Set of all GC chunks with at least one allocated thing. The * conservative GC uses it to quickly check if a possible GC thing points * into an allocated chunk. */ js::GCChunkSet gcChunkSet; @@ -514,17 +417,17 @@ struct JSRuntime /* Well-known numbers held for use by this runtime's contexts. */ js::Value NaNValue; js::Value negativeInfinityValue; js::Value positiveInfinityValue; JSAtom *emptyString; - /* List of active contexts sharing this runtime; protected by gcLock. */ + /* List of active contexts sharing this runtime. */ JSCList contextList; bool hasContexts() const { return !JS_CLIST_IS_EMPTY(&contextList); } /* Per runtime debug hooks -- see jsprvtd.h and jsdbgapi.h. */ JSDebugHooks globalDebugHooks; @@ -545,30 +448,19 @@ struct JSRuntime JSCList debuggerList; /* Client opaque pointers */ void *data; #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; /* * Security callbacks set on the runtime are used by each context unless * an override is set on the context. */ @@ -582,45 +474,53 @@ struct JSRuntime /* * The propertyRemovals counter is incremented for every JSObject::clear, * and for each JSObject::remove method call that frees a slot in the given * object. See js_NativeGet and js_NativeSet in jsobj.cpp. */ 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; const char *numGrouping; /* * Weak references to lazily-created, well-known XML singletons. * * NB: Singleton objects must be carefully disconnected from the rest of * the object graph usually associated with a JSContext's global object, * including the set of standard class objects. See jsxml.c for details. */ 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_; public: void setTrustedPrincipals(JSPrincipals *p) { trustedPrincipals_ = p; } JSPrincipals *trustedPrincipals() const { return trustedPrincipals_; } /* Literal table maintained by jsatom.c functions. */ @@ -628,16 +528,20 @@ struct JSRuntime /* Tables of strings that are pre-allocated in the atomsCompartment. */ js::StaticStrings staticStrings; JSWrapObjectCallback wrapObjectCallback; 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 * reporting the OOM, we ignore it. */ int32_t inOOMReport; JSRuntime(); @@ -734,23 +638,30 @@ struct JSRuntime * This should be called after system malloc/realloc returns NULL to try * to recove some memory or to report an error. Failures in malloc and * calloc are signaled by p == null and p == reinterpret_cast<void *>(1). * Other values of p mean a realloc failure. * * 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 /* * Linked list mapping format strings for JS_{Convert,Push}Arguments{,VA} to * formatter functions. Elements are sorted in non-increasing format string * length order. */ struct JSArgumentFormatMap { @@ -894,34 +805,24 @@ struct JSContext * True if generating an error, to prevent runaway recursion. * NB: generatingError packs with throwing below. */ JSPackedBool generatingError; /* 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. */ JSCompartment *compartment; 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; /* ContextStack convenience functions */ inline bool hasfp() const { return stack.hasfp(); } inline js::StackFrame* fp() const { return stack.fp(); } inline js::StackFrame* maybefp() const { return stack.maybefp(); } inline js::FrameRegs& regs() const { return stack.regs(); } @@ -1039,27 +940,20 @@ struct JSContext return !!(runOptions & ropt); } bool hasStrictOption() const { return hasRunOption(JSOPTION_STRICT); } bool hasWErrorOption() const { return hasRunOption(JSOPTION_WERROR); } 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. */ JSCList threadLinks; /* JSThread contextList linkage */ #endif /* Stack of thread-stack-allocated GC roots. */ js::AutoGCRooter *autoGCRooters; @@ -1160,18 +1054,16 @@ struct JSContext #ifdef JS_THREADSAFE /* * When non-null JSContext::free_ delegates the job to the background * thread. */ js::GCHelperThread *gcBackgroundFree; #endif - js::ThreadData *threadData() { return JS_THREAD_DATA(this); } - inline void* malloc_(size_t bytes) { return runtime->malloc_(bytes, this); } inline void* mallocNoReport(size_t bytes) { JS_ASSERT(bytes != 0); return runtime->malloc_(bytes, NULL); } @@ -1246,23 +1138,16 @@ struct JSContext JS_FRIEND_API(size_t) sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) const; static inline JSContext *fromLinkField(JSCList *link) { JS_ASSERT(link); return reinterpret_cast<JSContext *>(uintptr_t(link) - offsetof(JSContext, link)); } -#ifdef JS_THREADSAFE - static inline JSContext *fromThreadLinks(JSCList *link) { - JS_ASSERT(link); - return reinterpret_cast<JSContext *>(uintptr_t(link) - offsetof(JSContext, threadLinks)); - } -#endif - private: /* * The allocation code calls the function to indicate either OOM failure * when p is null or that a memory pressure counter has reached some * threshold when p is not null. The function takes the pointer and not * a boolean flag to minimize the amount of code in its inlined callers. */ JS_FRIEND_API(void) checkMallocGCPressure(void *p); @@ -1320,89 +1205,40 @@ class AutoXMLRooter : private AutoGCRoot friend void AutoGCRooter::trace(JSTracer *trc); private: JSXML * const xml; JS_DECL_USE_GUARD_OBJECT_NOTIFIER }; #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; JS_DECL_USE_GUARD_OBJECT_NOTIFIER public: explicit AutoUnlockGC(JSRuntime *rt JS_GUARD_OBJECT_NOTIFIER_PARAM) : rt(rt) { JS_GUARD_OBJECT_NOTIFIER_INIT; JS_UNLOCK_GC(rt); } ~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 public: explicit AutoKeepAtoms(JSRuntime *rt JS_GUARD_OBJECT_NOTIFIER_PARAM) : rt(rt) @@ -1473,111 +1309,56 @@ class JSAutoResolveFlags ~JSAutoResolveFlags() { mContext->resolveFlags = mSaved; } private: JSContext *mContext; uintN mSaved; 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. */ class ThreadContextRange { JSCList *begin; JSCList *end; public: explicit ThreadContextRange(JSContext *cx) { -#ifdef JS_THREADSAFE - end = &cx->thread()->contextList; -#else end = &cx->runtime->contextList; -#endif begin = end->next; } bool empty() const { return begin == end; } void popFront() { JS_ASSERT(!empty()); begin = begin->next; } JSContext *front() const { -#ifdef JS_THREADSAFE - return JSContext::fromThreadLinks(begin); -#else return JSContext::fromLinkField(begin); -#endif } }; } /* namespace js */ /* * Create and destroy functions for JSContext, which is manually allocated * and exclusively owned. */ 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); /* * If unlocked, acquire and release rt->gcLock around *iterp update; otherwise * the caller must be holding rt->gcLock. */ extern JSContext * @@ -1653,53 +1434,40 @@ js_ReportValueErrorFlags(JSContext *cx, #define js_ReportValueError3(cx,errorNumber,spindex,v,fallback,arg1,arg2) \ ((void)js_ReportValueErrorFlags(cx, JSREPORT_ERROR, errorNumber, \ spindex, v, fallback, arg1, arg2)) 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 /* * If the operation callback flag was set, call the operation callback. * This macro can run the full GC. Return true if it is OK to continue and * false otherwise. */ #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 * is to be terminated. */ extern JSBool 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, * use cx->stack.currentScript or cx->stack.currentScriptedScopeChain. */ extern js::StackFrame * js_GetScriptedCaller(JSContext *cx, js::StackFrame *fp);
--- a/js/src/jscntxtinlines.h +++ b/js/src/jscntxtinlines.h @@ -82,17 +82,17 @@ GetGlobalForScopeChain(JSContext *cx) if (!scope) return NULL; return &scope->asGlobal(); } inline GSNCache * GetGSNCache(JSContext *cx) { - return &JS_THREAD_DATA(cx)->gsnCache; + return &cx->runtime->gsnCache; } class AutoNamespaceArray : protected AutoGCRooter { public: AutoNamespaceArray(JSContext *cx) : AutoGCRooter(cx, NAMESPACES) { array.init(); }
--- a/js/src/jsfriendapi.cpp +++ b/js/src/jsfriendapi.cpp @@ -556,20 +556,20 @@ CallContextDebugHandler(JSContext *cx, J if (!CanCallContextDebugHandler(cx)) return JSTRAP_RETURN; return cx->debugHooks->debuggerHandler(cx, script, bc, rval, cx->debugHooks->debuggerHandlerData); } #ifdef JS_THREADSAFE -JSThread * -GetContextThread(const JSContext *cx) +void * +GetOwnerThread(const JSContext *cx) { - return cx->thread(); + return cx->runtime->ownerThread(); } JS_FRIEND_API(unsigned) GetContextOutstandingRequests(const JSContext *cx) { return cx->outstandingRequests; } @@ -580,28 +580,28 @@ GetRuntimeGCLock(const JSRuntime *rt) } AutoSkipConservativeScan::AutoSkipConservativeScan(JSContext *cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL) : context(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 JS_FRIEND_API(JSCompartment *) GetContextCompartment(const JSContext *cx) { return cx->compartment; } @@ -621,22 +621,19 @@ SetActivityCallback(JSRuntime *rt, Activ JS_FRIEND_API(bool) IsContextRunningJS(JSContext *cx) { return !cx->stack.empty(); } 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&) GetRuntimeCompartments(JSRuntime *rt) { return rt->compartments; }
--- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -470,18 +470,18 @@ GetPCCountScriptCount(JSContext *cx); JS_FRIEND_API(JSString *) GetPCCountScriptSummary(JSContext *cx, size_t script); 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); JS_FRIEND_API(PRLock *) GetRuntimeGCLock(const JSRuntime *rt); class JS_FRIEND_API(AutoSkipConservativeScan) @@ -557,17 +557,17 @@ CanCallContextDebugHandler(JSContext *cx extern JS_FRIEND_API(JSTrapStatus) CallContextDebugHandler(JSContext *cx, JSScript *script, jsbytecode *bc, Value *rval); extern JS_FRIEND_API(bool) 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<JSCompartment*, 0, SystemAllocPolicy> CompartmentVector; extern JS_FRIEND_API(const CompartmentVector&) GetRuntimeCompartments(JSRuntime *rt); extern JS_FRIEND_API(size_t) SizeOfJSContext();
--- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -101,16 +101,22 @@ #ifdef MOZ_VALGRIND # define JS_VALGRIND #endif #ifdef JS_VALGRIND # include <valgrind/memcheck.h> #endif +#ifdef XP_WIN +# include "jswin.h" +#else +# include <unistd.h> +#endif + using namespace mozilla; using namespace js; using namespace js::gc; namespace js { namespace gc { #ifdef JS_GC_ZEAL @@ -870,25 +876,19 @@ js_InitGC(JSRuntime *rt, uint32_t maxbyt if (!rt->gcRootsHash.init(256)) return false; if (!rt->gcLocksHash.init(256)) 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 /* * Separate gcMaxMallocBytes from gcMaxBytes but initialize to maxbytes * for default backward API compatibility. */ @@ -1074,33 +1074,41 @@ MarkWordConservatively(JSTracer *trc, ui static void MarkRangeConservatively(JSTracer *trc, const uintptr_t *begin, const uintptr_t *end) { JS_ASSERT(begin <= end); for (const uintptr_t *i = begin; i < end; ++i) 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 MarkStackRangeConservatively(JSTracer *trc, Value *beginv, Value *endv) { /* * Normally, the drainMarkStack phase of marking will never trace outside * of the compartment currently being collected. However, conservative @@ -1129,69 +1137,49 @@ MarkStackRangeConservatively(JSTracer *t JS_ASSERT(begin <= end); for (const uintptr_t *i = begin; i < end; i += sizeof(Value) / sizeof(uintptr_t)) MarkWordConservatively(trc, *i); #else MarkRangeConservatively(trc, begin, end); #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) # pragma warning(disable: 4611) #endif (void) setjmp(registerSnapshot.jmpbuf); #if defined(_MSC_VER) # pragma warning(pop) #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 */ bool js_IsAddressableGCThing(JSRuntime *rt, uintptr_t w, gc::AllocKind *thingKind, void **thing) { return js::IsAddressableGCThing(rt, w, thingKind, NULL, thing) == CGCT_VALID; @@ -1259,48 +1247,45 @@ js_AddRootRT(JSRuntime *rt, jsval *vp, c /* * Due to the long-standing, but now removed, use of rt->gcLock across the * bulk of js_GC, API users have come to depend on JS_AddRoot etc. locking * properly with a racing GC, without calling JS_AddRoot from a request. * We have to preserve API compatibility here, now that we avoid holding * 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)); } JS_FRIEND_API(JSBool) js_AddGCThingRootRT(JSRuntime *rt, void **rp, const char *name) { /* * Due to the long-standing, but now removed, use of rt->gcLock across the * bulk of js_GC, API users have come to depend on JS_AddRoot etc. locking * properly with a racing GC, without calling JS_AddRoot from a request. * We have to preserve API compatibility here, now that we avoid holding * 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)); } JS_FRIEND_API(JSBool) js_RemoveRoot(JSRuntime *rt, void *rp) { /* * Due to the JS_RemoveRootRT API, we may be called outside of a request. * 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; } typedef RootedValueMap::Range RootRange; typedef RootedValueMap::Entry RootEntry; typedef RootedValueMap::Enum RootEnum; @@ -1655,19 +1640,16 @@ ArenaLists::finalizeScripts(JSContext *c finalizeNow(cx, FINALIZE_SCRIPT); } static void 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); } /* static */ void * ArenaLists::refillFreeList(JSContext *cx, AllocKind thingKind) { @@ -2095,17 +2077,17 @@ MarkWeakReferences(GCMarker *gcmarker) } static void 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()); for (GCLocks::Range r = rt->gcLocksHash.all(); !r.empty(); r.popFront()) gc_lock_traversal(r.front(), trc); if (rt->scriptPCCounters) { @@ -2136,44 +2118,42 @@ MarkRuntime(JSTracer *trc) for (CellIterUnderGC i(c, FINALIZE_SCRIPT); !i.done(); i.next()) { JSScript *script = i.get<JSScript>(); if (script->pcCounters) MarkRoot(trc, script, "profilingScripts"); } } } - 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) (*op)(trc, rt->gcBlackRootsData); if (!IS_GC_MARKING_TRACER(trc)) { /* We don't want to miss these when called from TraceRuntime. */ if (JSTraceDataOp op = rt->gcGrayRootsTraceOp) (*op)(trc, rt->gcGrayRootsData); } } 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 TriggerCompartmentGC(JSCompartment *comp, gcstats::Reason reason) { JSRuntime *rt = comp->rt; JS_ASSERT(!rt->gcRunning); @@ -2203,17 +2183,17 @@ TriggerCompartmentGC(JSCompartment *comp /* * Trigger the GC when it is safe to call an operation callback on any * thread. */ rt->gcIsNeeded = true; rt->gcTriggerCompartment = comp; rt->gcTriggerReason = reason; - TriggerAllOperationCallbacks(comp->rt); + comp->rt->triggerOperationCallback(); } void MaybeGC(JSContext *cx) { JSRuntime *rt = cx->runtime; JS_ASSERT(rt->onOwnerThread()); @@ -2376,30 +2356,47 @@ ExpireChunksAndArenas(JSRuntime *rt, boo } if (shouldShrink) DecommitArenas(rt); } #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() { if (!(wakeup = PR_NewCondVar(rt->gcLock))) return false; if (!(done = PR_NewCondVar(rt->gcLock))) return false; thread = PR_CreateThread(PR_USER_THREAD, threadMain, this, PR_PRIORITY_NORMAL, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0); if (!thread) return false; - backgroundAllocation = (js_GetCPUCount() >= 2); + backgroundAllocation = (GetCPUCount() >= 2); return true; } void GCHelperThread::finish() { PRThread *join = NULL; { @@ -2675,17 +2672,17 @@ SweepCompartments(JSContext *cx, JSGCInv static void BeginMarkPhase(JSContext *cx, GCMarker *gcmarker, JSGCInvocationKind gckind) { JSRuntime *rt = cx->runtime; for (GCCompartmentsIter c(rt); !c.done(); c.next()) c->purge(cx); - js_PurgeThreads(cx); + rt->purge(cx); { JSContext *iter = NULL; while (JSContext *acx = js_ContextIterator(rt, JS_TRUE, &iter)) acx->purge(); } /* @@ -2839,23 +2836,17 @@ SweepPhase(JSContext *cx, GCMarker *gcma { gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_XPCONNECT); if (rt->gcCallback) (void) rt->gcCallback(cx, JSGC_FINALIZE_END); } } -/* - * 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) { JSRuntime *rt = cx->runtime; rt->gcNumber++; /* Clear gcIsNeeded now, when we are about to start a normal GC cycle. */ rt->gcIsNeeded = false; @@ -2875,210 +2866,65 @@ MarkAndSweep(JSContext *cx, JSGCInvocati rt->gcIncrementalTracer = &gcmarker; BeginMarkPhase(cx, &gcmarker, gckind); gcmarker.drainMarkStack(); EndMarkPhase(cx, &gcmarker, 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); ~AutoGCSession(); private: JSContext *context; AutoGCSession(const AutoGCSession&) MOZ_DELETE; 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) { JSRuntime *rt = cx->runtime; 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; /* * We should not be depending on cx->compartment in the GC, so set it to * NULL to look for violations. */ SwitchToCompartment sc(cx, (JSCompartment *)NULL); @@ -3098,19 +2944,16 @@ GCCycle(JSContext *cx, JSCompartment *co JS_ASSERT(!cx->gcBackgroundFree); rt->gcHelperThread.waitBackgroundSweepOrAllocEnd(); if (rt->hasContexts() && rt->gcHelperThread.prepareForBackgroundSweep()) cx->gcBackgroundFree = &rt->gcHelperThread; #endif MarkAndSweep(cx, gckind); - if (!comp) - js_PurgeThreads_PostGlobalSweep(cx); - #ifdef JS_THREADSAFE if (cx->gcBackgroundFree) { JS_ASSERT(cx->gcBackgroundFree == &rt->gcHelperThread); cx->gcBackgroundFree = NULL; rt->gcHelperThread.startBackgroundSweep(cx, gckind == GC_SHRINK); } #endif @@ -3211,17 +3054,17 @@ void TraceRuntime(JSTracer *trc) { JS_ASSERT(!IS_GC_MARKING_TRACER(trc)); #ifdef JS_THREADSAFE { JSContext *cx = trc->context; JSRuntime *rt = cx->runtime; - if (rt->gcThread != cx->thread()) { + if (!rt->gcRunning) { AutoLockGC lock(rt); AutoGCSession gcsession(cx); rt->gcHelperThread.waitBackgroundSweepEnd(); AutoUnlockGC unlock(rt); AutoCopyFreeListToArenas copy(rt); RecordNativeStackTopForGC(trc->context);
--- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -1315,21 +1315,16 @@ struct WrapperHasher static bool match(const Value &l, const Value &k) { return l == k; } }; typedef HashMap<Value, Value, WrapperHasher, SystemAllocPolicy> WrapperMap; } /* namespace js */ -#ifdef DEBUG -extern bool -CheckAllocation(JSContext *cx); -#endif - extern JS_FRIEND_API(JSGCTraceKind) js_GetGCThingTraceKind(void *thing); extern JSBool js_InitGC(JSRuntime *rt, uint32_t maxbytes); extern void js_FinishGC(JSRuntime *rt); @@ -1411,32 +1406,16 @@ typedef enum JSGCInvocationKind { /* Minimize GC triggers and release empty GC chunks right away. */ GC_SHRINK = 1 } JSGCInvocationKind; /* Pass NULL for |comp| to get a full GC. */ 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 class GCHelperThread { enum State { IDLE, SWEEPING, @@ -1580,67 +1559,16 @@ struct GCChunkHasher { JS_ASSERT(!(uintptr_t(k) & gc::ChunkMask)); JS_ASSERT(!(uintptr_t(l) & gc::ChunkMask)); return k == l; } }; typedef HashSet<js::gc::Chunk *, GCChunkHasher, SystemAllocPolicy> 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<class T> struct MarkStack { T *stack; T *tos; T *limit; bool push(T item) { if (tos == limit)
--- a/js/src/jsgcinlines.h +++ b/js/src/jsgcinlines.h @@ -341,17 +341,17 @@ class CellIter: public CellIterImpl #endif if (lists->isSynchronizedFreeList(kind)) { lists = NULL; } else { JS_ASSERT(!comp->rt->gcRunning); lists->copyFreeListToArena(kind); } #ifdef DEBUG - counter = &JS_THREAD_DATA(cx)->noGCOrAllocationCheck; + counter = &cx->runtime->noGCOrAllocationCheck; ++*counter; #endif init(comp, kind); } ~CellIter() { #ifdef DEBUG JS_ASSERT(*counter > 0); @@ -379,17 +379,17 @@ inline T * NewGCThing(JSContext *cx, js::gc::AllocKind kind, size_t thingSize) { JS_ASSERT(thingSize == js::gc::Arena::thingSize(kind)); #ifdef JS_THREADSAFE JS_ASSERT_IF((cx->compartment == cx->runtime->atomsCompartment), 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()) js::gc::RunDebugGC(cx); #endif js::gc::MaybeCheckStackRoots(cx); @@ -406,17 +406,17 @@ inline T * TryNewGCThing(JSContext *cx, js::gc::AllocKind kind, size_t thingSize) { JS_ASSERT(thingSize == js::gc::Arena::thingSize(kind)); #ifdef JS_THREADSAFE JS_ASSERT_IF((cx->compartment == cx->runtime->atomsCompartment), 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()) return NULL; #endif void *t = cx->compartment->arenas.allocateFromFreeList(kind, thingSize); return static_cast<T *>(t);
--- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -1253,23 +1253,23 @@ class GenericInterruptEnabler : public I T *variable; T value; }; 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) void js::AssertValidPropertyCacheHit(JSContext *cx, JSObject *start, JSObject *found, PropertyCacheEntry *entry) { @@ -1549,17 +1549,17 @@ js::Interpret(JSContext *cx, StackFrame JS_END_MACRO /* * Prepare to call a user-supplied branch handler, and abort the script * if it returns false. */ #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 #define BRANCH(n) \ JS_BEGIN_MACRO \ regs.pc += (n); \ op = (JSOp) *regs.pc; \ if ((n) <= 0) \
deleted file mode 100644 --- 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 <stdlib.h> -#include <string.h> - -#ifdef XP_WIN -# include "jswin.h" -#else -# include <unistd.h> -#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 <windows.h>. - */ -#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 <libkern/OSAtomic.h> - -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<void *>(ov), - reinterpret_cast<void *>(nv), - reinterpret_cast<void * volatile *>(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 <sys/atomic_op.h> - -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; i<listc; i++) { - m->next = 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<PRStatus> 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<PRStatus> 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 */
--- a/js/src/jslock.h +++ b/js/src/jslock.h @@ -34,207 +34,54 @@ * 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 ***** */ #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 - -#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 - -#if defined(JS_USE_ONLY_NSPR_LOCKS) || !JS_HAS_NATIVE_COMPARE_AND_SWAP -# define NSPR_LOCK 1 -#else -# undef NSPR_LOCK -#endif - -#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) 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_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 +#else /* 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(); +# 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)) -#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 public: AutoAtomicIncrement(int32_t *p JS_GUARD_OBJECT_NOTIFIER_PARAM) : p(p) { JS_GUARD_OBJECT_NOTIFIER_INIT; JS_ATOMIC_INCREMENT(p); } ~AutoAtomicIncrement() { JS_ATOMIC_DECREMENT(p); } }; -} /* namespace js */ - -#endif +} /* namespace js */ #endif /* jslock_h___ */
deleted file mode 100644 --- 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
--- a/js/src/jsnum.cpp +++ b/js/src/jsnum.cpp @@ -103,17 +103,17 @@ ComputeAccurateDecimalInteger(JSContext char c = char(start[i]); JS_ASSERT(('0' <= c && c <= '9') || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z')); cstr[i] = c; } cstr[length] = 0; 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); return false; } if (err == JS_DTOA_ERANGE && *dp == HUGE_VAL) *dp = js_PositiveInfinity; cx->free_(cstr); @@ -811,17 +811,17 @@ num_to(JSContext *cx, Native native, JSD ToCStringBuf cbuf; numStr = IntToCString(&cbuf, jsint(precision)); JS_ASSERT(numStr); JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_PRECISION_RANGE, numStr); return JS_FALSE; } } - 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); return JS_FALSE; } JSString *str = js_NewStringCopyZ(cx, numStr); if (!str) return JS_FALSE; @@ -1082,20 +1082,20 @@ FracNumberToCString(JSContext *cx, ToCSt * Printing floating-point numbers quickly and accurately with integers. * Florian Loitsch, PLDI 2010. * * It fails on a small number of cases, whereupon we fall back to * js_dtostr() (which uses David Gay's dtoa). */ 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; } char * NumberToCString(JSContext *cx, ToCStringBuf *cbuf, jsdouble d, jsint base/* = 10*/) { int32_t i; @@ -1416,17 +1416,17 @@ js_strtod(JSContext *cx, const jschar *s istr = cstr; if ((negative = (*istr == '-')) != 0 || *istr == '+') istr++; if (*istr == 'I' && !strncmp(istr, js_Infinity_str, sizeof js_Infinity_str - 1)) { d = negative ? js_NegativeInfinity : js_PositiveInfinity; 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) d = js_NegativeInfinity; } i = estr - cstr; if (cstr != cbuf)
--- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -1514,31 +1514,16 @@ JSObject::getPrivateDataOffset(size_t nf struct JSObject_Slots2 : JSObject { js::Value fslots[2]; }; struct JSObject_Slots4 : JSObject { js::Value fslots[4]; }; struct JSObject_Slots8 : JSObject { js::Value fslots[8]; }; struct JSObject_Slots12 : JSObject { js::Value fslots[12]; }; 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; size_t length; JSValueArray(jsval *v, size_t c) : array(v), length(c) {} };
--- a/js/src/jsopcode.cpp +++ b/js/src/jsopcode.cpp @@ -457,17 +457,17 @@ ToDisassemblySource(JSContext *cx, jsval return false; nbytes = JS_sprintf_append(NULL, "%s", nbytes); if (!nbytes) return false; bytes->initBytes(nbytes); return true; } - if (cx->runtime->gcRunning || JS_THREAD_DATA(cx)->noGCOrAllocationCheck) { + if (cx->runtime->gcRunning || cx->runtime->noGCOrAllocationCheck) { char *source = JS_sprintf_append(NULL, "<value>"); if (!source) return false; bytes->initBytes(source); return true; } if (!JSVAL_IS_PRIMITIVE(v)) {
--- a/js/src/jsproxy.cpp +++ b/js/src/jsproxy.cpp @@ -78,17 +78,17 @@ GetFunctionProxyConstruct(JSObject *prox JS_ASSERT(IsFunctionProxy(proxy)); JS_ASSERT(proxy->slotSpan() > JSSLOT_PROXY_CONSTRUCT); return proxy->getSlotRef(JSSLOT_PROXY_CONSTRUCT); } 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; op = op->next; } return false; } @@ -705,28 +705,28 @@ ScriptedProxyHandler::iterate(JSContext return ProxyHandler::iterate(cx, proxy, flags, vp); return Trap(cx, handler, tvr.value(), 0, NULL, vp) && ReturnedValueMustNotBePrimitive(cx, proxy, ATOM(iterate), *vp); } 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; } }; bool Proxy::getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set, PropertyDescriptor *desc) { JS_CHECK_RECURSION(cx, return false);
--- a/js/src/jsprvtd.h +++ b/js/src/jsprvtd.h @@ -81,17 +81,16 @@ typedef uintptr_t jsatomid; /* Struct typedefs. */ typedef struct JSArgumentFormatMap JSArgumentFormatMap; typedef struct JSGCThing JSGCThing; 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. */ typedef struct JSAtomState JSAtomState; typedef struct JSCodeSpec JSCodeSpec; typedef struct JSPrinter JSPrinter; typedef struct JSStackHeader JSStackHeader; typedef struct JSSubString JSSubString;
--- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -776,17 +776,17 @@ JSScript::initCounts(JSContext *cx) cursor += capacity * sizeof(double); next = pc + GetBytecodeLength(pc); } JS_ASSERT(size_t(cursor - base) == bytes); /* 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; } void JSScript::destroyCounts(JSContext *cx) { @@ -1730,17 +1730,17 @@ JSScript::ensureHasDebug(JSContext *cx) return false; /* * Ensure that any Interpret() instances running on this script have * interrupts enabled. The interrupts must stay enabled until the * 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; } bool JSScript::recompileForStepMode(JSContext *cx) {
deleted file mode 100644 --- 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 -! -! ====================================================================== -!
deleted file mode 100644 --- 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 -! -! ====================================================================== -!
--- a/js/src/methodjit/Compiler.cpp +++ b/js/src/methodjit/Compiler.cpp @@ -3928,29 +3928,17 @@ mjit::Compiler::emitStubCall(void *ptr, void mjit::Compiler::interruptCheckHelper() { Jump jump; if (cx->runtime->gcZeal() >= js::gc::ZealVerifierThreshold) { /* 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 /* Handle processors that can't load from absolute addresses. */ RegisterID reg = frame.allocReg(); masm.move(ImmPtr(interrupt), reg); jump = masm.branchTest32(Assembler::NonZero, Address(reg, 0)); frame.freeReg(reg);
--- a/js/src/methodjit/StubCalls.cpp +++ b/js/src/methodjit/StubCalls.cpp @@ -1892,17 +1892,17 @@ stubs::InvariantFailure(VMFrame &f, void return rval; } void JS_FASTCALL 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(); f.cx->clearPendingException(); } void JS_FASTCALL stubs::FunctionFramePrologue(VMFrame &f)
--- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -3450,17 +3450,17 @@ CancelExecution(JSRuntime *rt) { gCanceled = true; if (gExitCode == 0) gExitCode = EXITCODE_TIMEOUT; #ifdef JS_THREADSAFE 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) /* It is not safe to call fputs from signals. */ /* Dummy assignment avoids GCC warning on "attribute warn_unused_result" */ ssize_t dummy = write(2, msg, sizeof(msg) - 1); (void)dummy; #else
--- a/js/src/shell/jsworkers.cpp +++ b/js/src/shell/jsworkers.cpp @@ -50,16 +50,26 @@ #include "jscntxt.h" #include "jsdbgapi.h" #include "jsstdint.h" #include "jslock.h" #include "jsworkers.h" 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. * * == Object lifetime rules == * * - The ThreadPool lasts from init() to finish(). * * - The ThreadPool owns the MainQueue and the WorkerQueue. Those live from @@ -145,17 +155,17 @@ class Worker; class WorkerParent { protected: typedef HashSet<Worker *, DefaultHasher<Worker *>, SystemAllocPolicy> ChildSet; ChildSet children; 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; bool addChild(Worker *w) { AutoLock hold(getLock()); return children.put(w) != NULL; } @@ -172,65 +182,65 @@ class WorkerParent { void notifyTerminating(); }; template <class T> class ThreadSafeQueue { protected: Queue<T, SystemAllocPolicy> queue; - JSLock *lock; + PRLock *lock; PRCondVar *condvar; bool closed; private: Vector<T, 8, SystemAllocPolicy> busy; protected: ThreadSafeQueue() : lock(NULL), condvar(NULL), closed(false) {} ~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. virtual bool shouldStop() { return closed; } public: 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) { AutoLock hold(lock); if (closed) return false; if (queue.empty()) - JS_NOTIFY_ALL_CONDVAR(condvar); + PR_NotifyAllCondVar(condvar); return queue.push(t); } void close() { AutoLock hold(lock); closed = true; queue.clear(); - JS_NOTIFY_ALL_CONDVAR(condvar); + PR_NotifyAllCondVar(condvar); } // The caller must hold the lock. bool take(T *t) { 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); return true; } // The caller must hold the lock. void drop(T item) { @@ -248,17 +258,17 @@ class ThreadSafeQueue bool isIdle() { AutoLock hold(lock); return lockedIsIdle(); } void wake() { AutoLock hold(lock); - JS_NOTIFY_ALL_CONDVAR(condvar); + PR_NotifyAllCondVar(condvar); } void trace(JSTracer *trc) { AutoLock hold(lock); for (T *p = busy.begin(); p != busy.end(); p++) (*p)->trace(trc); queue.trace(trc); } @@ -366,17 +376,17 @@ class MainQueue MOZ_FINAL : public Event bool init() { return initThreadSafeQueue() && initWorkerParent(); } void destroy(JSContext *cx) { while (!queue.empty()) queue.pop()->destroy(cx); delete this; } - virtual JSLock *getLock() { return lock; } + virtual PRLock *getLock() { return lock; } virtual ThreadPool *getThreadPool() { return threadPool; } protected: virtual bool shouldStop(); public: virtual bool post(Event *event) { return EventQueue::post(event); } @@ -385,17 +395,17 @@ class MainQueue MOZ_FINAL : public Event void traceChildren(JSTracer *trc) { EventQueue::trace(trc); } JSBool mainThreadWork(JSContext *cx, bool continueOnError) { JSAutoSuspendRequest suspend(cx); AutoLock hold(lock); Event *event; while (take(&event)) { - JS_RELEASE_LOCK(lock); + PR_Unlock(lock); Event::Result result; { JSAutoRequest req(cx); result = event->process(cx); if (result == Event::forwardToParent) { // FIXME - pointlessly truncates the string to 8 bits jsval data; JSAutoByteString bytes; @@ -409,17 +419,17 @@ class MainQueue MOZ_FINAL : public Event result = Event::fail; } if (result == Event::fail && continueOnError) { if (JS_IsExceptionPending(cx) && !JS_ReportPendingException(cx)) JS_ClearPendingException(cx); result = Event::ok; } } - JS_ACQUIRE_LOCK(lock); + PR_Lock(lock); drop(event); event->destroy(cx); if (result != Event::ok) return false; } return true; } }; @@ -591,17 +601,17 @@ class ThreadPool class Worker MOZ_FINAL : public WorkerParent { private: ThreadPool *threadPool; WorkerParent *parent; JSObject *object; // Worker object exposed to parent JSRuntime *runtime; JSContext *context; - JSLock *lock; + PRLock *lock; Queue<Event *, SystemAllocPolicy> events; // owning pointers to pending events Event *current; bool terminated; int32_t terminateFlag; static JSClass jsWorkerClass; Worker() @@ -611,17 +621,17 @@ class Worker MOZ_FINAL : public WorkerPa bool init(JSContext *parentcx, WorkerParent *parent, JSObject *obj) { JS_ASSERT(!threadPool && !this->parent && !object && !lock); if (!initWorkerParent() || !parent->addChild(this)) return false; threadPool = parent->getThreadPool(); this->parent = parent; this->object = obj; - lock = JS_NEW_LOCK(); + lock = PR_NewLock(); return lock && createRuntime(parentcx) && createContext(parentcx, parent) && JS_SetPrivate(parentcx, obj, this); } bool createRuntime(JSContext *parentcx) { runtime = JS_NewRuntime(1L * 1024L * 1024L); @@ -676,17 +686,16 @@ class Worker MOZ_FINAL : public WorkerPa ctor = JS_GetConstructor(context, proto); if (!ctor) goto bad; js::SetFunctionNativeReserved(post, 0, PRIVATE_TO_JSVAL(this)); JS_EndRequest(context); - JS_ClearContextThread(context); return true; bad: JS_EndRequest(context); JS_DestroyContext(context); context = NULL; return false; } @@ -764,23 +773,22 @@ class Worker MOZ_FINAL : public WorkerPa dispose(); } void dispose() { JS_ASSERT(!current); 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; } if (runtime) { JS_DestroyRuntime(runtime); runtime = NULL; } object = NULL; @@ -796,17 +804,17 @@ class Worker MOZ_FINAL : public WorkerPa JSString *scriptName, JSObject *obj); JSObject *asObject() { return object; } JSObject *getGlobal() { return JS_GetGlobalObject(context); } WorkerParent *getParent() { return parent; } - virtual JSLock *getLock() { return lock; } + virtual PRLock *getLock() { return lock; } virtual ThreadPool *getThreadPool() { return threadPool; } bool post(Event *event) { AutoLock hold(lock); if (terminated) return false; if (!current && events.empty() && !threadPool->getWorkerQueue()->post(this)) @@ -1025,25 +1033,25 @@ MainQueue::trace(JSTracer *trc) } void WorkerQueue::work() { AutoLock hold(lock); 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); } } } const bool mswin = #ifdef XP_WIN true #else @@ -1131,18 +1139,16 @@ Worker::processOneEvent() AutoLock hold1(lock); if (lockedCheckTermination() || events.empty()) return; event = current = events.pop(); } JS_SetRuntimeThread(runtime); - JS_SetContextThread(context); - JS_SetNativeStackQuota(context, gMaxStackSize); Event::Result result; { JSAutoRequest ar(context); result = event->process(context); } // Note: we have to leave the above request before calling parent->post or @@ -1166,17 +1172,16 @@ Worker::processOneEvent() } if (!err) { // FIXME - out of memory, probably should panic } } if (event) event->destroy(context); - JS_ClearContextThread(context); JS_ClearRuntimeThread(runtime); { AutoLock hold2(lock); current = NULL; if (!lockedCheckTermination() && !events.empty()) { // Re-enqueue this worker. OOM here effectively kills the worker. if (!threadPool->getWorkerQueue()->post(this))
--- a/js/src/vm/RegExpObject-inl.h +++ b/js/src/vm/RegExpObject-inl.h @@ -258,32 +258,22 @@ RegExpObject::setMultiline(bool enabled) inline void RegExpObject::setSticky(bool enabled) { setSlot(STICKY_FLAG_SLOT, BooleanValue(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<RegExpPrivate> *result) { - RegExpPrivateCache *cache = getOrCreateCache(cx); + RegExpPrivateCache *cache = cx->runtime->getRegExpPrivateCache(cx); if (!cache) return false; if (RegExpPrivateCache::Ptr p = cache->lookup(atom)) { RegExpPrivateCacheValue &cacheValue = p->value; if (cacheValue.kind() == targetKind && cacheValue.rep()->getFlags() == flags) { NeedsIncRef<RegExpPrivate> cached(cacheValue.rep()); cached->incref(cx); @@ -302,17 +292,17 @@ detail::RegExpPrivate::cacheInsert(JSCon { JS_ASSERT(priv); /* * Note: allocation performed since lookup may cause a garbage collection, * 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; if (RegExpPrivateCache::AddPtr addPtr = cache->lookupForAdd(atom)) { /* We clobber existing entries with the same source (but different flags or kind). */ JS_ASSERT(addPtr->value.rep()->getFlags() != priv->getFlags() || addPtr->value.kind() != kind); addPtr->value.reset(priv, kind); @@ -396,30 +386,30 @@ detail::RegExpPrivateCode::compile(JSCon /* * The YARR JIT compiler attempts to compile the parsed pattern. If * it cannot, it informs us via |codeBlock.isFallBack()|, in which * case we have to bytecode compile it. */ #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; } JSGlobalData globalData(execAlloc); jitCompile(yarrPattern, &globalData, codeBlock); if (!codeBlock.isFallBack()) return true; } #endif - WTF::BumpPointerAllocator *bumpAlloc = cx->threadData()->getOrCreateBumpPointerAllocator(cx); + WTF::BumpPointerAllocator *bumpAlloc = cx->runtime->getBumpPointerAllocator(cx); if (!bumpAlloc) { js_ReportOutOfMemory(cx); return false; } codeBlock.setFallBack(true); byteCode = byteCompile(yarrPattern, bumpAlloc).get(); return true; @@ -505,21 +495,23 @@ detail::RegExpPrivate::decref(JSContext #ifdef JS_THREADSAFE JS_OPT_ASSERT_IF(cx->runtime->gcHelperThread.getThread(), PR_GetCurrentThread() != cx->runtime->gcHelperThread.getThread()); #endif 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 this->~RegExpPrivate(); memset(this, 0xcd, sizeof(*this)); cx->free_(this); #else cx->delete_(this);
--- a/js/src/vm/RegExpObject.h +++ b/js/src/vm/RegExpObject.h @@ -383,17 +383,16 @@ class RegExpPrivate bool compile(JSContext *cx, TokenStream *ts); static inline void checkMatchPairs(JSString *input, int *buf, size_t matchItemCount); static 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<RegExpPrivate> *result); static bool cacheInsert(JSContext *cx, JSAtom *atom, RegExpPrivateCacheKind kind, RegExpPrivate *priv); public: static AlreadyIncRefed<RegExpPrivate> create(JSContext *cx, JSLinearString *source, RegExpFlag flags, TokenStream *ts);
--- a/js/src/vm/Stack.cpp +++ b/js/src/vm/Stack.cpp @@ -487,49 +487,25 @@ StackSpace::sizeOfCommitted() return (trustedEnd_ - base_) * sizeof(Value); #endif } /*****************************************************************************/ 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 { return seg_ && seg_ == space().seg_; } bool ContextStack::containsSlow(const StackFrame *target) const
--- a/js/xpconnect/loader/mozJSComponentLoader.cpp +++ b/js/xpconnect/loader/mozJSComponentLoader.cpp @@ -312,17 +312,16 @@ public: ~JSCLContextHelper(); void reportErrorAfterPop(char *buf); operator JSContext*() const {return mContext;} private: JSContext* mContext; - intN mContextThread; nsIThreadJSContextStack* mContextStack; char* mBuf; // prevent copying and assignment JSCLContextHelper(const JSCLContextHelper &) MOZ_DELETE; const JSCLContextHelper& operator=(const JSCLContextHelper &) MOZ_DELETE; }; @@ -1350,33 +1349,28 @@ mozJSComponentLoader::ModuleEntry::GetFa return NULL; return f.forget(); } //---------------------------------------------------------------------- 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); JSContext* cx = nsnull; mContextStack->Peek(&cx); mContextStack = nsnull;
--- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -941,17 +941,17 @@ XPCJSRuntime::WatchdogMain(void *arg) sleepInterval = PR_INTERVAL_NO_TIMEOUT; self->mWatchdogHibernating = true; } #ifdef DEBUG PRStatus status = #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. */ PR_NotifyCondVar(self->mWatchdogWakeup); } //static void @@ -1038,17 +1038,16 @@ void XPCJSRuntime::SystemIsBeingShutDown JSContext * XPCJSRuntime::GetJSCycleCollectionContext() { if (!mJSCycleCollectionContext) { mJSCycleCollectionContext = JS_NewContext(mJSRuntime, 0); if (!mJSCycleCollectionContext) return nsnull; - JS_ClearContextThread(mJSCycleCollectionContext); } return mJSCycleCollectionContext; } XPCJSRuntime::~XPCJSRuntime() { if (mWatchdogWakeup) { // If the watchdog thread is running, tell it to terminate waking it @@ -1062,20 +1061,18 @@ XPCJSRuntime::~XPCJSRuntime() PR_NotifyCondVar(mWatchdogWakeup); PR_WaitCondVar(mWatchdogWakeup, PR_INTERVAL_NO_TIMEOUT); } } PR_DestroyCondVar(mWatchdogWakeup); mWatchdogWakeup = nsnull; } - if (mJSCycleCollectionContext) { - JS_SetContextThread(mJSCycleCollectionContext); + if (mJSCycleCollectionContext) JS_DestroyContextNoGC(mJSCycleCollectionContext); - } #ifdef XPC_DUMP_AT_SHUTDOWN { // count the total JSContexts in use JSContext* iter = nsnull; int count = 0; while (JS_ContextIterator(mJSRuntime, &iter)) count ++; @@ -1634,40 +1631,40 @@ ReportJSRuntimeStats(const JS::IterateDa callback, closure); ReportMemoryBytes(pathPrefix + NS_LITERAL_CSTRING("runtime/contexts"), nsIMemoryReporter::KIND_HEAP, data.runtimeContexts, "Memory used by JSContext objects and certain structures " "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 + NS_LITERAL_CSTRING("gc-heap-chunk-dirty-unused"), &gcTotal, data.gcHeapChunkDirtyUnused, "Memory on the garbage-collected JavaScript heap, within chunks with at " "least one allocated GC thing, that could be holding useful data but " "currently isn't. Memory here is mutually exclusive with memory reported" @@ -1960,19 +1957,19 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* xpc::WrapperFactory::Rewrap, xpc::WrapperFactory::PrepareForWrapping); js::SetPreserveWrapperCallback(mJSRuntime, PreserveWrapper); #ifdef MOZ_CRASHREPORTER 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); NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSGCHeap)); NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSSystemCompartmentCount)); NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSUserCompartmentCount)); NS_RegisterMemoryMultiReporter(new XPConnectJSCompartmentsMultiReporter); }
--- a/js/xpconnect/src/XPCThreadContext.cpp +++ b/js/xpconnect/src/XPCThreadContext.cpp @@ -50,17 +50,16 @@ using namespace mozilla; /***************************************************************************/ XPCJSContextStack::~XPCJSContextStack() { if (mOwnSafeJSContext) { - JS_SetContextThread(mOwnSafeJSContext); JS_DestroyContext(mOwnSafeJSContext); mOwnSafeJSContext = nsnull; } } JSContext* XPCJSContextStack::Pop() { @@ -104,17 +103,16 @@ GetPrincipalFromCx(JSContext *cx) return globalData->GetPrincipal(); } return nsnull; } bool XPCJSContextStack::Push(JSContext *cx) { - MOZ_ASSERT_IF(cx, JS_GetContextThread(cx)); if (mStack.Length() == 0) { mStack.AppendElement(cx); return true; } XPCJSContextInfo &e = mStack[mStack.Length() - 1]; if (e.cx) { if (e.cx == cx) { @@ -418,17 +416,17 @@ XPCPerThreadData::GetDataImpl(JSContext if (PR_FAILURE == PR_SetThreadPrivate(gTLSIndex, data)) { NS_ERROR("PR_SetThreadPrivate failed!"); delete data; return nsnull; } } if (cx && !sMainJSThread && NS_IsMainThread()) { - sMainJSThread = js::GetContextThread(cx); + sMainJSThread = js::GetOwnerThread(cx); sMainThreadData = data; sMainThreadData->mThread = PR_GetCurrentThread(); } return data; }
--- a/js/xpconnect/src/XPCWrappedJSClass.cpp +++ b/js/xpconnect/src/XPCWrappedJSClass.cpp @@ -63,20 +63,18 @@ bool AutoScriptEvaluate::StartEvaluating if (!mJSContext) return true; mEvaluated = true; if (!JS_GetErrorReporter(mJSContext)) { 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; // Saving the exception state keeps us from interfering with another script // that may also be running on this context. This occurred first with the // js debugger, as described in // http://bugzilla.mozilla.org/show_bug.cgi?id=88130 but presumably could // show up in any situation where a script calls into a wrapped js component @@ -97,18 +95,17 @@ AutoScriptEvaluate::~AutoScriptEvaluate( { if (!mJSContext || !mEvaluated) return; if (mState) JS_RestoreExceptionState(mJSContext, mState); 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 // been executed. // // Note: We rely on the rule that if any JSContext in our JSRuntime has // private data that points to an nsISupports subclass, it has also set // the JSOPTION_PRIVATE_IS_NSISUPPORTS option. @@ -538,18 +535,18 @@ GetContextFromObject(JSObject *obj) if (!ac.enter(ccx, obj)) return nsnull; XPCWrappedNativeScope* scope = XPCWrappedNativeScope::FindInJSObjectScope(ccx, obj); XPCContext *xpcc = scope->GetContext(); if (xpcc) { JSContext *cx = xpcc->GetJSContext(); - if (JS_GetContextThread(cx) == JS_GetCurrentThread()) - return cx; + JS_AbortIfWrongThread(JS_GetRuntime(cx)); + return cx; } return nsnull; } class SameOriginCheckedComponent : public nsISecurityCheckedComponent { public:
--- a/js/xpconnect/src/nsXPConnect.cpp +++ b/js/xpconnect/src/nsXPConnect.cpp @@ -549,24 +549,20 @@ nsXPConnect::BeginCycleCollection(nsCycl // It is important not to call GetSafeJSContext while on the // cycle-collector thread since this context will be destroyed // asynchronously and race with the main thread. In particular, we must // ensure that a context is passed to the XPCCallContext constructor. JSContext *cx = mRuntime->GetJSCycleCollectionContext(); 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; } static bool gcHasRun = false; if (!gcHasRun) { JSRuntime* rt = JS_GetRuntime(mCycleCollectionContext->GetJSContext()); if (!rt) NS_RUNTIMEABORT("Failed to get JS runtime!"); @@ -605,21 +601,25 @@ nsXPConnect::BeginCycleCollection(nsCycl NoteWeakMapsTracer trc(mCycleCollectionContext->GetJSContext(), TraceWeakMapping, cb); js::TraceWeakMaps(&trc); 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 nsXPConnect::NotifyEnterCycleCollectionThread() { NS_ABORT_IF_FALSE(!NS_IsMainThread(), "On main thread"); JS_SetRuntimeThread(mRuntime->GetJSRuntime()); } @@ -636,21 +636,18 @@ nsXPConnect::NotifyEnterMainThread() { NS_ABORT_IF_FALSE(NS_IsMainThread(), "Off main thread"); JS_SetRuntimeThread(mRuntime->GetJSRuntime()); } nsresult nsXPConnect::FinishTraverse() { - if (mCycleCollectionContext) { - JSContext *cx = mCycleCollectionContext->GetJSContext(); + if (mCycleCollectionContext) mCycleCollectionContext = nsnull; - JS_ClearContextThread(cx); - } return NS_OK; } nsresult nsXPConnect::FinishCycleCollection() { #ifdef DEBUG_CC if (mJSRoots.ops) {
--- a/js/xpconnect/src/xpcprivate.h +++ b/js/xpconnect/src/xpcprivate.h @@ -534,17 +534,17 @@ public: // nsCycleCollectionParticipant NS_IMETHOD Root(void *p); NS_IMETHOD Unlink(void *p); NS_IMETHOD Unroot(void *p); NS_IMETHOD Traverse(void *p, nsCycleCollectionTraversalCallback &cb); // nsCycleCollectionLanguageRuntime - virtual void NotifyLeaveMainThread(); + virtual bool NotifyLeaveMainThread(); virtual void NotifyEnterCycleCollectionThread(); virtual void NotifyLeaveCycleCollectionThread(); virtual void NotifyEnterMainThread(); virtual nsresult BeginCycleCollection(nsCycleCollectionTraversalCallback &cb, bool explainExpectedLiveGarbage); virtual nsresult FinishTraverse(); virtual nsresult FinishCycleCollection(); virtual nsCycleCollectionParticipant *ToParticipant(void *p); @@ -3660,19 +3660,17 @@ public: { // Do a release-mode assert that we're not doing anything significant in // XPConnect off the main thread. If you're an extension developer hitting // this, you need to change your code. See bug 716167. if (!NS_LIKELY(NS_IsMainThread() || NS_IsCycleCollectorThread())) 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; } return GetDataImpl(cx); } @@ -3765,17 +3763,17 @@ public: void ClearWrappedNativeThreadsafetyReportDepth() {mWrappedNativeThreadsafetyReportDepth = 0;} #endif static void ShutDown() {sMainJSThread = nsnull; sMainThreadData = nsnull;} static bool IsMainThread(JSContext *cx) - { return js::GetContextThread(cx) == sMainJSThread; } + { return js::GetOwnerThread(cx) == sMainJSThread; } private: XPCPerThreadData(); static XPCPerThreadData* GetDataImpl(JSContext *cx); private: XPCJSContextStack* mJSContextStack; XPCPerThreadData* mNextThread;
--- a/js/xpconnect/tests/chrome/Makefile.in +++ b/js/xpconnect/tests/chrome/Makefile.in @@ -70,18 +70,16 @@ include $(topsrcdir)/config/rules.mk test_bug679861.xul \ test_APIExposer.xul \ test_bug664689.xul \ test_precisegc.xul \ test_nodelists.xul \ 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 # wrappers. # test_wrappers-2.xul \ libs:: $(_CHROME_FILES) $(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/chrome/$(relativesrcdir)
deleted file mode 100644 --- a/js/xpconnect/tests/chrome/test_ccbeginfail.xul +++ /dev/null @@ -1,67 +0,0 @@ -<?xml version="1.0"?> -<?xml-stylesheet type="text/css" href="chrome://global/skin"?> -<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?> -<!-- -https://bugzilla.mozilla.org/show_bug.cgi?id=710761 ---> -<window title="Mozilla Bug 710761" - xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> - <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> - - <!-- test results are displayed in the html:body --> - <body xmlns="http://www.w3.org/1999/xhtml"> - <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=710761" - target="_blank">Mozilla Bug 710761</a> - </body> - - <!-- test code goes here --> - <script type="application/javascript"> - <![CDATA[ - /** Test for Bug 710761 **/ - - let noCallbacks = true; - - var beginFailListener = { - QueryInterface: function QueryInterface(aIID) { - if (aIID.equals(Components.interfaces.nsICycleCollectorListener) || - aIID.equals(Components.interfaces.nsISupports)) - return this; - throw Components.results.NS_NOINTERFACE; - }, - - /* nsICycleCollectorListener */ - begin: function () { - throw Components.results.NS_ERROR_FAILURE; - }, - noteRefCountedObject: function (addr, rc, descr) { - noCallbacks = false; - }, - noteGCedObject: function (addr, marked, descr) { - noCallbacks = false; - }, - noteEdge: function (addr, descr) { - noCallbacks = false; - }, - beginResults: function () { - noCallbacks = false; - }, - describeRoot: function (addr, known) { - noCallbacks = false; - }, - describeGarbage: function (addr) { - noCallbacks = false; - }, - end: function () { - noCallbacks = false; - }, - }; - - window.QueryInterface(Components.interfaces.nsIInterfaceRequestor) - .getInterface(Components.interfaces.nsIDOMWindowUtils) - .cycleCollect(beginFailListener); - - ok(noCallbacks, "If cycle collector listener begin fails, no further callbacks should be called."); - - ]]> - </script> -</window>
deleted file mode 100644 --- a/js/xpconnect/tests/chrome/test_ccdump.xul +++ /dev/null @@ -1,49 +0,0 @@ -<?xml version="1.0"?> -<?xml-stylesheet type="text/css" href="chrome://global/skin"?> -<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?> -<!-- -https://bugzilla.mozilla.org/show_bug.cgi?id=709162 ---> -<window title="Mozilla Bug 709162" - xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> - <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> - - <!-- test results are displayed in the html:body --> - <body xmlns="http://www.w3.org/1999/xhtml"> - <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=709162" - target="_blank">Mozilla Bug 709162</a> - </body> - - <!-- test code goes here --> - <script type="application/javascript"> - <![CDATA[ - /** Test for Bug 709162 **/ - - var emptyListener = { - QueryInterface: function QueryInterface(aIID) { - if (aIID.equals(Components.interfaces.nsICycleCollectorListener) || - aIID.equals(Components.interfaces.nsISupports)) - return this; - throw Components.results.NS_NOINTERFACE; - }, - - /* nsICycleCollectorListener */ - begin: function () {}, - noteRefCountedObject: function (addr, rc, descr) {}, - noteGCedObject: function (addr, marked, descr) {}, - noteEdge: function (addr, descr) {}, - beginResults: function () {}, - describeRoot: function (addr, known) {}, - describeGarbage: function (addr) {}, - end: function () {}, - }; - - window.QueryInterface(Components.interfaces.nsIInterfaceRequestor) - .getInterface(Components.interfaces.nsIDOMWindowUtils) - .cycleCollect(emptyListener); - - ok(true, "Dump cycle collector graph without crashing."); - - ]]> - </script> -</window>
--- a/xpcom/base/nsCycleCollector.cpp +++ b/xpcom/base/nsCycleCollector.cpp @@ -3743,20 +3743,23 @@ public: if (!mCollector->PrepareForCollection(&whiteNodes)) return 0; NS_ASSERTION(!mListener, "Should have cleared this already!"); if (aListener && NS_FAILED(aListener->Begin())) 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; if (mCollected) { mCollected = mCollector->FinishCollection(aListener); mCollector->CleanupAfterCollection();
--- a/xpcom/base/nsCycleCollector.h +++ b/xpcom/base/nsCycleCollector.h @@ -86,18 +86,21 @@ void nsCycleCollector_shutdown(); // If the JS runtime is registered nsCycleCollector_collect will call // nsCycleCollectionJSRuntime::Collect which will call // nsCycleCollector_doCollect, else nsCycleCollector_collect will call // nsCycleCollector_doCollect directly. 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; /** * Should we force a JavaScript GC before a CC? */ virtual bool NeedCollect() = 0;