Bug 1414310 - Assert that JS threading APIs are only used between JS_Initialize and JS_Shutdown r=luke
authorJon Coppeard <jcoppeard@mozilla.com>
Thu, 09 Nov 2017 15:00:47 +0000
changeset 391059 b86c252b68fac5f673d8c6fb44640a5d376fcc8c
parent 391058 3ea95e09f4b360f74ceeaeaea66535f02b977fe6
child 391060 d41ed6f635b6d7b8d058b6029d43cb873a74092d
push id32860
push userebalazs@mozilla.com
push dateFri, 10 Nov 2017 09:56:38 +0000
treeherdermozilla-central@864174ac0707 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke
bugs1414310
milestone58.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 1414310 - Assert that JS threading APIs are only used between JS_Initialize and JS_Shutdown r=luke
js/public/Initialization.h
js/src/jsutil.h
js/src/threading/Mutex.cpp
js/src/threading/Thread.h
js/src/vm/Initialization.cpp
--- a/js/public/Initialization.h
+++ b/js/public/Initialization.h
@@ -8,17 +8,22 @@
 #ifndef js_Initialization_h
 #define js_Initialization_h
 
 #include "jstypes.h"
 
 namespace JS {
 namespace detail {
 
-enum class InitState { Uninitialized = 0, Running, ShutDown };
+enum class InitState {
+    Uninitialized = 0,
+    Initializing,
+    Running,
+    ShutDown
+};
 
 /**
  * SpiderMonkey's initialization status is tracked here, and it controls things
  * that should happen only once across all runtimes.  It's an API requirement
  * that JS_Init (and JS_ShutDown, if called) be called in a thread-aware
  * manner, so this (internal -- embedders, don't use!) variable doesn't need to
  * be atomic.
  */
@@ -94,17 +99,17 @@ JS_InitWithFailureDiagnostic(void)
  * JS_ShutDown() at the correct times, and therefore this API should ideally not
  * be necessary to use.  This is only intended to be used in cases where the
  * embedder isn't in full control of deciding whether to initialize SpiderMonkey
  * or hand off the task to another consumer.
  */
 inline bool
 JS_IsInitialized(void)
 {
-  return JS::detail::libraryInitState != JS::detail::InitState::Uninitialized;
+  return JS::detail::libraryInitState >= JS::detail::InitState::Running;
 }
 
 /**
  * Destroy free-standing resources allocated by SpiderMonkey, not associated
  * with any runtime, context, or other structure.
  *
  * This method should be called after all other JSAPI data has been properly
  * cleaned up: every new runtime must have been destroyed, every new context
--- a/js/src/jsutil.h
+++ b/js/src/jsutil.h
@@ -15,16 +15,17 @@
 #include "mozilla/Compiler.h"
 #include "mozilla/GuardObjects.h"
 #include "mozilla/HashFunctions.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/PodOperations.h"
 
 #include <limits.h>
 
+#include "js/Initialization.h"
 #include "js/Utility.h"
 #include "js/Value.h"
 
 #define JS_ALWAYS_TRUE(expr)      MOZ_ALWAYS_TRUE(expr)
 #define JS_ALWAYS_FALSE(expr)     MOZ_ALWAYS_FALSE(expr)
 
 #if defined(JS_DEBUG)
 # define JS_DIAGNOSTICS_ASSERT(expr) MOZ_ASSERT(expr)
@@ -42,16 +43,26 @@ js_memcpy(void* dst_, const void* src_, 
     MOZ_ASSERT_IF(dst >= src, (size_t) (dst - src) >= len);
     MOZ_ASSERT_IF(src >= dst, (size_t) (src - dst) >= len);
 
     return memcpy(dst, src, len);
 }
 
 namespace js {
 
+// An internal version of JS_IsInitialized() that returns whether SpiderMonkey
+// is currently initialized or is in the process of being initialized.
+inline bool
+IsInitialized()
+{
+    using namespace JS::detail;
+    return libraryInitState == InitState::Initializing ||
+           libraryInitState == InitState::Running;
+}
+
 template <class T>
 static inline void
 Reverse(T* beg, T* end)
 {
     while (beg != end) {
         if (--end == beg)
             return;
         T tmp = *beg;
--- a/js/src/threading/Mutex.cpp
+++ b/js/src/threading/Mutex.cpp
@@ -1,18 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "threading/Mutex.h"
 
-#include "js/Initialization.h"
-#include "js/Utility.h"
+#include "jsutil.h"
 
 using namespace js;
 
 #ifdef DEBUG
 
 MOZ_THREAD_LOCAL(js::Mutex::MutexVector*) js::Mutex::HeldMutexStack;
 
 /* static */ bool
@@ -26,35 +25,31 @@ js::Mutex::ShutDown()
 {
   js_delete(HeldMutexStack.get());
   HeldMutexStack.set(nullptr);
 }
 
 /* static */ js::Mutex::MutexVector&
 js::Mutex::heldMutexStack()
 {
+  MOZ_ASSERT(js::IsInitialized());
   auto stack = HeldMutexStack.get();
   if (!stack) {
     AutoEnterOOMUnsafeRegion oomUnsafe;
     stack = js_new<MutexVector>();
     if (!stack)
       oomUnsafe.crash("js::Mutex::heldMutexStack");
     HeldMutexStack.set(stack);
   }
   return *stack;
 }
 
 void
 js::Mutex::lock()
 {
-  if (!JS_IsInitialized()) {
-    MutexImpl::lock();
-    return;
-  }
-
   auto& stack = heldMutexStack();
   if (!stack.empty()) {
     const Mutex& prev = *stack.back();
     if (id_.order <= prev.id_.order) {
       fprintf(stderr,
               "Attempt to acquire mutex %s with order %d while holding %s with order %d\n",
               id_.name, id_.order, prev.id_.name, prev.id_.order);
       MOZ_CRASH("Mutex ordering violation");
@@ -66,21 +61,16 @@ js::Mutex::lock()
   AutoEnterOOMUnsafeRegion oomUnsafe;
   if (!stack.append(this))
     oomUnsafe.crash("js::Mutex::lock");
 }
 
 void
 js::Mutex::unlock()
 {
-  if (!JS_IsInitialized()) {
-    MutexImpl::unlock();
-    return;
-  }
-
   auto& stack = heldMutexStack();
   MOZ_ASSERT(stack.back() == this);
   MutexImpl::unlock();
   stack.popBack();
 }
 
 bool
 js::Mutex::ownedByCurrentThread() const
--- a/js/src/threading/Thread.h
+++ b/js/src/threading/Thread.h
@@ -11,17 +11,18 @@
 #include "mozilla/Attributes.h"
 #include "mozilla/HashFunctions.h"
 #include "mozilla/IndexSequence.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/Tuple.h"
 
 #include <stdint.h>
 
-#include "js/Utility.h"
+#include "jsutil.h"
+
 #include "threading/LockGuard.h"
 #include "threading/Mutex.h"
 #include "vm/MutexIDs.h"
 
 #ifdef XP_WIN
 # define THREAD_RETURN_TYPE unsigned int
 # define THREAD_CALL_API __stdcall
 #else
@@ -96,17 +97,19 @@ public:
             typename NonConstO = typename mozilla::RemoveConst<O>::Type,
             typename DerefO = typename mozilla::RemoveReference<NonConstO>::Type,
             typename = typename mozilla::EnableIf<mozilla::IsSame<DerefO, Options>::value,
                                                   void*>::Type>
   explicit Thread(O&& options = Options())
     : idMutex_(mutexid::ThreadId)
     , id_(Id())
     , options_(mozilla::Forward<O>(options))
-  { }
+  {
+    MOZ_ASSERT(js::IsInitialized());
+  }
 
   // Start a thread of execution at functor |f| with parameters |args|. This
   // method will return false if thread creation fails. This Thread must not
   // already have been created. Note that the arguments must be either POD or
   // rvalue references (mozilla::Move). Attempting to pass a reference will
   // result in the value being copied, which may not be the intended behavior.
   // See the comment below on ThreadTrampoline::args for an explanation.
   template <typename F, typename... Args>
--- a/js/src/vm/Initialization.cpp
+++ b/js/src/vm/Initialization.cpp
@@ -77,16 +77,18 @@ JS::detail::InitWithFailureDiagnostic(bo
 #endif
 
     MOZ_ASSERT(libraryInitState == InitState::Uninitialized,
                "must call JS_Init once before any JSAPI operation except "
                "JS_SetICUMemoryFunctions");
     MOZ_ASSERT(!JSRuntime::hasLiveRuntimes(),
                "how do we have live runtimes before JS_Init?");
 
+    libraryInitState = InitState::Initializing;
+
     PRMJ_NowInit();
 
     // The first invocation of `ProcessCreation` creates a temporary thread
     // and crashes if that fails, i.e. because we're out of memory. To prevent
     // that from happening at some later time, get it out of the way during
     // startup.
     mozilla::TimeStamp::ProcessCreation();