From ea7268ed2f80b0b76ced1737fb00083badf55425 Mon Sep 17 00:00:00 2001 From: Lars T Hansen Date: Thu, 5 Dec 2019 18:36:21 +0000 Subject: [PATCH] Bug 1599659 - Predicate to test context for live SABs. r=jonco SABs become foreground-finalizable so that we can access the runtime during finalization. Then a simple counter on the runtime will track live SABs for the runtime, and the predicate on the context can get its information from the runtime. Fallout: SABs are now enabled on the globals used for jsapi-tests. Differential Revision: https://phabricator.services.mozilla.com/D55783 --HG-- extra : moz-landing-system : lando --- js/src/jsapi-tests/moz.build | 1 + js/src/jsapi-tests/testSABAccounting.cpp | 34 ++++++++++++++++++++++++ js/src/jsapi-tests/tests.cpp | 3 ++- js/src/jsapi.cpp | 4 +++ js/src/jsapi.h | 6 +++++ js/src/vm/Runtime.cpp | 1 + js/src/vm/Runtime.h | 18 +++++++++++++ js/src/vm/SharedArrayObject.cpp | 8 ++++-- 8 files changed, 72 insertions(+), 3 deletions(-) create mode 100644 js/src/jsapi-tests/testSABAccounting.cpp diff --git a/js/src/jsapi-tests/moz.build b/js/src/jsapi-tests/moz.build index c30f3bb94811..9324c9e4e051 100644 --- a/js/src/jsapi-tests/moz.build +++ b/js/src/jsapi-tests/moz.build @@ -92,6 +92,7 @@ UNIFIED_SOURCES += [ 'testRegExp.cpp', 'testResolveRecursion.cpp', 'tests.cpp', + 'testSABAccounting.cpp', 'testSameValue.cpp', 'testSavedStacks.cpp', 'testScriptInfo.cpp', diff --git a/js/src/jsapi-tests/testSABAccounting.cpp b/js/src/jsapi-tests/testSABAccounting.cpp new file mode 100644 index 000000000000..9767e5faca2a --- /dev/null +++ b/js/src/jsapi-tests/testSABAccounting.cpp @@ -0,0 +1,34 @@ +#include "jsapi.h" +#include "jsfriendapi.h" + +#include "builtin/TestingFunctions.h" +#include "js/SharedArrayBuffer.h" +#include "jsapi-tests/tests.h" + +BEGIN_TEST(testSABAccounting) { + // Purge what we can + JS::PrepareForFullGC(cx); + NonIncrementalGC(cx, GC_SHRINK, JS::GCReason::API); + + // Self-hosting and chrome code should not use SABs, or the point of this + // predicate is completely lost. + CHECK(!JS_ContainsSharedArrayBuffer(cx)); + + JS::RootedObject obj(cx), obj2(cx); + CHECK(obj = JS::NewSharedArrayBuffer(cx, 4096)); + CHECK(JS_ContainsSharedArrayBuffer(cx)); + CHECK(obj2 = JS::NewSharedArrayBuffer(cx, 4096)); + CHECK(JS_ContainsSharedArrayBuffer(cx)); + + // Discard those objects again. + obj = nullptr; + obj2 = nullptr; + JS::PrepareForFullGC(cx); + NonIncrementalGC(cx, GC_SHRINK, JS::GCReason::API); + + // Should be back to base state. + CHECK(!JS_ContainsSharedArrayBuffer(cx)); + + return true; +} +END_TEST(testSABAccounting) diff --git a/js/src/jsapi-tests/tests.cpp b/js/src/jsapi-tests/tests.cpp index 26eff0667617..f5b7ae068e9a 100644 --- a/js/src/jsapi-tests/tests.cpp +++ b/js/src/jsapi-tests/tests.cpp @@ -92,7 +92,8 @@ JSObject* JSAPITest::createGlobal(JSPrincipals* principals) { .setStreamsEnabled(true) .setFieldsEnabled(true) .setAwaitFixEnabled(true) - .setWeakRefsEnabled(true); + .setWeakRefsEnabled(true) + .setSharedMemoryAndAtomicsEnabled(true); newGlobal = JS_NewGlobalObject(cx, getGlobalClass(), principals, JS::FireOnNewGlobalHook, options); if (!newGlobal) { diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index b40f11b61d7a..bacce5f2157d 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -390,6 +390,10 @@ JS_PUBLIC_API void JS_SetFutexCanWait(JSContext* cx) { cx->fx.setCanWait(true); } +JS_PUBLIC_API bool JS_ContainsSharedArrayBuffer(JSContext* cx) { + return cx->runtime()->hasLiveSABs(); +} + JS_PUBLIC_API JSRuntime* JS_GetParentRuntime(JSContext* cx) { return cx->runtime()->parentRuntime ? cx->runtime()->parentRuntime : cx->runtime(); diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 9abae21585d1..5388c9344af6 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -344,6 +344,12 @@ extern JS_PUBLIC_API JSRuntime* JS_GetRuntime(JSContext* cx); extern JS_PUBLIC_API void JS_SetFutexCanWait(JSContext* cx); +// Returns true if there are any live SharedArrayBuffer objects, including those +// for wasm memories, associated with the context. This is conservative, +// because it does not run GC. Some dead objects may not have been collected +// yet and thus will be thought live. +extern JS_PUBLIC_API bool JS_ContainsSharedArrayBuffer(JSContext* cx); + namespace js { void AssertHeapIsIdle(); diff --git a/js/src/vm/Runtime.cpp b/js/src/vm/Runtime.cpp index 8c0dd6331d03..7aa710667c2c 100644 --- a/js/src/vm/Runtime.cpp +++ b/js/src/vm/Runtime.cpp @@ -149,6 +149,7 @@ JSRuntime::JSRuntime(JSRuntime* parentRuntime) staticStrings(nullptr), commonNames(nullptr), wellKnownSymbols(nullptr), + liveSABs(0), offthreadIonCompilationEnabled_(true), parallelParsingEnabled_(true), #ifdef DEBUG diff --git a/js/src/vm/Runtime.h b/js/src/vm/Runtime.h index 4ae9e019b729..971733df94de 100644 --- a/js/src/vm/Runtime.h +++ b/js/src/vm/Runtime.h @@ -840,6 +840,24 @@ struct JSRuntime { JSRuntime* thisFromCtor() { return this; } + private: + // Number of live SharedArrayBuffer objects, including those in Wasm shared + // memories. uint64_t to avoid any risk of overflow. + js::MainThreadData liveSABs; + + public: + void incSABCount() { + MOZ_RELEASE_ASSERT(liveSABs != UINT64_MAX); + liveSABs++; + } + + void decSABCount() { + MOZ_RELEASE_ASSERT(liveSABs > 0); + liveSABs--; + } + + bool hasLiveSABs() const { return liveSABs > 0; } + public: void reportAllocationOverflow() { js::ReportAllocationOverflow(nullptr); } diff --git a/js/src/vm/SharedArrayObject.cpp b/js/src/vm/SharedArrayObject.cpp index bb0fa0f35098..b98b6bf22a07 100644 --- a/js/src/vm/SharedArrayObject.cpp +++ b/js/src/vm/SharedArrayObject.cpp @@ -251,6 +251,7 @@ SharedArrayBufferObject* SharedArrayBufferObject::New( MOZ_ASSERT(obj->getClass() == &class_); + cx->runtime()->incSABCount(); obj->acceptRawBuffer(buffer, length); return obj; @@ -273,7 +274,9 @@ SharedArrayRawBuffer* SharedArrayBufferObject::rawBufferObject() const { } void SharedArrayBufferObject::Finalize(JSFreeOp* fop, JSObject* obj) { - MOZ_ASSERT(fop->maybeOnHelperThread()); + // Must be foreground finalizable so that we can account for the object. + MOZ_ASSERT(fop->onMainThread()); + fop->runtime()->decSABCount(); SharedArrayBufferObject& buf = obj->as(); @@ -327,6 +330,7 @@ SharedArrayBufferObject* SharedArrayBufferObject::createFromNewRawBuffer( return nullptr; } + cx->runtime()->incSABCount(); obj->acceptRawBuffer(buffer, initialSize); return obj; @@ -373,7 +377,7 @@ const JSClass SharedArrayBufferObject::class_ = { JSCLASS_DELAY_METADATA_BUILDER | JSCLASS_HAS_RESERVED_SLOTS(SharedArrayBufferObject::RESERVED_SLOTS) | JSCLASS_HAS_CACHED_PROTO(JSProto_SharedArrayBuffer) | - JSCLASS_BACKGROUND_FINALIZE, + JSCLASS_FOREGROUND_FINALIZE, &SharedArrayBufferObjectClassOps, &SharedArrayBufferObjectClassSpec, JS_NULL_CLASS_EXT};