author | Brian Hackett <bhackett1024@gmail.com> |
Thu, 25 Apr 2013 13:55:33 -0600 | |
changeset 129956 | e478c0c1940f1db2448ae67f77f80f32021223fc |
parent 129955 | e73333270ce5d106c1e59d21fbfc2a3feff56539 |
child 129957 | 165352b12d5ef4dfbff5cbde7c7a06bd66d576eb |
push id | 24596 |
push user | ryanvm@gmail.com |
push date | Sat, 27 Apr 2013 01:20:57 +0000 |
treeherder | mozilla-central@0e45f1b9521f [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | djvj, luke |
bugs | 864957 |
milestone | 23.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/src/ion/AsmJSLink.cpp +++ b/js/src/ion/AsmJSLink.cpp @@ -256,30 +256,30 @@ AsmJSActivation::AsmJSActivation(JSConte { if (cx->runtime->spsProfiler.enabled()) { profiler_ = &cx->runtime->spsProfiler; profiler_->enterNative("asm.js code", this); } prev_ = cx_->runtime->mainThread.asmJSActivationStack_; - PerThreadData::AsmJSActivationStackLock lock(cx_->runtime->mainThread); + JSRuntime::AutoLockForOperationCallback lock(cx_->runtime); cx_->runtime->mainThread.asmJSActivationStack_ = this; (void) errorRejoinSP_; // squelch GCC warning } AsmJSActivation::~AsmJSActivation() { if (profiler_) profiler_->exitNative(); JS_ASSERT(cx_->runtime->mainThread.asmJSActivationStack_ == this); - PerThreadData::AsmJSActivationStackLock lock(cx_->runtime->mainThread); + JSRuntime::AutoLockForOperationCallback lock(cx_->runtime); cx_->runtime->mainThread.asmJSActivationStack_ = prev_; } static const unsigned ASM_MODULE_SLOT = 0; static const unsigned ASM_EXPORT_INDEX_SLOT = 1; static JSBool CallAsmJS(JSContext *cx, unsigned argc, Value *vp)
--- a/js/src/ion/AsmJSSignalHandlers.cpp +++ b/js/src/ion/AsmJSSignalHandlers.cpp @@ -971,17 +971,17 @@ EnsureAsmJSSignalHandlersInstalled(JSRun // the innermost asm.js module activation's code. This will trigger a SIGSEGV, // taking us into AsmJSFaultHandler. From there, we can manually redirect // execution to call js_HandleExecutionInterrupt. The memory is un-protected // from the signal handler after control flow is redirected. void js::TriggerOperationCallbackForAsmJSCode(JSRuntime *rt) { #if defined(JS_ASMJS) - PerThreadData::AsmJSActivationStackLock lock(rt->mainThread); + JS_ASSERT(rt->currentThreadOwnsOperationCallbackLock()); AsmJSActivation *activation = rt->mainThread.asmJSActivationStackFromAnyThread(); if (!activation) return; const AsmJSModule &module = activation->module(); # if defined(XP_WIN)
--- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -704,56 +704,30 @@ JitSupportsFloatingPoint() } PerThreadData::PerThreadData(JSRuntime *runtime) : PerThreadDataFriendFields(), runtime_(runtime), ionTop(NULL), ionJSContext(NULL), ionStackLimit(0), -#ifdef JS_THREADSAFE - ionStackLimitLock_(NULL), -#endif ionActivation(NULL), asmJSActivationStack_(NULL), -#ifdef JS_THREADSAFE - asmJSActivationStackLock_(NULL), -#endif suppressGC(0) {} -bool -PerThreadData::init() -{ -#ifdef JS_THREADSAFE - ionStackLimitLock_ = PR_NewLock(); - if (!ionStackLimitLock_) - return false; - - asmJSActivationStackLock_ = PR_NewLock(); - if (!asmJSActivationStackLock_) - return false; -#endif - return true; -} - -PerThreadData::~PerThreadData() -{ -#ifdef JS_THREADSAFE - if (ionStackLimitLock_) - PR_DestroyLock(ionStackLimitLock_); - - if (asmJSActivationStackLock_) - PR_DestroyLock(asmJSActivationStackLock_); -#endif -} - JSRuntime::JSRuntime(JSUseHelperThreads useHelperThreads) : mainThread(this), interrupt(0), +#ifdef JS_THREADSAFE + operationCallbackLock(NULL), +#ifdef DEBUG + operationCallbackOwner(NULL), +#endif +#endif atomsCompartment(NULL), systemZone(NULL), numCompartments(0), localeCallbacks(NULL), defaultLocale(NULL), #ifdef JS_THREADSAFE ownerThread_(NULL), #endif @@ -936,21 +910,22 @@ JSRuntime::JSRuntime(JSUseHelperThreads #endif } bool JSRuntime::init(uint32_t maxbytes) { #ifdef JS_THREADSAFE ownerThread_ = PR_GetCurrentThread(); + + operationCallbackLock = PR_NewLock(); + if (!operationCallbackLock) + return false; #endif - if (!mainThread.init()) - return false; - js::TlsPerThreadData.set(&mainThread); #ifdef JS_METHODJIT_SPEW JMCheckLogging(); #endif if (!js_InitGC(this, maxbytes)) return false; @@ -1014,16 +989,20 @@ JSRuntime::init(uint32_t maxbytes) jitSupportsFloatingPoint = JitSupportsFloatingPoint(); return true; } JSRuntime::~JSRuntime() { #ifdef JS_THREADSAFE clearOwnerThread(); + + JS_ASSERT(!operationCallbackOwner); + if (operationCallbackLock) + PR_DestroyLock(operationCallbackLock); #endif /* * Even though all objects in the compartment are dead, we may have keep * some filenames around because of gcKeepAtoms. */ FreeScriptData(this); @@ -3117,17 +3096,17 @@ JS_SetNativeStackQuota(JSRuntime *rt, si rt->mainThread.nativeStackLimit = rt->nativeStackBase - (stackSize - 1); } #endif // If there's no pending interrupt request set on the runtime's main thread's // ionStackLimit, then update it so that it reflects the new nativeStacklimit. #ifdef JS_ION { - PerThreadData::IonStackLimitLock lock(rt->mainThread); + JSRuntime::AutoLockForOperationCallback lock(rt); if (rt->mainThread.ionStackLimit != uintptr_t(-1)) rt->mainThread.ionStackLimit = rt->mainThread.nativeStackLimit; } #endif } /************************************************************************/
--- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -140,16 +140,18 @@ JSRuntime::sizeOfIncludingThis(JSMallocS rtSizes->scriptData = scriptDataTable.sizeOfExcludingThis(mallocSizeOf); for (ScriptDataTable::Range r = scriptDataTable.all(); !r.empty(); r.popFront()) rtSizes->scriptData += mallocSizeOf(r.front()); } void JSRuntime::triggerOperationCallback() { + AutoLockForOperationCallback lock(this); + /* * Invalidate ionTop to trigger its over-recursion check. Note this must be * set before interrupt, to avoid racing with js_InvokeOperationCallback, * into a weird state where interrupt is stuck at 0 but ionStackLimit is * MAXADDR. */ mainThread.setIonStackLimit(-1);
--- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -481,91 +481,42 @@ class PerThreadData : public js::PerThre /* * If Ion code is on the stack, and has called into C++, this will be * aligned to an Ion exit frame. */ uint8_t *ionTop; JSContext *ionJSContext; uintptr_t ionStackLimit; -# ifdef JS_THREADSAFE - /* - * Synchronizes setting of ionStackLimit so signals by triggerOperationCallback don't - * get lost. - */ - PRLock *ionStackLimitLock_; - - class IonStackLimitLock { - PerThreadData &data_; - public: - IonStackLimitLock(PerThreadData &data) : data_(data) { - JS_ASSERT(data_.ionStackLimitLock_); - PR_Lock(data_.ionStackLimitLock_); - } - ~IonStackLimitLock() { - JS_ASSERT(data_.ionStackLimitLock_); - PR_Unlock(data_.ionStackLimitLock_); - } - }; -#else - class IonStackLimitLock { - public: - IonStackLimitLock(PerThreadData &data) {} - }; -# endif - void setIonStackLimit(uintptr_t limit) { - IonStackLimitLock lock(*this); - ionStackLimit = limit; - } + inline void setIonStackLimit(uintptr_t limit); /* * This points to the most recent Ion activation running on the thread. */ js::ion::IonActivation *ionActivation; /* * asm.js maintains a stack of AsmJSModule activations (see AsmJS.h). This * stack is used by JSRuntime::triggerOperationCallback to stop long- * running asm.js without requiring dynamic polling operations in the * generated code. Since triggerOperationCallback may run on a separate * thread than the JSRuntime's owner thread all reads/writes must be - * synchronized (by asmJSActivationStackLock_). + * synchronized (by rt->operationCallbackLock). */ private: friend class js::AsmJSActivation; - /* See AsmJSActivation comment. */ + /* See AsmJSActivation comment. Protected by rt->operationCallbackLock. */ js::AsmJSActivation *asmJSActivationStack_; -# ifdef JS_THREADSAFE - /* Synchronizes pushing/popping with triggerOperationCallback. */ - PRLock *asmJSActivationStackLock_; -# endif - public: static unsigned offsetOfAsmJSActivationStackReadOnly() { return offsetof(PerThreadData, asmJSActivationStack_); } - class AsmJSActivationStackLock { -# ifdef JS_THREADSAFE - PerThreadData &data_; - public: - AsmJSActivationStackLock(PerThreadData &data) : data_(data) { - PR_Lock(data_.asmJSActivationStackLock_); - } - ~AsmJSActivationStackLock() { - PR_Unlock(data_.asmJSActivationStackLock_); - } -# else - public: - AsmJSActivationStackLock(PerThreadData &) {} -# endif - }; - js::AsmJSActivation *asmJSActivationStackFromAnyThread() const { return asmJSActivationStack_; } js::AsmJSActivation *asmJSActivationStackFromOwnerThread() const { return asmJSActivationStack_; } /* @@ -574,18 +525,16 @@ class PerThreadData : public js::PerThre * debugging facilities that cannot tolerate a GC and would rather OOM * immediately, such as utilities exposed to GDB. Setting this flag is * extremely dangerous and should only be used when in an OOM situation or * in non-exposed debugging facilities. */ int32_t suppressGC; PerThreadData(JSRuntime *runtime); - ~PerThreadData(); - bool init(); bool associatedWith(const JSRuntime *rt) { return runtime_ == rt; } }; template<class Client> struct MallocProvider { void *malloc_(size_t bytes) { @@ -685,16 +634,65 @@ struct JSRuntime : public JS::shadow::Ru js::PerThreadData mainThread; /* * If non-zero, we were been asked to call the operation callback as soon * as possible. */ volatile int32_t interrupt; +#ifdef JS_THREADSAFE + private: + /* + * Lock taken when triggering the operation callback from another thread. + * Protects all data that is touched in this process. + */ + PRLock *operationCallbackLock; +#ifdef DEBUG + PRThread *operationCallbackOwner; +#endif + public: +#endif // JS_THREADSAFE + + class AutoLockForOperationCallback { +#ifdef JS_THREADSAFE + JSRuntime *rt; + public: + AutoLockForOperationCallback(JSRuntime *rt MOZ_GUARD_OBJECT_NOTIFIER_PARAM) : rt(rt) { + MOZ_GUARD_OBJECT_NOTIFIER_INIT; + PR_Lock(rt->operationCallbackLock); +#ifdef DEBUG + rt->operationCallbackOwner = PR_GetCurrentThread(); +#endif + } + ~AutoLockForOperationCallback() { + JS_ASSERT(rt->operationCallbackOwner == PR_GetCurrentThread()); +#ifdef DEBUG + rt->operationCallbackOwner = NULL; +#endif + PR_Unlock(rt->operationCallbackLock); + } +#else // JS_THREADSAFE + public: + AutoLockForOperationCallback(JSRuntime *rt MOZ_GUARD_OBJECT_NOTIFIER_PARAM) { + MOZ_GUARD_OBJECT_NOTIFIER_INIT; + } +#endif // JS_THREADSAFE + + MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER + }; + + bool currentThreadOwnsOperationCallbackLock() { +#if defined(JS_THREADSAFE) && defined(DEBUG) + return operationCallbackOwner == PR_GetCurrentThread(); +#else + return true; +#endif + } + /* Default compartment. */ JSCompartment *atomsCompartment; /* Embedders can use this zone however they wish. */ JS::Zone *systemZone; /* List of compartments and zones (protected by the GC lock). */ js::ZoneVector zones; @@ -1313,16 +1311,17 @@ struct JSRuntime : public JS::shadow::Ru bool jitHardening; bool jitSupportsFloatingPoint; // Used to reset stack limit after a signaled interrupt (i.e. ionStackLimit_ = -1) // has been noticed by Ion/Baseline. void resetIonStackLimit() { + AutoLockForOperationCallback lock(this); mainThread.setIonStackLimit(mainThread.nativeStackLimit); } // Cache for ion::GetPcScript(). js::ion::PcScriptCache *ionPcScriptCache; js::ThreadPool threadPool; @@ -2030,16 +2029,23 @@ enum DestroyContextMode { extern void DestroyContext(JSContext *cx, DestroyContextMode mode); enum ErrorArgumentsType { ArgumentsAreUnicode, ArgumentsAreASCII }; +inline void +PerThreadData::setIonStackLimit(uintptr_t limit) +{ + JS_ASSERT(runtime_->currentThreadOwnsOperationCallbackLock()); + ionStackLimit = limit; +} + } /* namespace js */ #ifdef va_start extern JSBool js_ReportErrorVA(JSContext *cx, unsigned flags, const char *format, va_list ap); extern JSBool js_ReportErrorNumberVA(JSContext *cx, unsigned flags, JSErrorCallback callback,