Bug 1663938 - Assert that the collector doesn't trigger barriers during marking r=sfink
authorJon Coppeard <jcoppeard@mozilla.com>
Fri, 11 Sep 2020 11:13:36 +0000
changeset 548301 9dd67fd23c9eb27ab2d6d0491296e82aa12a6631
parent 548300 5012e5d4c2d7fc7c72c9f1eb1434b94762da15f3
child 548302 49039661a8071e4ab5995224c1cebc72163a5db2
push id37776
push userbtara@mozilla.com
push dateFri, 11 Sep 2020 15:10:42 +0000
treeherdermozilla-central@b133e2d673e8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssfink
bugs1663938
milestone82.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 1663938 - Assert that the collector doesn't trigger barriers during marking r=sfink Differential Revision: https://phabricator.services.mozilla.com/D89638
js/src/gc/Barrier.cpp
js/src/gc/Barrier.h
js/src/gc/Cell.h
js/src/gc/StoreBuffer.h
js/src/vm/JSContext.cpp
js/src/vm/JSContext.h
--- a/js/src/gc/Barrier.cpp
+++ b/js/src/gc/Barrier.cpp
@@ -67,29 +67,33 @@ bool CurrentThreadIsIonCompiling() {
 }
 
 bool CurrentThreadIsIonCompilingSafeForMinorGC() {
   jit::JitContext* jcx = jit::MaybeGetJitContext();
   return jcx && jcx->inIonBackendSafeForMinorGC();
 }
 
 bool CurrentThreadIsGCMarking() {
-  return TlsContext.get()->gcUse == JSContext::GCUse::Marking;
+  JSContext* cx = MaybeGetJSContext();
+  return cx && cx->gcUse == JSContext::GCUse::Marking;
 }
 
 bool CurrentThreadIsGCSweeping() {
-  return TlsContext.get()->gcUse == JSContext::GCUse::Sweeping;
+  JSContext* cx = MaybeGetJSContext();
+  return cx && cx->gcUse == JSContext::GCUse::Sweeping;
 }
 
 bool CurrentThreadIsGCFinalizing() {
-  return TlsContext.get()->gcUse == JSContext::GCUse::Finalizing;
+  JSContext* cx = MaybeGetJSContext();
+  return cx && cx->gcUse == JSContext::GCUse::Finalizing;
 }
 
 bool CurrentThreadIsTouchingGrayThings() {
-  return TlsContext.get()->isTouchingGrayThings;
+  JSContext* cx = MaybeGetJSContext();
+  return cx && cx->isTouchingGrayThings;
 }
 
 AutoTouchingGrayThings::AutoTouchingGrayThings() {
   TlsContext.get()->isTouchingGrayThings++;
 }
 
 AutoTouchingGrayThings::~AutoTouchingGrayThings() {
   JSContext* cx = TlsContext.get();
--- a/js/src/gc/Barrier.h
+++ b/js/src/gc/Barrier.h
@@ -2,16 +2,18 @@
  * 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/. */
 
 #ifndef gc_Barrier_h
 #define gc_Barrier_h
 
+#include "mozilla/DebugOnly.h"
+
 #include <type_traits>  // std::true_type
 
 #include "NamespaceImports.h"
 
 #include "gc/Cell.h"
 #include "gc/StoreBuffer.h"
 #include "js/ComparisonOperators.h"  // JS::detail::DefineComparisonOps
 #include "js/HeapAPI.h"
--- a/js/src/gc/Cell.h
+++ b/js/src/gc/Cell.h
@@ -390,22 +390,24 @@ inline JS::TraceKind Cell::getTraceKind(
   return NurseryCellHeader::from(this)->traceKind();
 }
 
 /* static */ MOZ_ALWAYS_INLINE bool Cell::needWriteBarrierPre(JS::Zone* zone) {
   return JS::shadow::Zone::from(zone)->needsIncrementalBarrier();
 }
 
 /* static */ MOZ_ALWAYS_INLINE void Cell::readBarrier(Cell* thing) {
+  MOZ_ASSERT(!CurrentThreadIsGCMarking());
   if (thing->isTenured()) {
     TenuredCell::readBarrier(&thing->asTenured());
   }
 }
 
 /* static */ MOZ_ALWAYS_INLINE void Cell::writeBarrierPre(Cell* thing) {
+  MOZ_ASSERT(!CurrentThreadIsGCMarking());
   if (thing && thing->isTenured()) {
     TenuredCell::writeBarrierPre(&thing->asTenured());
   }
 }
 
 bool TenuredCell::isMarkedAny() const {
   MOZ_ASSERT(arena()->allocated());
   return chunk()->bitmap.isMarkedAny(this);
@@ -458,48 +460,52 @@ JS::Zone* TenuredCell::zoneFromAnyThread
 
 bool TenuredCell::isInsideZone(JS::Zone* zone) const {
   return zone == arena()->zone;
 }
 
 /* static */ MOZ_ALWAYS_INLINE void TenuredCell::readBarrier(
     TenuredCell* thing) {
   MOZ_ASSERT(!CurrentThreadIsIonCompiling());
+  MOZ_ASSERT(!CurrentThreadIsGCMarking());
   MOZ_ASSERT(thing);
   MOZ_ASSERT(CurrentThreadCanAccessZone(thing->zoneFromAnyThread()));
+
   // Barriers should not be triggered on main thread while collecting.
-  MOZ_ASSERT_IF(CurrentThreadCanAccessRuntime(thing->runtimeFromAnyThread()),
+  mozilla::DebugOnly<JSRuntime*> runtime = thing->runtimeFromAnyThread();
+  MOZ_ASSERT_IF(CurrentThreadCanAccessRuntime(runtime),
                 !JS::RuntimeHeapIsCollecting());
 
   JS::shadow::Zone* shadowZone = thing->shadowZoneFromAnyThread();
   if (shadowZone->needsIncrementalBarrier()) {
-    // Barriers are only enabled on the main thread and are disabled while
-    // collecting.
-    MOZ_ASSERT(!RuntimeFromMainThreadIsHeapMajorCollecting(shadowZone));
+    // We should only observe barriers being enabled on the main thread.
+    MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtime));
     Cell* tmp = thing;
     TraceManuallyBarrieredGenericPointerEdge(shadowZone->barrierTracer(), &tmp,
                                              "read barrier");
     MOZ_ASSERT(tmp == thing);
   }
 
   if (thing->isMarkedGray()) {
     // There shouldn't be anything marked gray unless we're on the main thread.
-    MOZ_ASSERT(CurrentThreadCanAccessRuntime(thing->runtimeFromAnyThread()));
+    MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtime));
     if (!JS::RuntimeHeapIsCollecting()) {
       JS::UnmarkGrayGCThingRecursively(
           JS::GCCellPtr(thing, thing->getTraceKind()));
     }
   }
 }
 
 void AssertSafeToSkipBarrier(TenuredCell* thing);
 
 /* static */ MOZ_ALWAYS_INLINE void TenuredCell::writeBarrierPre(
     TenuredCell* thing) {
   MOZ_ASSERT(!CurrentThreadIsIonCompiling());
+  MOZ_ASSERT(!CurrentThreadIsGCMarking());
+
   if (!thing) {
     return;
   }
 
 #ifdef JS_GC_ZEAL
   // When verifying pre barriers we need to switch on all barriers, even
   // those on the Atoms Zone. Normally, we never enter a parse task when
   // collecting in the atoms zone, so will filter out atoms below.
@@ -510,32 +516,32 @@ void AssertSafeToSkipBarrier(TenuredCell
   // any off thread barriers that reach us and assert that they would normally
   // not be possible.
   if (!CurrentThreadCanAccessRuntime(thing->runtimeFromAnyThread())) {
     AssertSafeToSkipBarrier(thing);
     return;
   }
 #endif
 
+  // Barriers can be triggered on the main thread while collecting, but are
+  // disabled. For example, this happens when destroying HeapPtr wrappers.
+
   JS::shadow::Zone* shadowZone = thing->shadowZoneFromAnyThread();
   if (shadowZone->needsIncrementalBarrier()) {
     MOZ_ASSERT(!RuntimeFromMainThreadIsHeapMajorCollecting(shadowZone));
     Cell* tmp = thing;
     TraceManuallyBarrieredGenericPointerEdge(shadowZone->barrierTracer(), &tmp,
                                              "pre barrier");
     MOZ_ASSERT(tmp == thing);
   }
 }
 
 static MOZ_ALWAYS_INLINE void AssertValidToSkipBarrier(TenuredCell* thing) {
   MOZ_ASSERT(!IsInsideNursery(thing));
-  MOZ_ASSERT_IF(
-      thing,
-      MapAllocToTraceKind(thing->getAllocKind()) != JS::TraceKind::Object &&
-          MapAllocToTraceKind(thing->getAllocKind()) != JS::TraceKind::String);
+  MOZ_ASSERT_IF(thing, !IsNurseryAllocable(thing->getAllocKind()));
 }
 
 /* static */ MOZ_ALWAYS_INLINE void TenuredCell::writeBarrierPost(
     void* cellp, TenuredCell* prior, TenuredCell* next) {
   AssertValidToSkipBarrier(next);
 }
 
 #ifdef DEBUG
--- a/js/src/gc/StoreBuffer.h
+++ b/js/src/gc/StoreBuffer.h
@@ -17,16 +17,21 @@
 #include "ds/LifoAlloc.h"
 #include "gc/Nursery.h"
 #include "js/AllocPolicy.h"
 #include "js/MemoryMetrics.h"
 #include "js/UniquePtr.h"
 #include "threading/Mutex.h"
 
 namespace js {
+
+#ifdef DEBUG
+extern bool CurrentThreadIsGCMarking();
+#endif
+
 namespace gc {
 
 class Arena;
 class ArenaCellSet;
 
 #ifdef DEBUG
 extern bool CurrentThreadHasLockedGC();
 #endif
@@ -389,16 +394,17 @@ class StoreBuffer {
   };
 
   // The GC runs tasks that may access the storebuffer in parallel and so must
   // take a lock. The mutator may only access the storebuffer from the main
   // thread.
   inline void CheckAccess() const {
 #ifdef DEBUG
     if (JS::RuntimeHeapIsBusy()) {
+      MOZ_ASSERT(!CurrentThreadIsGCMarking());
       MOZ_ASSERT(lock_.ownedByCurrentThread());
     } else {
       MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtime_));
     }
 #endif
   }
 
   template <typename Buffer, typename Edge>
--- a/js/src/vm/JSContext.cpp
+++ b/js/src/vm/JSContext.cpp
@@ -80,16 +80,25 @@
 #include "vm/JSScript-inl.h"
 #include "vm/Stack-inl.h"
 
 using namespace js;
 
 using mozilla::DebugOnly;
 using mozilla::PodArrayZero;
 
+#ifdef DEBUG
+JSContext* js::MaybeGetJSContext() {
+  if (!TlsContext.init()) {
+    return nullptr;
+  }
+  return TlsContext.get();
+}
+#endif
+
 bool js::AutoCycleDetector::init() {
   MOZ_ASSERT(cyclic);
 
   AutoCycleDetector::Vector& vector = cx->cycleDetectorVector();
 
   for (JSObject* obj2 : vector) {
     if (MOZ_UNLIKELY(obj == obj2)) {
       return true;
--- a/js/src/vm/JSContext.h
+++ b/js/src/vm/JSContext.h
@@ -132,16 +132,17 @@ enum class ContextKind {
   // Context for the main thread of a JSRuntime.
   MainThread,
 
   // Context for a helper thread.
   HelperThread
 };
 
 #ifdef DEBUG
+JSContext* MaybeGetJSContext();
 bool CurrentThreadIsParseThread();
 #endif
 
 enum class InterruptReason : uint32_t {
   GC = 1 << 0,
   AttachIonCompilations = 1 << 1,
   CallbackUrgent = 1 << 2,
   CallbackCanWait = 1 << 3,