Bug 1334845 - Create only one JSContext per helper thread, r=jandem.
authorBrian Hackett <bhackett1024@gmail.com>
Tue, 07 Feb 2017 13:20:32 -0700
changeset 341292 e2b893be9f4ca14fe0b96080092d9b19b46b5d70
parent 341291 2922efcfc78d861138c50c4d8731230d24343224
child 341293 3bd986e5c3c856b81a5b09fdd69925555ec5a93f
push id31329
push usercbook@mozilla.com
push dateWed, 08 Feb 2017 10:30:09 +0000
treeherdermozilla-central@3a95aa424665 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs1334845
milestone54.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
Bug 1334845 - Create only one JSContext per helper thread, r=jandem.
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/vm/HelperThreads.cpp
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -1026,17 +1026,17 @@ JSContext::JSContext(JSRuntime* runtime,
     if (!TlsContext.get())
         TlsContext.set(this);
 }
 
 JSContext::~JSContext()
 {
 #ifdef XP_WIN
     if (threadNative_)
-        CloseHandle((HANDLE)threadNative_);
+        CloseHandle((HANDLE)threadNative_.ref());
 #endif
 
     /* Free the stuff hanging off of cx. */
     MOZ_ASSERT(!resolvingList);
 
     if (dtoaState)
         DestroyDtoaState(dtoaState);
 
@@ -1046,16 +1046,28 @@ JSContext::~JSContext()
 #ifdef JS_SIMULATOR
     js::jit::Simulator::Destroy(simulator_);
 #endif
 
     if (TlsContext.get() == this)
         TlsContext.set(nullptr);
 }
 
+void
+JSContext::setRuntime(JSRuntime* rt)
+{
+    MOZ_ASSERT(!resolvingList);
+    MOZ_ASSERT(!compartment());
+    MOZ_ASSERT(!activation());
+    MOZ_ASSERT(!unwrappedException_.ref().initialized());
+    MOZ_ASSERT(!asyncStackForNewActivations_.ref().initialized());
+
+    runtime_ = rt;
+}
+
 bool
 JSContext::getPendingException(MutableHandleValue rval)
 {
     MOZ_ASSERT(throwing);
     rval.set(unwrappedException());
     if (IsAtomsCompartment(compartment()))
         return true;
     bool wasOverRecursed = overRecursed_;
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -88,29 +88,33 @@ struct JSContext : public JS::RootingCon
                    public js::MallocProvider<JSContext>
 {
     explicit JSContext(JSRuntime* runtime, const JS::ContextOptions& options);
     ~JSContext();
 
     bool init();
 
   private:
-    JSRuntime* const runtime_;
+    js::UnprotectedData<JSRuntime*> runtime_;
 
     // System handle for the thread this context is associated with.
-    size_t threadNative_;
+    js::WriteOnceData<size_t> threadNative_;
 
     // The thread on which this context is running, if this is performing a parse task.
     js::ThreadLocalData<js::HelperThread*> helperThread_;
 
     js::ThreadLocalData<JS::ContextOptions> options_;
 
     js::ThreadLocalData<js::gc::ArenaLists*> arenas_;
 
   public:
+    // This is used by helper threads to change the runtime their context is
+    // currently operating on.
+    void setRuntime(JSRuntime* rt);
+
     size_t threadNative() const { return threadNative_; }
 
     inline js::gc::ArenaLists* arenas() const { return arenas_; }
 
     template <typename T>
     bool isInsideCurrentZone(T thing) const {
         return thing->zoneFromAnyThread() == zone_;
     }
--- a/js/src/vm/HelperThreads.cpp
+++ b/js/src/vm/HelperThreads.cpp
@@ -868,16 +868,26 @@ GlobalHelperThreadState::checkTaskThread
             count++;
         if (count >= maxThreads)
             return false;
     }
 
     return true;
 }
 
