Backed out 8 changesets (bug 1476405) for causing frequent failures in bug 1479022. a=backout
authorCosmin Sabou <csabou@mozilla.com>
Sat, 28 Jul 2018 01:25:25 +0300
changeset 483854 77b07565d0212ca073649c0f7c21ebc3b63d9fac
parent 483853 35a17ebc4ee64460cdac22d3fb2a57e1215e9b0f
child 483884 71f410a4fe344f37447aee40403c53225ea2efdc
push id9719
push userffxbld-merge
push dateFri, 24 Aug 2018 17:49:46 +0000
treeherdermozilla-beta@719ec98fba77 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbackout
bugs1476405, 1479022
milestone63.0a1
backs outad1674e9152da31151ab9f9f099f83ca4ff2d832
e0a021b27d2c66d46ba973d66d1360678411da26
771288dbf8529d45786b42a21dc66b180bb674ca
aeebad4f2dc31901f5b63263169229455e827ac2
4831cbfd03ded9ea6dcc8d6f0797f5f80fb717c7
0b0c243a1827e193d045d6b3566c87ca87035c48
236b366fdf3731ef95e0ba75b8f24f03181343ee
c767b1b618fbdc8bc894719f5ed7ecdcc9fc5165
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
Backed out 8 changesets (bug 1476405) for causing frequent failures in bug 1479022. a=backout Backed out changeset ad1674e9152d (bug 1476405) Backed out changeset e0a021b27d2c (bug 1476405) Backed out changeset 771288dbf852 (bug 1476405) Backed out changeset aeebad4f2dc3 (bug 1476405) Backed out changeset 4831cbfd03de (bug 1476405) Backed out changeset 0b0c243a1827 (bug 1476405) Backed out changeset 236b366fdf37 (bug 1476405) Backed out changeset c767b1b618fb (bug 1476405)
dom/media/CubebUtils.cpp
ipc/chromium/src/base/platform_thread_posix.cc
ipc/chromium/src/base/platform_thread_win.cc
js/src/vm/HelperThreads.cpp
js/xpconnect/src/XPCJSContext.cpp
media/audioipc/client/src/context.rs
media/audioipc/client/src/lib.rs
toolkit/components/backgroundhangmonitor/BackgroundHangMonitor.cpp
tools/profiler/core/platform.cpp
xpcom/threads/nsThread.cpp
xpcom/threads/nsThread.h
xpcom/threads/nsThreadManager.cpp
xpcom/threads/nsThreadManager.h
--- a/dom/media/CubebUtils.cpp
+++ b/dom/media/CubebUtils.cpp
@@ -54,17 +54,16 @@
 #endif
 
 extern "C" {
 
 struct AudioIpcInitParams {
   int mServerConnection;
   size_t mPoolSize;
   size_t mStackSize;
-  void (*mThreadCreateCallback)(const char*);
 };
 
 // These functions are provided by audioipc-server crate
 extern void* audioipc_server_start();
 extern mozilla::ipc::FileDescriptor::PlatformHandleType audioipc_server_new_client(void*);
 extern void audioipc_server_stop(void*);
 // These functions are provided by audioipc-client crate
 extern int audioipc_client_init(cubeb**, const char*, const AudioIpcInitParams*);
@@ -426,19 +425,16 @@ cubeb* GetCubebContextUnlocked()
     } else {
       MOZ_DIAGNOSTIC_ASSERT(sIPCConnection);
     }
 
     AudioIpcInitParams initParams;
     initParams.mPoolSize = sAudioIPCPoolSize;
     initParams.mStackSize = sAudioIPCStackSize;
     initParams.mServerConnection = sIPCConnection->ClonePlatformHandle().release();
-    initParams.mThreadCreateCallback = [](const char* aName) {
-      PROFILER_REGISTER_THREAD(aName);
-    };
 
     MOZ_LOG(gCubebLog, LogLevel::Debug, ("%s: %d", PREF_AUDIOIPC_POOL_SIZE, (int) initParams.mPoolSize));
     MOZ_LOG(gCubebLog, LogLevel::Debug, ("%s: %d", PREF_AUDIOIPC_STACK_SIZE, (int) initParams.mStackSize));
 
     rv = audioipc_client_init(&sCubebContext, sBrandName, &initParams);
   } else {
     rv = cubeb_init(&sCubebContext, sBrandName, sCubebBackendName.get());
   }
--- a/ipc/chromium/src/base/platform_thread_posix.cc
+++ b/ipc/chromium/src/base/platform_thread_posix.cc
@@ -21,29 +21,23 @@
 #if !defined(OS_MACOSX)
 #include <unistd.h>
 #endif
 
 #if defined(OS_BSD) && !defined(OS_NETBSD) && !defined(__GLIBC__)
 #include <pthread_np.h>
 #endif
 
