Bug 1297343 - Make some more object classes background finalized r=sfink
authorJon Coppeard <jcoppeard@mozilla.com>
Thu, 25 Aug 2016 12:00:56 +0100
changeset 354687 9be45967d42c71f0afbedb5c99dc2c052db09b0c
parent 354686 5bc7fa6fdd66e44f117d8f4eb7fc0d1881da184e
child 354688 361c0ddbcef03797e737cd676ea43521ab3c0fde
push id1324
push usermtabara@mozilla.com
push dateMon, 16 Jan 2017 13:07:44 +0000
treeherdermozilla-release@a01c49833940 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssfink
bugs1297343
milestone51.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 1297343 - Make some more object classes background finalized r=sfink
js/src/builtin/Intl.cpp
js/src/builtin/MapObject.cpp
js/src/builtin/ModuleObject.cpp
js/src/builtin/WeakMapObject.cpp
js/src/gc/GCRuntime.h
js/src/jsexn.cpp
js/src/jsgc.cpp
js/src/jsgc.h
js/src/jsscript.cpp
js/src/vm/Debugger.cpp
js/src/vm/GlobalObject.cpp
js/src/vm/PIC.cpp
js/src/vm/RegExpStatics.cpp
js/src/vm/Runtime.h
js/src/vm/SavedStacks.cpp
js/src/vm/SharedArrayObject.cpp
--- a/js/src/builtin/Intl.cpp
+++ b/js/src/builtin/Intl.cpp
@@ -758,16 +758,18 @@ js::intl_Collator(JSContext* cx, unsigne
     // intl_Collator is an intrinsic for self-hosted JavaScript, so it cannot
     // be used with "new", but it still has to be treated as a constructor.
     return Collator(cx, args, true);
 }
 
 static void
 collator_finalize(FreeOp* fop, JSObject* obj)
 {
+    MOZ_ASSERT(fop->onMainThread());
+
     // This is-undefined check shouldn't be necessary, but for internal
     // brokenness in object allocation code.  For the moment, hack around it by
     // explicitly guarding against the possibility of the reserved slot not
     // containing a private.  See bug 949220.
     const Value& slot = obj->as<NativeObject>().getReservedSlot(UCOLLATOR_SLOT);
     if (!slot.isUndefined()) {
         if (UCollator* coll = static_cast<UCollator*>(slot.toPrivate()))
             ucol_close(coll);
@@ -1249,16 +1251,18 @@ js::intl_NumberFormat(JSContext* cx, uns
     // cannot be used with "new", but it still has to be treated as a
     // constructor.
     return NumberFormat(cx, args, true);
 }
 
 static void
 numberFormat_finalize(FreeOp* fop, JSObject* obj)
 {
+    MOZ_ASSERT(fop->onMainThread());
+
     // This is-undefined check shouldn't be necessary, but for internal
     // brokenness in object allocation code.  For the moment, hack around it by
     // explicitly guarding against the possibility of the reserved slot not
     // containing a private.  See bug 949220.
     const Value& slot = obj->as<NativeObject>().getReservedSlot(UNUMBER_FORMAT_SLOT);
     if (!slot.isUndefined()) {
         if (UNumberFormat* nf = static_cast<UNumberFormat*>(slot.toPrivate()))
             unum_close(nf);
@@ -1714,16 +1718,18 @@ js::intl_DateTimeFormat(JSContext* cx, u
     // cannot be used with "new", but it still has to be treated as a
     // constructor.
     return DateTimeFormat(cx, args, true);
 }
 
 static void
 dateTimeFormat_finalize(FreeOp* fop, JSObject* obj)
 {
+    MOZ_ASSERT(fop->onMainThread());
+
     // This is-undefined check shouldn't be necessary, but for internal
     // brokenness in object allocation code.  For the moment, hack around it by
     // explicitly guarding against the possibility of the reserved slot not
     // containing a private.  See bug 949220.
     const Value& slot = obj->as<NativeObject>().getReservedSlot(UDATE_FORMAT_SLOT);
     if (!slot.isUndefined()) {
         if (UDateFormat* df = static_cast<UDateFormat*>(slot.toPrivate()))
             udat_close(df);
--- a/js/src/builtin/MapObject.cpp
+++ b/js/src/builtin/MapObject.cpp
@@ -179,16 +179,17 @@ MapIteratorObject::create(JSContext* cx,
     iterobj->setSlot(RangeSlot, PrivateValue(range));
     iterobj->setSlot(KindSlot, Int32Value(int32_t(kind)));
     return iterobj;
 }
 
 void
 MapIteratorObject::finalize(FreeOp* fop, JSObject* obj)
 {
+    MOZ_ASSERT(fop->onMainThread());
     fop->delete_(MapIteratorObjectRange(static_cast<NativeObject*>(obj)));
 }
 
 bool
 MapIteratorObject::next(JSContext* cx, Handle<MapIteratorObject*> mapIterator,
                         HandleArrayObject resultPairObj)
 {
     // Check invariants for inlined _GetNextMapEntryForIterator.
@@ -464,16 +465,17 @@ MapObject::create(JSContext* cx, HandleO
 
     mapObj->setPrivate(map.release());
     return mapObj;
 }
 
 void
 MapObject::finalize(FreeOp* fop, JSObject* obj)
 {
+    MOZ_ASSERT(fop->onMainThread());
     if (ValueMap* map = obj->as<MapObject>().getData())
         fop->delete_(map);
 }
 
 bool
 MapObject::construct(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
@@ -919,16 +921,17 @@ SetIteratorObject::create(JSContext* cx,
     iterobj->setSlot(KindSlot, Int32Value(int32_t(kind)));
     iterobj->setSlot(RangeSlot, PrivateValue(range));
     return iterobj;
 }
 
 void
 SetIteratorObject::finalize(FreeOp* fop, JSObject* obj)
 {
+    MOZ_ASSERT(fop->onMainThread());
     fop->delete_(obj->as<SetIteratorObject>().range());
 }
 
 bool
 SetIteratorObject::is(HandleValue v)
 {
     return v.isObject() && v.toObject().is<SetIteratorObject>();
 }
@@ -1114,16 +1117,17 @@ SetObject::mark(JSTracer* trc, JSObject*
         for (ValueSet::Range r = set->all(); !r.empty(); r.popFront())
             MarkKey(r, r.front(), trc);
     }
 }
 
 void
 SetObject::finalize(FreeOp* fop, JSObject* obj)
 {
+    MOZ_ASSERT(fop->onMainThread());
     SetObject* setobj = static_cast<SetObject*>(obj);
     if (ValueSet* set = setobj->getData())
         fop->delete_(set);
 }
 
 bool
 SetObject::construct(JSContext* cx, unsigned argc, Value* vp)
 {
--- a/js/src/builtin/ModuleObject.cpp
+++ b/js/src/builtin/ModuleObject.cpp
@@ -560,17 +560,17 @@ ModuleObject::classOps_ = {
     ModuleObject::trace
 };
 
 /* static */ const Class
 ModuleObject::class_ = {
     "Module",
     JSCLASS_HAS_RESERVED_SLOTS(ModuleObject::SlotCount) |
     JSCLASS_IS_ANONYMOUS |
-    JSCLASS_FOREGROUND_FINALIZE,
+    JSCLASS_BACKGROUND_FINALIZE,
     &ModuleObject::classOps_
 };
 
 #define DEFINE_ARRAY_SLOT_ACCESSOR(cls, name, slot)                           \
     ArrayObject&                                                              \
     cls::name() const                                                         \
     {                                                                         \
         return getFixedSlot(cls::slot).toObject().as<ArrayObject>();          \
@@ -616,16 +616,17 @@ ModuleObject::create(ExclusiveContext* c
 
     self->initReservedSlot(FunctionDeclarationsSlot, PrivateValue(funDecls));
     return self;
 }
 
 /* static */ void
 ModuleObject::finalize(js::FreeOp* fop, JSObject* obj)
 {
+    MOZ_ASSERT(fop->maybeOffMainThread());
     ModuleObject* self = &obj->as<ModuleObject>();
     if (self->hasImportBindings())
         fop->delete_(&self->importBindings());
     if (IndirectBindingMap* bindings = self->namespaceBindings())
         fop->delete_(bindings);
     if (FunctionDeclarationVector* funDecls = self->functionDeclarations())
         fop->delete_(funDecls);
 }
--- a/js/src/builtin/WeakMapObject.cpp
+++ b/js/src/builtin/WeakMapObject.cpp
@@ -225,16 +225,17 @@ WeakMap_mark(JSTracer* trc, JSObject* ob
 {
     if (ObjectValueMap* map = obj->as<WeakMapObject>().getMap())
         map->trace(trc);
 }
 
 static void
 WeakMap_finalize(FreeOp* fop, JSObject* obj)
 {
+    MOZ_ASSERT(fop->maybeOffMainThread());
     if (ObjectValueMap* map = obj->as<WeakMapObject>().getMap()) {
 #ifdef DEBUG
         map->~ObjectValueMap();
         memset(static_cast<void*>(map), 0xdc, sizeof(*map));
         fop->free_(map);
 #else
         fop->delete_(map);
 #endif
@@ -395,17 +396,17 @@ static const ClassOps WeakMapObjectClass
     nullptr, /* construct */
     WeakMap_mark
 };
 
 const Class WeakMapObject::class_ = {
     "WeakMap",
     JSCLASS_HAS_PRIVATE |
     JSCLASS_HAS_CACHED_PROTO(JSProto_WeakMap) |
-    JSCLASS_FOREGROUND_FINALIZE,
+    JSCLASS_BACKGROUND_FINALIZE,
     &WeakMapObjectClassOps
 };
 
 static const JSFunctionSpec weak_map_methods[] = {
     JS_FN("has",    WeakMap_has, 1, 0),
     JS_FN("get",    WeakMap_get, 1, 0),
     JS_FN("delete", WeakMap_delete, 1, 0),
     JS_FN("set",    WeakMap_set, 2, 0),
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -966,17 +966,17 @@ class GCRuntime
     bool shouldReleaseObservedTypes();
     void endSweepingZoneGroup();
     IncrementalProgress sweepPhase(SliceBudget& sliceBudget, AutoLockForExclusiveAccess& lock);
     void endSweepPhase(bool lastGC, AutoLockForExclusiveAccess& lock);
     void sweepZones(FreeOp* fop, bool lastGC);
     void decommitAllWithoutUnlocking(const AutoLockGC& lock);
     void startDecommit();
     void queueZonesForBackgroundSweep(ZoneList& zones);
-    void sweepBackgroundThings(ZoneList& zones, LifoAlloc& freeBlocks, ThreadType threadType);
+    void sweepBackgroundThings(ZoneList& zones, LifoAlloc& freeBlocks);
     void assertBackgroundSweepingFinished();
     bool shouldCompact();
     void beginCompactPhase();
     IncrementalProgress compactPhase(JS::gcreason::Reason reason, SliceBudget& sliceBudget,
                                      AutoLockForExclusiveAccess& lock);
     void endCompactPhase(JS::gcreason::Reason reason);
     void sweepTypesAfterCompacting(Zone* zone);
     void sweepZoneAfterCompacting(Zone* zone);
--- a/js/src/jsexn.cpp
+++ b/js/src/jsexn.cpp
@@ -80,17 +80,17 @@ static const ClassOps ErrorObjectClassOp
     nullptr,                 /* trace       */
 };
 
 #define IMPLEMENT_ERROR_CLASS(name, classSpecPtr) \
     { \
         js_Error_str, /* yes, really */ \
         JSCLASS_HAS_CACHED_PROTO(JSProto_##name) | \
         JSCLASS_HAS_RESERVED_SLOTS(ErrorObject::RESERVED_SLOTS) | \
-        JSCLASS_FOREGROUND_FINALIZE, \
+        JSCLASS_BACKGROUND_FINALIZE, \
         &ErrorObjectClassOps, \
         classSpecPtr \
     }
 
 const ClassSpec
 ErrorObject::errorClassSpec_ = {
     ErrorObject::createConstructor,
     ErrorObject::createProto,
@@ -256,16 +256,17 @@ js::ComputeStackString(JSContext* cx)
         return nullptr;
 
     return str.get();
 }
 
 static void
 exn_finalize(FreeOp* fop, JSObject* obj)
 {
+    MOZ_ASSERT(fop->maybeOffMainThread());
     if (JSErrorReport* report = obj->as<ErrorObject>().getErrorReport())
         fop->free_(report);
 }
 
 JSErrorReport*
 js::ErrorFromException(JSContext* cx, HandleObject objArg)
 {
     // It's ok to UncheckedUnwrap here, since all we do is get the
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -3156,26 +3156,26 @@ js::gc::BackgroundDecommitTask::run()
     ChunkPool toFree = runtime->gc.expireEmptyChunkPool(lock);
     if (toFree.count()) {
         AutoUnlockGC unlock(lock);
         FreeChunkPool(runtime, toFree);
     }
 }
 
 void
-GCRuntime::sweepBackgroundThings(ZoneList& zones, LifoAlloc& freeBlocks, ThreadType threadType)
+GCRuntime::sweepBackgroundThings(ZoneList& zones, LifoAlloc& freeBlocks)
 {
     freeBlocks.freeAll();
 
     if (zones.isEmpty())
         return;
 
     // We must finalize thing kinds in the order specified by BackgroundFinalizePhases.
     Arena* emptyArenas = nullptr;
-    FreeOp fop(threadType == MainThread ? rt : nullptr);
+    FreeOp fop(nullptr);
     for (unsigned phase = 0 ; phase < ArrayLength(BackgroundFinalizePhases) ; ++phase) {
         for (Zone* zone = zones.front(); zone; zone = zone->nextZone()) {
             for (auto kind : BackgroundFinalizePhases[phase].kinds) {
                 Arena* arenas = zone->arenas.arenaListsToSweep[kind];
                 MOZ_RELEASE_ASSERT(uintptr_t(arenas) != uintptr_t(-1));
                 if (arenas)
                     ArenaLists::backgroundFinalize(&fop, arenas, &emptyArenas);
             }
@@ -3372,17 +3372,17 @@ GCHelperState::doSweep(AutoLockGC& lock)
             AutoSetThreadIsSweeping threadIsSweeping;
 
             ZoneList zones;
             zones.transferFrom(rt->gc.backgroundSweepZones);
             LifoAlloc freeLifoAlloc(JSRuntime::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE);
             freeLifoAlloc.transferFrom(&rt->gc.blocksToFreeAfterSweeping);
 
             AutoUnlockGC unlock(lock);
-            rt->gc.sweepBackgroundThings(zones, freeLifoAlloc, BackgroundThread);
+            rt->gc.sweepBackgroundThings(zones, freeLifoAlloc);
         }
     } while (!rt->gc.backgroundSweepZones.isEmpty());
 }
 
 bool
 GCHelperState::onBackgroundThread()
 {
     return thread.isSome() && *thread == ThisThread::GetId();
@@ -5118,17 +5118,17 @@ GCRuntime::endSweepingZoneGroup()
 
     /* Start background thread to sweep zones if required. */
     ZoneList zones;
     for (GCZoneGroupIter zone(rt); !zone.done(); zone.next())
         zones.append(zone);
     if (sweepOnBackgroundThread)
         queueZonesForBackgroundSweep(zones);
     else
-        sweepBackgroundThings(zones, blocksToFreeAfterSweeping, MainThread);
+        sweepBackgroundThings(zones, blocksToFreeAfterSweeping);
 
     /* Reset the list of arenas marked as being allocated during sweep phase. */
     while (Arena* arena = arenasAllocatedDuringSweep) {
         arenasAllocatedDuringSweep = arena->getNextAllocDuringSweep();
         arena->unsetAllocDuringSweep();
     }
 }
 
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -21,22 +21,16 @@
 #include "threading/Thread.h"
 #include "vm/NativeObject.h"
 
 namespace js {
 
 class AutoLockHelperThreadState;
 unsigned GetCPUCount();
 
-enum ThreadType
-{
-    MainThread,
-    BackgroundThread
-};
-
 namespace gcstats {
 struct Statistics;
 } // namespace gcstats
 
 class Nursery;
 
 namespace gc {
 
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -1281,16 +1281,17 @@ ScriptSourceObject::trace(JSTracer* trc,
             sso->setReservedSlot(INTRODUCTION_SCRIPT_SLOT, PrivateValue(script));
         }
     }
 }
 
 void
 ScriptSourceObject::finalize(FreeOp* fop, JSObject* obj)
 {
+    MOZ_ASSERT(fop->onMainThread());
     ScriptSourceObject* sso = &obj->as<ScriptSourceObject>();
 
     // If code coverage is enabled, record the filename associated with this
     // source object.
     if (fop->runtime()->lcovOutput.isEnabled())
         sso->compartment()->lcovOutput.collectSourceFile(sso->compartment(), sso);
 
     sso->source()->decref();
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -91,17 +91,17 @@ const ClassOps DebuggerFrame::classOps_ 
     nullptr,    /* construct   */
     nullptr,    /* trace   */
 };
 
 const Class DebuggerFrame::class_ = {
     "Frame",
     JSCLASS_HAS_PRIVATE |
     JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGFRAME_COUNT) |
-    JSCLASS_FOREGROUND_FINALIZE,
+    JSCLASS_BACKGROUND_FINALIZE,
     &DebuggerFrame::classOps_
 };
 
 enum {
     JSSLOT_DEBUGARGUMENTS_FRAME,
     JSSLOT_DEBUGARGUMENTS_COUNT
 };
 
@@ -3100,16 +3100,18 @@ Debugger::findZoneEdges(Zone* zone, js::
             finder.addEdgeTo(w);
         }
     }
 }
 
 /* static */ void
 Debugger::finalize(FreeOp* fop, JSObject* obj)
 {
+    MOZ_ASSERT(fop->onMainThread());
+
     Debugger* dbg = fromJSObject(obj);
     if (!dbg)
         return;
     fop->delete_(dbg);
 }
 
 const ClassOps Debugger::classOps_ = {
     nullptr,    /* addProperty */
@@ -7578,16 +7580,17 @@ DebuggerFrame_maybeDecrementFrameScriptS
     /* If this frame has an onStep handler, decrement the script's count. */
     if (!frameobj->getReservedSlot(JSSLOT_DEBUGFRAME_ONSTEP_HANDLER).isUndefined())
         frame.script()->decrementStepModeCount(fop);
 }
 
 static void
 DebuggerFrame_finalize(FreeOp* fop, JSObject* obj)
 {
+    MOZ_ASSERT(fop->maybeOffMainThread());
     DebuggerFrame_freeScriptFrameIterData(fop, obj);
 }
 
 /* static */ DebuggerFrame*
 DebuggerFrame::checkThis(JSContext* cx, const CallArgs& args, const char* fnname, bool checkLive)
 {
     JSObject* thisobj = NonNullObject(cx, args.thisv());
     if (!thisobj)
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -655,16 +655,17 @@ js::DefinePropertiesAndFunctions(JSConte
     if (fs && !JS_DefineFunctions(cx, obj, fs))
         return false;
     return true;
 }
 
 static void
 GlobalDebuggees_finalize(FreeOp* fop, JSObject* obj)
 {
+    MOZ_ASSERT(fop->maybeOffMainThread());
     fop->delete_((GlobalObject::DebuggerVector*) obj->as<NativeObject>().getPrivate());
 }
 
 static const ClassOps
 GlobalDebuggees_classOps = {
     nullptr,
     nullptr,
     nullptr,
@@ -674,17 +675,17 @@ GlobalDebuggees_classOps = {
     nullptr,
     GlobalDebuggees_finalize
 };
 
 static const Class
 GlobalDebuggees_class = {
     "GlobalDebuggee",
     JSCLASS_HAS_PRIVATE |
-    JSCLASS_FOREGROUND_FINALIZE,
+    JSCLASS_BACKGROUND_FINALIZE,
     &GlobalDebuggees_classOps
 };
 
 GlobalObject::DebuggerVector*
 GlobalObject::getDebuggers() const
 {
     Value debuggers = getReservedSlot(DEBUGGERS);
     if (debuggers.isUndefined())
--- a/js/src/vm/PIC.cpp
+++ b/js/src/vm/PIC.cpp
@@ -271,16 +271,17 @@ js::ForOfPIC::Chain::sweep(FreeOp* fop)
         stubs_ = next;
     }
     fop->delete_(this);
 }
 
 static void
 ForOfPIC_finalize(FreeOp* fop, JSObject* obj)
 {
+    MOZ_ASSERT(fop->maybeOffMainThread());
     if (ForOfPIC::Chain* chain = ForOfPIC::fromJSObject(&obj->as<NativeObject>()))
         chain->sweep(fop);
 }
 
 static void
 ForOfPIC_traceObject(JSTracer* trc, JSObject* obj)
 {
     if (ForOfPIC::Chain* chain = ForOfPIC::fromJSObject(&obj->as<NativeObject>()))
@@ -294,17 +295,17 @@ static const ClassOps ForOfPICClassOps =
     nullptr,              /* hasInstance */
     nullptr,              /* construct   */
     ForOfPIC_traceObject
 };
 
 const Class ForOfPIC::class_ = {
     "ForOfPIC",
     JSCLASS_HAS_PRIVATE |
-    JSCLASS_FOREGROUND_FINALIZE,
+    JSCLASS_BACKGROUND_FINALIZE,
     &ForOfPICClassOps
 };
 
 /* static */ NativeObject*
 js::ForOfPIC::createForOfPICObject(JSContext* cx, Handle<GlobalObject*> global)
 {
     assertSameCompartment(cx, global);
     NativeObject* obj = NewNativeObjectWithGivenProto(cx, &ForOfPIC::class_, nullptr);
--- a/js/src/vm/RegExpStatics.cpp
+++ b/js/src/vm/RegExpStatics.cpp
@@ -17,16 +17,17 @@ using namespace js;
  * per-global and not leak, we create a js::Class to wrap the C++ instance and
  * provide an appropriate finalizer. We lazily create and store an instance of
  * that js::Class in a global reserved slot.
  */
 
 static void
 resc_finalize(FreeOp* fop, JSObject* obj)
 {
+    MOZ_ASSERT(fop->onMainThread());
     RegExpStatics* res = static_cast<RegExpStatics*>(obj->as<RegExpStaticsObject>().getPrivate());
     fop->delete_(res);
 }
 
 static void
 resc_trace(JSTracer* trc, JSObject* obj)
 {
     void* pdata = obj->as<RegExpStaticsObject>().getPrivate();
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -130,16 +130,22 @@ class FreeOp : public JSFreeOp
 
     explicit FreeOp(JSRuntime* maybeRuntime);
     ~FreeOp();
 
     bool onMainThread() const {
         return runtime_ != nullptr;
     }
 
+    bool maybeOffMainThread() const {
+        // Sometimes background finalization happens on the main thread so
+        // runtime_ being null doesn't always mean we are off the main thread.
+        return !runtime_;
+    }
+
     bool isDefaultFreeOp() const;
 
     inline void free_(void* p);
     inline void freeLater(void* p);
 
     inline bool appendJitPoisonRange(const jit::JitPoisonRange& range);
 
     template <class T>
--- a/js/src/vm/SavedStacks.cpp
+++ b/js/src/vm/SavedStacks.cpp
@@ -362,16 +362,17 @@ SavedFrame::protoAccessors[] = {
     JS_PSG("asyncParent", SavedFrame::asyncParentProperty, 0),
     JS_PSG("parent", SavedFrame::parentProperty, 0),
     JS_PS_END
 };
 
 /* static */ void
 SavedFrame::finalize(FreeOp* fop, JSObject* obj)
 {
+    MOZ_ASSERT(fop->onMainThread());
     JSPrincipals* p = obj->as<SavedFrame>().getPrincipals();
     if (p) {
         JSRuntime* rt = obj->runtimeFromMainThread();
         JS_DropPrincipals(rt->contextFromMainThread(), p);
     }
 }
 
 JSAtom*
--- a/js/src/vm/SharedArrayObject.cpp
+++ b/js/src/vm/SharedArrayObject.cpp
@@ -296,16 +296,18 @@ SharedArrayBufferObject::rawBufferObject
     Value v = getReservedSlot(RAWBUF_SLOT);
     MOZ_ASSERT(!v.isUndefined());
     return reinterpret_cast<SharedArrayRawBuffer*>(v.toPrivate());
 }
 
 void
 SharedArrayBufferObject::Finalize(FreeOp* fop, JSObject* obj)
 {
+    MOZ_ASSERT(fop->maybeOffMainThread());
+
     SharedArrayBufferObject& buf = obj->as<SharedArrayBufferObject>();
 
     // Detect the case of failure during SharedArrayBufferObject creation,
     // which causes a SharedArrayRawBuffer to never be attached.
     Value v = buf.getReservedSlot(RAWBUF_SLOT);
     if (!v.isUndefined()) {
         buf.rawBufferObject()->dropReference();
         buf.dropRawBuffer();
@@ -347,17 +349,17 @@ static const ClassOps SharedArrayBufferO
     nullptr, /* trace */
 };
 
 const Class SharedArrayBufferObject::class_ = {
     "SharedArrayBuffer",
     JSCLASS_DELAY_METADATA_BUILDER |
     JSCLASS_HAS_RESERVED_SLOTS(SharedArrayBufferObject::RESERVED_SLOTS) |
     JSCLASS_HAS_CACHED_PROTO(JSProto_SharedArrayBuffer) |
-    JSCLASS_FOREGROUND_FINALIZE,
+    JSCLASS_BACKGROUND_FINALIZE,
     &SharedArrayBufferObjectClassOps,
     JS_NULL_CLASS_SPEC,
     JS_NULL_CLASS_EXT
 };
 
 JSObject*
 js::InitSharedArrayBufferClass(JSContext* cx, HandleObject obj)
 {