+struct MOZ_RAII AutoSetContextRuntime
+{
+    explicit AutoSetContextRuntime(JSRuntime* rt) {
+        TlsContext.get()->setRuntime(rt);
+    }
+    ~AutoSetContextRuntime() {
+        TlsContext.get()->setRuntime(nullptr);
+    }
+};
+
 static inline bool
 IsHelperThreadSimulatingOOM(js::oom::ThreadType threadType)
 {
 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
     return js::oom::targetThread == threadType;
 #else
     return false;
 #endif
@@ -1177,25 +1187,25 @@ js::GCParallelTask::runFromMainThread(JS
     mozilla::TimeStamp timeStart = mozilla::TimeStamp::Now();
     run();
     duration_ = mozilla::TimeStamp::Now() - timeStart;
 }
 
 void
 js::GCParallelTask::runFromHelperThread(AutoLockHelperThreadState& locked)
 {
-    JSContext cx(runtime(), JS::ContextOptions());
+    AutoSetContextRuntime ascr(runtime());
     gc::AutoSetThreadIsPerformingGC performingGC;
 
     {
         AutoUnlockHelperThreadState parallelSection(locked);
         mozilla::TimeStamp timeStart = mozilla::TimeStamp::Now();
-        cx.heapState = JS::HeapState::MajorCollecting;
+        TlsContext.get()->heapState = JS::HeapState::MajorCollecting;
         run();
-        cx.heapState = JS::HeapState::Idle;
+        TlsContext.get()->heapState = JS::HeapState::Idle;
         duration_ = mozilla::TimeStamp::Now() - timeStart;
     }
 
     state = Finished;
     HelperThreadState().notifyAll(GlobalHelperThreadState::CONSUMER, locked);
 }
 
 bool
@@ -1489,17 +1499,16 @@ HelperThread::handlePromiseTaskWorkload(
     MOZ_ASSERT(HelperThreadState().canStartPromiseTask(locked));
     MOZ_ASSERT(idle());
 
     PromiseTask* task = HelperThreadState().promiseTasks(locked).popCopy();
     currentTask.emplace(task);
 
     {
         AutoUnlockHelperThreadState unlock(locked);
-        JSContext cx(nullptr, JS::ContextOptions());
 
         task->execute();
 
         if (!task->runtime()->finishAsyncTaskCallback(task)) {
             // We cannot simply delete the task now because the PromiseTask must
             // be destroyed on its runtime's thread. Add it to a list of tasks
             // to delete before the next GC.
             AutoEnterOOMUnsafeRegion oomUnsafe;
@@ -1543,17 +1552,17 @@ HelperThread::handleIonWorkload(AutoLock
     {
         AutoUnlockHelperThreadState unlock(locked);
 
         TraceLoggerThread* logger = TraceLoggerForCurrentThread();
         TraceLoggerEvent event(logger, TraceLogger_AnnotateScripts, builder->script());
         AutoTraceLog logScript(logger, event);
         AutoTraceLog logCompile(logger, TraceLogger_IonCompilation);
 
-        JSContext cx(rt, JS::ContextOptions());
+        AutoSetContextRuntime ascr(rt);
         jit::JitContext jctx(jit::CompileRuntime::get(rt),
                              jit::CompileCompartment::get(builder->script()->compartment()),
                              &builder->alloc());
         builder->setBackgroundCodegen(jit::CompileBackEnd(builder));
     }
 
     FinishOffThreadIonCompile(builder, locked);
     currentTask.reset();
@@ -1660,23 +1669,23 @@ HelperThread::handleParseWorkload(AutoLo
 
     currentTask.emplace(HelperThreadState().parseWorklist(locked).popCopy());
     ParseTask* task = parseTask();
     task->cx->setHelperThread(this);
 
     for (size_t i = 0; i < ArrayLength(task->cx->nativeStackLimit); i++)
         task->cx->nativeStackLimit[i] = stackLimit;
 
-    MOZ_ASSERT(!TlsContext.get());
+    JSContext* oldcx = TlsContext.get();
     TlsContext.set(task->cx);
     {
         AutoUnlockHelperThreadState unlock(locked);
         task->parse();
     }
-    TlsContext.set(nullptr);
+    TlsContext.set(oldcx);
 
     // The callback is invoked while we are still off the main thread.
     task->callback(task, task->callbackData);
 
     // FinishOffThreadScript will need to be called on the script to
     // migrate it into the correct compartment.
     {
         AutoEnterOOMUnsafeRegion oomUnsafe;
@@ -1858,17 +1867,17 @@ void
 HelperThread::handleGCHelperWorkload(AutoLockHelperThreadState& locked)
 {
     MOZ_ASSERT(HelperThreadState().canStartGCHelperTask(locked));
     MOZ_ASSERT(idle());
 
     currentTask.emplace(HelperThreadState().gcHelperWorklist(locked).popCopy());
     GCHelperState* task = gcHelperTask();
 
-    JSContext cx(task->runtime(), JS::ContextOptions());
+    AutoSetContextRuntime ascr(task->runtime());
 
     {
         AutoUnlockHelperThreadState unlock(locked);
         task->work();
     }
 
     currentTask.reset();
     HelperThreadState().notifyAll(GlobalHelperThreadState::CONSUMER, locked);
@@ -1877,16 +1886,18 @@ HelperThread::handleGCHelperWorkload(Aut
 void
 HelperThread::threadLoop()
 {
     MOZ_ASSERT(CanUseExtraThreads());
 
     JS::AutoSuppressGCAnalysis nogc;
     AutoLockHelperThreadState lock;
 
+    JSContext cx(nullptr, JS::ContextOptions());
+
     // Compute the thread's stack limit, for over-recursed checks.
     uintptr_t stackLimit = GetNativeStackBase();
 #if JS_STACK_GROWTH_DIRECTION > 0
     stackLimit += HELPER_STACK_QUOTA;
 #else
     stackLimit -= HELPER_STACK_QUOTA;
 #endif