Bug 1476405: Part 3 - Use reasonable stack sizes and create wrappers for JS threads. r?erahm,tcampbell draft
authorKris Maglione <maglione.k@gmail.com>
Thu, 19 Jul 2018 19:29:56 -0700
changeset 821042 7922493a0d83901a17f9eb298de8c2f8050d8f3c
parent 821041 68935ef593163b30172950789be82ec5730bc6c9
child 821043 9144972b2bdcfb6239929c41df78cd443ed1e7b7
push id116998
push usermaglione.k@gmail.com
push dateFri, 20 Jul 2018 20:53:25 +0000
reviewerserahm, tcampbell
bugs1476405
milestone63.0a1
Bug 1476405: Part 3 - Use reasonable stack sizes and create wrappers for JS threads. r?erahm,tcampbell For ordinary JS helper threads, we can update names and create wrappers using the existing thread profiler hooks, but we still need to update their default stack sizes to avoid huge pages. For the XPConnect JS Watchdog thread, we sometimes get a wrapper as it is, but only sometimes. And we never use a reasonable stack size. MozReview-Commit-ID: EuR3gL5JATL
js/src/vm/HelperThreads.cpp
js/xpconnect/src/XPCJSContext.cpp
--- a/js/src/vm/HelperThreads.cpp
+++ b/js/src/vm/HelperThreads.cpp
@@ -938,17 +938,22 @@ js::EnqueuePendingParseTasksAfterGC(JSRu
 bool
 js::CurrentThreadIsParseThread()
 {
     JSContext* cx = TlsContext.get();
     return cx->helperThread() && cx->helperThread()->parseTask();
 }
 #endif
 
-static const uint32_t kDefaultHelperStackSize = 2048 * 1024;
+// We want our default stack size limit to be approximately 2MB, to be safe, but
+// expect most threads to use much less. On Linux, however, requesting a stack
+// of 2MB or larger risks the kernel allocating an entire 2MB huge page for it
+// on first access, which we do not want. To avoid this possibility, we subtract
+// 2 standard VM page sizes from our default.
+static const uint32_t kDefaultHelperStackSize = 2048 * 1024 - 2 * 4096;
 static const uint32_t kDefaultHelperStackQuota = 1800 * 1024;
 
 // TSan enforces a minimum stack size that's just slightly larger than our
 // default helper stack size.  It does this to store blobs of TSan-specific
 // data on each thread's stack.  Unfortunately, that means that even though
 // we'll actually receive a larger stack than we requested, the effective
 // usable space of that stack is significantly less than what we expect.
 // To offset TSan stealing our stack space from underneath us, double the
--- a/js/xpconnect/src/XPCJSContext.cpp
+++ b/js/xpconnect/src/XPCJSContext.cpp
@@ -74,16 +74,20 @@
 
 static MOZ_THREAD_LOCAL(XPCJSContext*) gTlsContext;
 
 using namespace mozilla;
 using namespace xpc;
 using namespace JS;
 using mozilla::dom::AutoEntryScript;
 
+// The watchdog thread loop is pretty trivial, and should not require much stack
+// space to do its job. So only give it 32KiB.
+static constexpr size_t kWatchdogStackSize = 32 * 1024;
+
 static void WatchdogMain(void* arg);
 class Watchdog;
 class WatchdogManager;
 class MOZ_RAII AutoLockWatchdog final
 {
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
     Watchdog* const mWatchdog;
 
@@ -138,17 +142,17 @@ class Watchdog
         {
             AutoLockWatchdog lock(this);
 
             // Gecko uses thread private for accounting and has to clean up at thread exit.
             // Therefore, even though we don't have a return value from the watchdog, we need to
             // join it on shutdown.
             mThread = PR_CreateThread(PR_USER_THREAD, WatchdogMain, this,
                                       PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
-                                      PR_JOINABLE_THREAD, 0);
+                                      PR_JOINABLE_THREAD, kWatchdogStackSize);
             if (!mThread)
                 MOZ_CRASH("PR_CreateThread failed!");
 
             // WatchdogMain acquires the lock and then asserts mInitialized. So
             // make sure to set mInitialized before releasing the lock here so
             // that it's atomic with the creation of the thread.
             mInitialized = true;
         }
@@ -467,16 +471,18 @@ AutoLockWatchdog::~AutoLockWatchdog()
         PR_Unlock(mWatchdog->GetLock());
     }
 }
 
 static void
 WatchdogMain(void* arg)
 {
     AUTO_PROFILER_REGISTER_THREAD("JS Watchdog");
+    // Create an nsThread wrapper for the thread and register it with the thread manager.
+    Unused << NS_GetCurrentThread();
     NS_SetCurrentThreadName("JS Watchdog");
 
     Watchdog* self = static_cast<Watchdog*>(arg);
     WatchdogManager* manager = self->Manager();
 
     // Lock lasts until we return
     AutoLockWatchdog lock(self);