-#include "nsThreadUtils.h"
-
 #if defined(OS_MACOSX)
 namespace base {
 void InitThreading();
 }  // namespace
 #endif
 
 static void* ThreadFunc(void* closure) {
-  // Create a nsThread wrapper for the current platform thread, and register it
-  // with the thread manager.
-  (void) NS_GetCurrentThread();
-
   PlatformThread::Delegate* delegate =
       static_cast<PlatformThread::Delegate*>(closure);
   delegate->ThreadMain();
   return NULL;
 }
 
 // static
 PlatformThreadId PlatformThread::CurrentId() {
@@ -93,20 +87,31 @@ void PlatformThread::Sleep(int duration_
 void PlatformThread::SetName(const char* name) {
   // On linux we can get the thread names to show up in the debugger by setting
   // the process name for the LWP.  We don't want to do this for the main
   // thread because that would rename the process, causing tools like killall
   // to stop working.
   if (PlatformThread::CurrentId() == getpid())
     return;
 
-  // Using NS_SetCurrentThreadName, as opposed to using platform APIs directly,
-  // also sets the thread name on the PRThread wrapper, and allows us to
-  // retrieve it using PR_GetThreadName.
-  NS_SetCurrentThreadName(name);
+  // http://0pointer.de/blog/projects/name-your-threads.html
+  // Set the name for the LWP (which gets truncated to 15 characters).
+  // Note that glibc also has a 'pthread_setname_np' api, but it may not be
+  // available everywhere and it's only benefit over using prctl directly is
+  // that it can set the name of threads other than the current thread.
+#if defined(OS_LINUX)
+  prctl(PR_SET_NAME, reinterpret_cast<uintptr_t>(name), 0, 0, 0); 
+#elif defined(OS_NETBSD)
+  pthread_setname_np(pthread_self(), "%s", (void *)name);
+#elif defined(OS_BSD) && !defined(__GLIBC__)
+  pthread_set_name_np(pthread_self(), name);
+#elif defined(OS_SOLARIS)
+  pthread_setname_np(pthread_self(), name);
+#else
+#endif
 }
 #endif // !OS_MACOSX
 
 namespace {
 
 bool CreateThread(size_t stack_size, bool joinable,
                   PlatformThread::Delegate* delegate,
                   PlatformThreadHandle* thread_handle) {
@@ -119,19 +124,18 @@ bool CreateThread(size_t stack_size, boo
   pthread_attr_init(&attributes);
 
   // Pthreads are joinable by default, so only specify the detached attribute if
   // the thread should be non-joinable.
   if (!joinable) {
     pthread_attr_setdetachstate(&attributes, PTHREAD_CREATE_DETACHED);
   }
 
-  if (stack_size == 0)
-    stack_size = nsIThreadManager::DEFAULT_STACK_SIZE;
-  pthread_attr_setstacksize(&attributes, stack_size);
+  if (stack_size > 0)
+    pthread_attr_setstacksize(&attributes, stack_size);
 
   success = !pthread_create(thread_handle, &attributes, ThreadFunc, delegate);
 
   pthread_attr_destroy(&attributes);
   return success;
 }
 
 }  // anonymous namespace
--- a/ipc/chromium/src/base/platform_thread_win.cc
+++ b/ipc/chromium/src/base/platform_thread_win.cc
@@ -4,36 +4,30 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "base/platform_thread.h"
 
 #include "base/logging.h"
 #include "base/win_util.h"
 
-#include "nsThreadUtils.h"
-
 namespace {
 
 // The information on how to set the thread name comes from
 // a MSDN article: http://msdn2.microsoft.com/en-us/library/xcb2z8hs.aspx
 const DWORD kVCThreadNameException = 0x406D1388;
 
 typedef struct tagTHREADNAME_INFO {
   DWORD dwType;  // Must be 0x1000.
   LPCSTR szName;  // Pointer to name (in user addr space).
   DWORD dwThreadID;  // Thread ID (-1=caller thread).
   DWORD dwFlags;  // Reserved for future use, must be zero.
 } THREADNAME_INFO;
 
 DWORD __stdcall ThreadFunc(void* closure) {
-  // Create a nsThread wrapper for the current platform thread, and register it
-  // with the thread manager.
-  (void) NS_GetCurrentThread();
-
   PlatformThread::Delegate* delegate =
       static_cast<PlatformThread::Delegate*>(closure);
   delegate->ThreadMain();
   return 0;
 }
 
 }  // namespace
 
@@ -49,20 +43,34 @@ void PlatformThread::YieldCurrentThread(
 
 // static
 void PlatformThread::Sleep(int duration_ms) {
   ::Sleep(duration_ms);
 }
 
 // static
 void PlatformThread::SetName(const char* name) {
-  // Using NS_SetCurrentThreadName, as opposed to using platform APIs directly,
-  // also sets the thread name on the PRThread wrapper, and allows us to
-  // retrieve it using PR_GetThreadName.
-  NS_SetCurrentThreadName(name);
+#ifdef HAVE_SEH_EXCEPTIONS
+  // The debugger needs to be around to catch the name in the exception.  If
+  // there isn't a debugger, we are just needlessly throwing an exception.
+  if (!::IsDebuggerPresent())
+    return;
+
+  THREADNAME_INFO info;
+  info.dwType = 0x1000;
+  info.szName = name;
+  info.dwThreadID = CurrentId();
+  info.dwFlags = 0;
+
+  MOZ_SEH_TRY {
+    RaiseException(kVCThreadNameException, 0, sizeof(info)/sizeof(DWORD),
+                   reinterpret_cast<DWORD_PTR*>(&info));
+  } MOZ_SEH_EXCEPT(EXCEPTION_CONTINUE_EXECUTION) {
+  }
+#endif
 }
 
 // static
 bool PlatformThread::Create(size_t stack_size, Delegate* delegate,
                             PlatformThreadHandle* thread_handle) {
   unsigned int flags = 0;
   if (stack_size > 0) {
     flags = STACK_SIZE_PARAM_IS_A_RESERVATION;
--- a/js/src/vm/HelperThreads.cpp
+++ b/js/src/vm/HelperThreads.cpp
@@ -938,22 +938,17 @@ js::EnqueuePendingParseTasksAfterGC(JSRu
 bool
 js::CurrentThreadIsParseThread()
 {
     JSContext* cx = TlsContext.get();
     return cx->helperThread() && cx->helperThread()->parseTask();
 }
 #endif
 
-// 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 kDefaultHelperStackSize = 2048 * 1024;
 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,20 +74,16 @@
 
 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;
 
@@ -142,17 +138,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, kWatchdogStackSize);
+                                      PR_JOINABLE_THREAD, 0);
             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;
         }
@@ -471,18 +467,16 @@ 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);
 
--- a/media/audioipc/client/src/context.rs
+++ b/media/audioipc/client/src/context.rs
@@ -15,17 +15,16 @@ use futures::Future;
 use futures_cpupool::{self, CpuPool};
 use libc;
 use std::{fmt, io, mem, ptr};
 use std::ffi::{CStr, CString};
 use std::os::raw::c_void;
 use std::os::unix::io::FromRawFd;
 use std::os::unix::net;
 use std::sync::mpsc;
-use std::thread;
 use stream;
 use tokio_core::reactor::{Handle, Remote};
 use tokio_uds::UnixStream;
 
 struct CubebClient;
 
 impl rpc::Client for CubebClient {
     type Request = ServerMessage;
@@ -95,55 +94,41 @@ impl ContextOps for ClientContext {
             let _ = tx_rpc.send(rpc);
             Some(())
         }
 
         assert_not_in_callback();
 
         let (tx_rpc, rx_rpc) = mpsc::channel();
 
-        let params = CPUPOOL_INIT_PARAMS.with(|p| {
-            p.replace(None).unwrap()
-        });
-
-        let thread_create_callback = params.thread_create_callback;
-
-        let register_thread = move || {
-            if let Some(func) = thread_create_callback {
-                let thr = thread::current();
-                let name = CString::new(thr.name().unwrap()).unwrap();
-                func(name.as_ptr());
-            }
-        };
-
         let core = t!(core::spawn_thread("AudioIPC Client RPC", move || {
             let handle = core::handle();
 
-            register_thread();
-
             open_server_stream()
                 .ok()
                 .and_then(|stream| UnixStream::from_stream(stream, &handle).ok())
                 .and_then(|stream| bind_and_send_client(stream, &handle, &tx_rpc))
                 .ok_or_else(|| {
                     io::Error::new(
                         io::ErrorKind::Other,
                         "Failed to open stream and create rpc.",
                     )
                 })
         }));
 
         let rpc = t!(rx_rpc.recv());
 
-        let cpupool = futures_cpupool::Builder::new()
+        let cpupool = CPUPOOL_INIT_PARAMS.with(|p| {
+            let params = p.replace(None).unwrap();
+            futures_cpupool::Builder::new()
                 .name_prefix("AudioIPC")
-                .after_start(register_thread)
                 .pool_size(params.pool_size)
                 .stack_size(params.stack_size)
-                .create();
+                .create()
+        });
 
         let ctx = Box::new(ClientContext {
             _ops: &CLIENT_OPS as *const _,
             rpc: rpc,
             core: core,
             cpu_pool: cpupool,
         });
         Ok(unsafe { Context::from_ptr(Box::into_raw(ctx) as *mut _) })
--- a/media/audioipc/client/src/lib.rs
+++ b/media/audioipc/client/src/lib.rs
@@ -32,32 +32,29 @@ thread_local!(static IN_CALLBACK: std::c
 thread_local!(static CPUPOOL_INIT_PARAMS: InitParamsTls = std::cell::RefCell::new(None));
 
 #[repr(C)]
 #[derive(Clone, Copy, Debug)]
 pub struct AudioIpcInitParams {
     pub server_connection: c_int,
     pub pool_size: usize,
     pub stack_size: usize,
-    pub thread_create_callback: Option<extern "C" fn(*const ::std::os::raw::c_char)>,
 }
 
 #[derive(Clone, Copy, Debug)]
 struct CpuPoolInitParams {
     pub pool_size: usize,
     pub stack_size: usize,
-    pub thread_create_callback: Option<extern "C" fn(*const ::std::os::raw::c_char)>,
 }
 
 impl CpuPoolInitParams {
     pub fn init_with(params: &AudioIpcInitParams) -> Self {
         CpuPoolInitParams {
             pool_size: params.pool_size,
             stack_size: params.stack_size,
-            thread_create_callback: params.thread_create_callback,
         }
     }
 }
 
 fn set_in_callback(in_callback: bool) {
     IN_CALLBACK.with(|b| {
         assert_eq!(*b.borrow(), !in_callback);
         *b.borrow_mut() = in_callback;
--- a/toolkit/components/backgroundhangmonitor/BackgroundHangMonitor.cpp
+++ b/toolkit/components/backgroundhangmonitor/BackgroundHangMonitor.cpp
@@ -263,18 +263,17 @@ BackgroundHangManager::BackgroundHangMan
   : mShutdown(false)
   , mLock("BackgroundHangManager")
 {
   // Lock so we don't race against the new monitor thread
   MonitorAutoLock autoLock(mLock);
 
   mHangMonitorThread = PR_CreateThread(
     PR_USER_THREAD, MonitorThread, this,
-    PR_PRIORITY_LOW, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD,
-    nsIThreadManager::DEFAULT_STACK_SIZE);
+    PR_PRIORITY_LOW, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0);
 
   MOZ_ASSERT(mHangMonitorThread, "Failed to create BHR monitor thread");
 
   DebugOnly<nsresult> rv
     = NS_NewNamedThread("BHMgr Processor",
                         getter_AddRefs(mHangProcessingThread));
   MOZ_ASSERT(NS_SUCCEEDED(rv) && mHangProcessingThread,
              "Failed to create BHR processing thread");
--- a/tools/profiler/core/platform.cpp
+++ b/tools/profiler/core/platform.cpp
@@ -3293,21 +3293,16 @@ profiler_feature_active(uint32_t aFeatur
 void
 profiler_register_thread(const char* aName, void* aGuessStackTop)
 {
   DEBUG_LOG("profiler_register_thread(%s)", aName);
 
   MOZ_ASSERT_IF(NS_IsMainThread(), Scheduler::IsCooperativeThread());
   MOZ_RELEASE_ASSERT(CorePS::Exists());
 
-  // Make sure we have a nsThread wrapper for the current thread, and that NSPR
-  // knows its name.
-  (void) NS_GetCurrentThread();
-  NS_SetCurrentThreadName(aName);
-
   PSAutoLock lock(gPSMutex);
 
   void* stackTop = GetStackTop(aGuessStackTop);
   locked_register_thread(lock, aName, stackTop);
 }
 
 void
 profiler_unregister_thread()
--- a/xpcom/threads/nsThread.cpp
+++ b/xpcom/threads/nsThread.cpp
@@ -400,47 +400,105 @@ nsThread::ThreadListMutex()
 
 /* static */ LinkedList<nsThread>&
 nsThread::ThreadList()
 {
   static LinkedList<nsThread> sList;
   return sList;
 }
 
-/* static */ void
-nsThread::ClearThreadList()
-{
-  OffTheBooksMutexAutoLock mal(ThreadListMutex());
-  while (ThreadList().popFirst()) {}
-}
-
 /* static */ nsThreadEnumerator
 nsThread::Enumerate()
 {
   return {};
 }
 
 /*static*/ void
 nsThread::ThreadFunc(void* aArg)
 {
   using mozilla::ipc::BackgroundChild;
 
   ThreadInitData* initData = static_cast<ThreadInitData*>(aArg);
   nsThread* self = initData->thread;  // strong reference
 
   self->mThread = PR_GetCurrentThread();
+  self->mThreadId = uint32_t(PlatformThread::CurrentId());
   self->mVirtualThread = GetCurrentVirtualThread();
   self->mEventTarget->SetCurrentThread();
   SetupCurrentThreadForChaosMode();
 
   if (!initData->name.IsEmpty()) {
     NS_SetCurrentThreadName(initData->name.BeginReading());
   }
 
-  self->InitCommon();
+  {
+#if defined(XP_LINUX)
+    pthread_attr_t attr;
+    pthread_attr_init(&attr);
+    pthread_getattr_np(pthread_self(), &attr);
+
+    size_t stackSize;
+    pthread_attr_getstack(&attr, &self->mStackBase, &stackSize);
+
+    // Glibc prior to 2.27 reports the stack size and base including the guard
+    // region, so we need to compensate for it to get accurate accounting.
+    // Also, this behavior difference isn't guarded by a versioned symbol, so we
+    // actually need to check the runtime glibc version, not the version we were
+    // compiled against.
+    static bool sAdjustForGuardSize = ({
+#ifdef __GLIBC__
+      unsigned major, minor;
+      sscanf(gnu_get_libc_version(), "%u.%u", &major, &minor) < 2 ||
+        major < 2 || (major == 2 && minor < 27);
+#else
+      false;
+#endif
+    });
+    if (sAdjustForGuardSize) {
+      size_t guardSize;
+      pthread_attr_getguardsize(&attr, &guardSize);
+
+      // Note: This assumes that the stack grows down, as is the case on all of
+      // our tier 1 platforms. On platforms where the stack grows up, the
+      // mStackBase adjustment is unnecessary, but doesn't cause any harm other
+      // than under-counting stack memory usage by one page.
+      self->mStackBase = reinterpret_cast<char*>(self->mStackBase) + guardSize;
+      stackSize -= guardSize;
+    }
+
+    self->mStackSize = stackSize;
+
+    // This is a bit of a hack.
+    //
+    // We really do want the NOHUGEPAGE flag on our thread stacks, since we
+    // don't expect any of them to need anywhere near 2MB of space. But setting
+    // it here is too late to have an effect, since the first stack page has
+    // already been faulted in existence, and NSPR doesn't give us a way to set
+    // it beforehand.
+    //
+    // What this does get us, however, is a different set of VM flags on our
+    // thread stacks compared to normal heap memory. Which makes the Linux
+    // kernel report them as separate regions, even when they are adjacent to
+    // heap memory. This allows us to accurately track the actual memory
+    // consumption of our allocated stacks.
+    madvise(self->mStackBase, stackSize, MADV_NOHUGEPAGE);
+
+    pthread_attr_destroy(&attr);
+#elif defined(XP_WIN)
+    static const DynamicallyLinkedFunctionPtr<GetCurrentThreadStackLimitsFn>
+      sGetStackLimits(L"kernel32.dll", "GetCurrentThreadStackLimits");
+
+    if (sGetStackLimits) {
+      ULONG_PTR stackBottom, stackTop;
+      sGetStackLimits(&stackBottom, &stackTop);
+      self->mStackBase = reinterpret_cast<void*>(stackBottom);
+      self->mStackSize = stackTop - stackBottom;
+    }
+#endif
+  }
 
   // Inform the ThreadManager
   nsThreadManager::get().RegisterCurrentThread(*self);
 
   mozilla::IOInterposer::RegisterCurrentThread();
 
   // This must come after the call to nsThreadManager::RegisterCurrentThread(),
   // because that call is needed to properly set up this thread as an nsThread,
@@ -511,91 +569,16 @@ nsThread::ThreadFunc(void* aArg)
 
 #ifdef MOZ_TASK_TRACER
   FreeTraceInfo();
 #endif
 
   NS_RELEASE(self);
 }
 
-void
-nsThread::InitCommon()
-{
-  mThreadId = uint32_t(PlatformThread::CurrentId());
-
-  {
-#if defined(XP_LINUX)
-    pthread_attr_t attr;
-    pthread_attr_init(&attr);
-    pthread_getattr_np(pthread_self(), &attr);
-
-    size_t stackSize;
-    pthread_attr_getstack(&attr, &mStackBase, &stackSize);
-
-    // Glibc prior to 2.27 reports the stack size and base including the guard
-    // region, so we need to compensate for it to get accurate accounting.
-    // Also, this behavior difference isn't guarded by a versioned symbol, so we
-    // actually need to check the runtime glibc version, not the version we were
-    // compiled against.
-    static bool sAdjustForGuardSize = ({
-#ifdef __GLIBC__
-      unsigned major, minor;
-      sscanf(gnu_get_libc_version(), "%u.%u", &major, &minor) < 2 ||
-        major < 2 || (major == 2 && minor < 27);
-#else
-      false;
-#endif
-    });
-    if (sAdjustForGuardSize) {
-      size_t guardSize;
-      pthread_attr_getguardsize(&attr, &guardSize);
-
-      // Note: This assumes that the stack grows down, as is the case on all of
-      // our tier 1 platforms. On platforms where the stack grows up, the
-      // mStackBase adjustment is unnecessary, but doesn't cause any harm other
-      // than under-counting stack memory usage by one page.
-      mStackBase = reinterpret_cast<char*>(mStackBase) + guardSize;
-      stackSize -= guardSize;
-    }
-
-    mStackSize = stackSize;
-
-    // This is a bit of a hack.
-    //
-    // We really do want the NOHUGEPAGE flag on our thread stacks, since we
-    // don't expect any of them to need anywhere near 2MB of space. But setting
-    // it here is too late to have an effect, since the first stack page has
-    // already been faulted in existence, and NSPR doesn't give us a way to set
-    // it beforehand.
-    //
-    // What this does get us, however, is a different set of VM flags on our
-    // thread stacks compared to normal heap memory. Which makes the Linux
-    // kernel report them as separate regions, even when they are adjacent to
-    // heap memory. This allows us to accurately track the actual memory
-    // consumption of our allocated stacks.
-    madvise(mStackBase, stackSize, MADV_NOHUGEPAGE);
-
-    pthread_attr_destroy(&attr);
-#elif defined(XP_WIN)
-    static const DynamicallyLinkedFunctionPtr<GetCurrentThreadStackLimitsFn>
-      sGetStackLimits(L"kernel32.dll", "GetCurrentThreadStackLimits");
-
-    if (sGetStackLimits) {
-      ULONG_PTR stackBottom, stackTop;
-      sGetStackLimits(&stackBottom, &stackTop);
-      mStackBase = reinterpret_cast<void*>(stackBottom);
-      mStackSize = stackTop - stackBottom;
-    }
-#endif
-  }
-
-  OffTheBooksMutexAutoLock mal(ThreadListMutex());
-  ThreadList().insertBack(this);
-}
-
 //-----------------------------------------------------------------------------
 
 // Tell the crash reporter to save a memory report if our heuristics determine
 // that an OOM failure is likely to occur soon.
 // Memory usage will not be checked more than every 30 seconds or saved more
 // than every 3 minutes
 // If |aShouldSave == kForceReport|, a report will be saved regardless of
 // whether the process is low on memory or not. However, it will still not be
@@ -682,27 +665,17 @@ nsThread::nsThread(NotNull<SynchronizedE
   , mCurrentPerformanceCounter(nullptr)
 {
 }
 
 nsThread::~nsThread()
 {
   NS_ASSERTION(mRequestedShutdownContexts.IsEmpty(),
                "shouldn't be waiting on other threads to shutdown");
-
-  // We shouldn't need to lock before checking isInList at this point. We're
-  // destroying the last reference to this object, so there's no way for anyone
-  // else to remove it in the middle of our check. And the not-in-list state is
-  // determined the element's next and previous members pointing to itself, so a
-  // non-atomic update to an adjacent member won't affect the outcome either.
-  if (isInList()) {
-    OffTheBooksMutexAutoLock mal(ThreadListMutex());
-    removeFrom(ThreadList());
-  }
-
+  MOZ_ASSERT(!isInList());
 #ifdef DEBUG
   // We deliberately leak these so they can be tracked by the leak checker.
   // If you're having nsThreadShutdownContext leaks, you can set:
   //   XPCOM_MEM_LOG_CLASSES=nsThreadShutdownContext
   // during a test run and that will at least tell you what thread is
   // requesting shutdown on another, which can be helpful for diagnosing
   // the leak.
   for (size_t i = 0; i < mRequestedShutdownContexts.Length(); ++i) {
@@ -726,16 +699,21 @@ nsThread::Init(const nsACString& aName)
   // ThreadFunc is responsible for setting mThread
   if (!PR_CreateThread(PR_USER_THREAD, ThreadFunc, &initData,
                        PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
                        PR_JOINABLE_THREAD, mStackSize)) {
     NS_RELEASE_THIS();
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
+  {
+    OffTheBooksMutexAutoLock mal(ThreadListMutex());
+    ThreadList().insertBack(this);
+  }
+
   // ThreadFunc will wait for this event to be run before it tries to access
   // mThread.  By delaying insertion of this event into the queue, we ensure
   // that mThread is set properly.
   {
     mEvents->PutEvent(do_AddRef(startup), EventPriority::Normal); // retain a reference
   }
 
   // Wait for thread to call ThreadManager::SetupCurrentThread, which completes
@@ -745,17 +723,16 @@ nsThread::Init(const nsACString& aName)
 }
 
 nsresult
 nsThread::InitCurrentThread()
 {
   mThread = PR_GetCurrentThread();
   mVirtualThread = GetCurrentVirtualThread();
   SetupCurrentThreadForChaosMode();
-  InitCommon();
 
   nsThreadManager::get().RegisterCurrentThread(*this);
   return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 // nsIEventTarget
 
@@ -874,23 +851,16 @@ nsThread::ShutdownInternal(bool aSync)
 }
 
 void
 nsThread::ShutdownComplete(NotNull<nsThreadShutdownContext*> aContext)
 {
   MOZ_ASSERT(mThread);
   MOZ_ASSERT(aContext->mTerminatingThread == this);
 
-  {
-    OffTheBooksMutexAutoLock mal(ThreadListMutex());
-    if (isInList()) {
-      removeFrom(ThreadList());
-    }
-  }
-
   if (aContext->mAwaitingShutdownAck) {
     // We're in a synchronous shutdown, so tell whatever is up the stack that
     // we're done and unwind the stack so it can call us again.
     aContext->mAwaitingShutdownAck = false;
     return;
   }
 
   // Now, it should be safe to join without fear of dead-locking.
--- a/xpcom/threads/nsThread.h
+++ b/xpcom/threads/nsThread.h
@@ -61,22 +61,16 @@ public:
            uint32_t aStackSize);
 
   // Initialize this as a wrapper for a new PRThread, and optionally give it a name.
   nsresult Init(const nsACString& aName = NS_LITERAL_CSTRING(""));
 
   // Initialize this as a wrapper for the current PRThread.
   nsresult InitCurrentThread();
 
-private:
-  // Initializes the mThreadId and stack base/size members, and adds the thread
-  // to the ThreadList().
-  void InitCommon();
-
-public:
   // The PRThread corresponding to this thread.
   PRThread* GetPRThread()
   {
     return mThread;
   }
 
   const void* StackBase() const { return mStackBase; }
   size_t StackSize() const { return mStackSize; }
@@ -171,21 +165,18 @@ protected:
   {
     nsIThreadObserver* obs;
     nsThread::GetObserver(&obs);
     return already_AddRefed<nsIThreadObserver>(obs);
   }
 
   struct nsThreadShutdownContext* ShutdownInternal(bool aSync);
 
-  friend class nsThreadManager;
-
   static mozilla::OffTheBooksMutex& ThreadListMutex();
   static mozilla::LinkedList<nsThread>& ThreadList();
-  static void ClearThreadList();
 
   RefPtr<mozilla::SynchronizedEventQueue> mEvents;
   RefPtr<mozilla::ThreadEventTarget> mEventTarget;
 
   // The shutdown contexts for any other threads we've asked to shut down.
   nsTArray<nsAutoPtr<struct nsThreadShutdownContext>> mRequestedShutdownContexts;
   // The shutdown context for ourselves.
   struct nsThreadShutdownContext* mShutdownContext;
@@ -209,18 +200,16 @@ protected:
   bool IsMainThread() const
   {
     return MainThreadFlag(mIsMainThread) == MAIN_THREAD;
   }
 
   // Set to true if this thread creates a JSRuntime.
   bool mCanInvokeJS;
 
-  bool mHasTLSEntry = false;
-
   // Used to track which event is being executed by ProcessNextEvent
   nsCOMPtr<nsIRunnable> mCurrentEvent;
 
   mozilla::TimeStamp mCurrentEventStart;
   mozilla::TimeStamp mNextIdleDeadline;
 
   RefPtr<mozilla::PerformanceCounter> mCurrentPerformanceCounter;
 };
--- a/xpcom/threads/nsThreadManager.cpp
+++ b/xpcom/threads/nsThreadManager.cpp
@@ -89,37 +89,22 @@ AssertIsOnMainThread()
 }
 
 } // mozilla namespace
 
 #endif
 
 typedef nsTArray<NotNull<RefPtr<nsThread>>> nsThreadArray;
 
-static bool sShutdownComplete;
-
 //-----------------------------------------------------------------------------
 
-/* static */ void
-nsThreadManager::ReleaseThread(void* aData)
+static void
+ReleaseObject(void* aData)
 {
-  if (sShutdownComplete) {
-    // We've already completed shutdown and released the references to all or
-    // our TLS wrappers. Don't try to release them again.
-    return;
-  }
-
-  auto* thread = static_cast<nsThread*>(aData);
-
-  get().UnregisterCurrentThread(*thread, true);
-
-  if (thread->mHasTLSEntry) {
-    thread->mHasTLSEntry = false;
-    thread->Release();
-  }
+  static_cast<nsISupports*>(aData)->Release();
 }
 
 // statically allocated instance
 NS_IMETHODIMP_(MozExternalRefCountType)
 nsThreadManager::AddRef()
 {
   return 2;
 }
@@ -246,17 +231,17 @@ nsThreadManager::Init()
   }
 
   if (!gTlsCurrentVirtualThread.init()) {
     return NS_ERROR_UNEXPECTED;
   }
 
   Scheduler::EventLoopActivation::Init();
 
-  if (PR_NewThreadPrivateIndex(&mCurThreadIndex, ReleaseThread) == PR_FAILURE) {
+  if (PR_NewThreadPrivateIndex(&mCurThreadIndex, ReleaseObject) == PR_FAILURE) {
     return NS_ERROR_FAILURE;
   }
 
 
 #ifdef MOZ_CANARY
   const int flags = O_WRONLY | O_APPEND | O_CREAT | O_NONBLOCK;
   const mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
   char* env_var_flag = getenv("MOZ_KILL_CANARIES");
@@ -314,44 +299,42 @@ nsThreadManager::Shutdown()
   // between when NewThread started, and when the thread finished initializing
   // and registering with ThreadManager.
   //
   mInitialized = false;
 
   // Empty the main thread event queue before we begin shutting down threads.
   NS_ProcessPendingEvents(mMainThread);
 
+  // We gather the threads from the hashtable into a list, so that we avoid
+  // holding the hashtable lock while calling nsIThread::Shutdown.
+  nsThreadArray threads;
   {
-    // We gather the threads from the hashtable into a list, so that we avoid
-    // holding the hashtable lock while calling nsIThread::Shutdown.
-    nsThreadArray threads;
-    {
-      OffTheBooksMutexAutoLock lock(mLock);
-      for (auto iter = mThreadsByPRThread.Iter(); !iter.Done(); iter.Next()) {
-        RefPtr<nsThread>& thread = iter.Data();
-        threads.AppendElement(WrapNotNull(thread));
-        iter.Remove();
-      }
+    OffTheBooksMutexAutoLock lock(mLock);
+    for (auto iter = mThreadsByPRThread.Iter(); !iter.Done(); iter.Next()) {
+      RefPtr<nsThread>& thread = iter.Data();
+      threads.AppendElement(WrapNotNull(thread));
+      iter.Remove();
     }
+  }
 
-    // It's tempting to walk the list of threads here and tell them each to stop
-    // accepting new events, but that could lead to badness if one of those
-    // threads is stuck waiting for a response from another thread.  To do it
-    // right, we'd need some way to interrupt the threads.
-    //
-    // Instead, we process events on the current thread while waiting for threads
-    // to shutdown.  This means that we have to preserve a mostly functioning
-    // world until such time as the threads exit.
+  // It's tempting to walk the list of threads here and tell them each to stop
+  // accepting new events, but that could lead to badness if one of those
+  // threads is stuck waiting for a response from another thread.  To do it
+  // right, we'd need some way to interrupt the threads.
+  //
+  // Instead, we process events on the current thread while waiting for threads
+  // to shutdown.  This means that we have to preserve a mostly functioning
+  // world until such time as the threads exit.
 
-    // Shutdown all threads that require it (join with threads that we created).
-    for (uint32_t i = 0; i < threads.Length(); ++i) {
-      NotNull<nsThread*> thread = threads[i];
-      if (thread->ShutdownRequired()) {
-        thread->Shutdown();
-      }
+  // Shutdown all threads that require it (join with threads that we created).
+  for (uint32_t i = 0; i < threads.Length(); ++i) {
+    NotNull<nsThread*> thread = threads[i];
+    if (thread->ShutdownRequired()) {
+      thread->Shutdown();
     }
   }
 
   // NB: It's possible that there are events in the queue that want to *start*
   // an asynchronous shutdown. But we have already shutdown the threads above,
   // so there's no need to worry about them. We only have to wait for all
   // in-flight asynchronous thread shutdowns to complete.
   mMainThread->WaitForAllAsynchronousShutdowns();
@@ -372,34 +355,16 @@ nsThreadManager::Shutdown()
   // have been processed.
   mMainThread->SetObserver(nullptr);
 
   // Release main thread object.
   mMainThread = nullptr;
 
   // Remove the TLS entry for the main thread.
   PR_SetThreadPrivate(mCurThreadIndex, nullptr);
-
-  {
-    // Cleanup the last references to any threads which haven't shut down yet.
-    nsTArray<RefPtr<nsThread>> threads;
-    for (auto* thread : nsThread::Enumerate()) {
-      if (thread->mHasTLSEntry) {
-        threads.AppendElement(dont_AddRef(thread));
-        thread->mHasTLSEntry = false;
-      }
-    }
-  }
-
-  // xpcshell tests sometimes leak the main thread. They don't enable leak
-  // checking, so that doesn't cause the test to fail, but leaving the entry in
-  // the thread list triggers an assertion, which does.
-  nsThread::ClearThreadList();
-
-  sShutdownComplete = true;
 }
 
 void
 nsThreadManager::RegisterCurrentThread(nsThread& aThread)
 {
   MOZ_ASSERT(aThread.GetPRThread() == PR_GetCurrentThread(), "bad aThread");
 
   OffTheBooksMutexAutoLock lock(mLock);
@@ -407,38 +372,31 @@ nsThreadManager::RegisterCurrentThread(n
   ++mCurrentNumberOfThreads;
   if (mCurrentNumberOfThreads > mHighestNumberOfThreads) {
     mHighestNumberOfThreads = mCurrentNumberOfThreads;
   }
 
   mThreadsByPRThread.Put(aThread.GetPRThread(), &aThread);  // XXX check OOM?
 
   aThread.AddRef();  // for TLS entry
-  aThread.mHasTLSEntry = true;
   PR_SetThreadPrivate(mCurThreadIndex, &aThread);
 }
 
 void
-nsThreadManager::UnregisterCurrentThread(nsThread& aThread, bool aIfExists)
+nsThreadManager::UnregisterCurrentThread(nsThread& aThread)
 {
   MOZ_ASSERT(aThread.GetPRThread() == PR_GetCurrentThread(), "bad aThread");
 
-  {
-    OffTheBooksMutexAutoLock lock(mLock);
+  OffTheBooksMutexAutoLock lock(mLock);
 
-    if (aIfExists && !mThreadsByPRThread.GetWeak(aThread.GetPRThread())) {
-      return;
-    }
-
-    --mCurrentNumberOfThreads;
-    mThreadsByPRThread.Remove(aThread.GetPRThread());
-  }
+  --mCurrentNumberOfThreads;
+  mThreadsByPRThread.Remove(aThread.GetPRThread());
 
   PR_SetThreadPrivate(mCurThreadIndex, nullptr);
-  // Ref-count balanced via ReleaseThread
+  // Ref-count balanced via ReleaseObject
 }
 
 nsThread*
 nsThreadManager::CreateCurrentThread(SynchronizedEventQueue* aQueue,
                                      nsThread::MainThreadFlag aMainThread)
 {
   // Make sure we don't have an nsThread yet.
   MOZ_ASSERT(!PR_GetThreadPrivate(mCurThreadIndex));
@@ -478,23 +436,17 @@ nsThreadManager::GetCurrentThread()
   }
 
   return thread.get();  // reference held in TLS
 }
 
 bool
 nsThreadManager::IsNSThread() const
 {
-  if (!mInitialized) {
-    return false;
-  }
-  if (auto* thread = (nsThread*)PR_GetThreadPrivate(mCurThreadIndex)) {
-    return thread->mShutdownRequired;
-  }
-  return false;
+  return mInitialized && !!PR_GetThreadPrivate(mCurThreadIndex);
 }
 
 NS_IMETHODIMP
 nsThreadManager::NewThread(uint32_t aCreationFlags,
                            uint32_t aStackSize,
                            nsIThread** aResult)
 {
   return NewNamedThread(NS_LITERAL_CSTRING(""), aStackSize, aResult);
--- a/xpcom/threads/nsThreadManager.h
+++ b/xpcom/threads/nsThreadManager.h
@@ -31,17 +31,17 @@ public:
   void Shutdown();
 
   // Called by nsThread to inform the ThreadManager it exists.  This method
   // must be called when the given thread is the current thread.
   void RegisterCurrentThread(nsThread& aThread);
 
   // Called by nsThread to inform the ThreadManager it is going away.  This
   // method must be called when the given thread is the current thread.
-  void UnregisterCurrentThread(nsThread& aThread, bool aIfExists = false);
+  void UnregisterCurrentThread(nsThread& aThread);
 
   // Returns the current thread.  Returns null if OOM or if ThreadManager isn't
   // initialized.  Creates the nsThread if one does not exist yet.
   nsThread* GetCurrentThread();
 
   // Returns true iff the currently running thread has an nsThread associated
   // with it (ie; whether this is a thread that we can dispatch runnables to).
   bool IsNSThread() const;
@@ -80,18 +80,16 @@ private:
     , mHighestNumberOfThreads(1)
   {
   }
 
   nsresult
   SpinEventLoopUntilInternal(nsINestedEventLoopCondition* aCondition,
                              bool aCheckingShutdown);
 
-  static void ReleaseThread(void* aData);
-
   nsRefPtrHashtable<nsPtrHashKey<PRThread>, nsThread> mThreadsByPRThread;
   unsigned            mCurThreadIndex;  // thread-local-storage index
   RefPtr<nsThread>  mMainThread;
   PRThread*         mMainPRThread;
   mozilla::OffTheBooksMutex mLock;  // protects tables
   mozilla::Atomic<bool,
                   mozilla::SequentiallyConsistent,
                   mozilla::recordreplay::Behavior::DontPreserve> mInitialized;