From 4757205b3c2a080780bfd6c04f407aed163bfe42 Mon Sep 17 00:00:00 2001 From: Shu-yu Guo Date: Tue, 13 Dec 2022 16:54:42 -0800 Subject: [PATCH] [rab/gsab,api] Add resizable BackingStore creation This CL adds v8::ArrayBuffer::NewResizableBackingStore. This API is needed to support Mojo cross-process serialization of resizable buffers. See https://chromium-review.googlesource.com/c/chromium/src/+/4086949 Bug: chromium:1396361, v8:11111 Change-Id: I1d3ad367f28015184fd80fd2f05a37a3659d3a66 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/4093555 Commit-Queue: Shu-yu Guo Reviewed-by: Adam Klein Cr-Commit-Position: refs/heads/main@{#84826} --- include/v8-array-buffer.h | 15 ++++++++++++ src/api/api.cc | 35 ++++++++++++++++++++++++++++ src/objects/backing-store.cc | 16 +++++++++---- src/objects/backing-store.h | 4 ++++ test/cctest/test-api-array-buffer.cc | 20 +++++++++++++++- 5 files changed, 84 insertions(+), 6 deletions(-) diff --git a/include/v8-array-buffer.h b/include/v8-array-buffer.h index 60ca18bb94..804fc42c4b 100644 --- a/include/v8-array-buffer.h +++ b/include/v8-array-buffer.h @@ -256,6 +256,21 @@ class V8_EXPORT ArrayBuffer : public Object { void* data, size_t byte_length, v8::BackingStore::DeleterCallback deleter, void* deleter_data); + /** + * Returns a new resizable standalone BackingStore that is allocated using the + * array buffer allocator of the isolate. The result can be later passed to + * ArrayBuffer::New. + * + * |byte_length| must be <= |max_byte_length|. + * + * This function is usable without an isolate. Unlike |NewBackingStore| calls + * with an isolate, GCs cannot be triggered, and there are no + * retries. Allocation failure will cause the function to crash with an + * out-of-memory error. + */ + static std::unique_ptr NewResizableBackingStore( + size_t byte_length, size_t max_byte_length); + /** * Returns true if this ArrayBuffer may be detached. */ diff --git a/src/api/api.cc b/src/api/api.cc index e51bd24479..b425d61ce3 100644 --- a/src/api/api.cc +++ b/src/api/api.cc @@ -8350,6 +8350,41 @@ std::unique_ptr v8::ArrayBuffer::NewBackingStore( static_cast(backing_store.release())); } +// static +std::unique_ptr v8::ArrayBuffer::NewResizableBackingStore( + size_t byte_length, size_t max_byte_length) { + Utils::ApiCheck(i::v8_flags.harmony_rab_gsab, + "v8::ArrayBuffer::NewResizableBackingStore", + "Constructing resizable ArrayBuffers is not supported"); + Utils::ApiCheck(byte_length <= max_byte_length, + "v8::ArrayBuffer::NewResizableBackingStore", + "Cannot construct resizable ArrayBuffer, byte_length must be " + "<= max_byte_length"); + Utils::ApiCheck( + byte_length <= i::JSArrayBuffer::kMaxByteLength, + "v8::ArrayBuffer::NewResizableBackingStore", + "Cannot construct resizable ArrayBuffer, requested length is too big"); + + size_t page_size, initial_pages, max_pages; + if (i::JSArrayBuffer::GetResizableBackingStorePageConfiguration( + nullptr, byte_length, max_byte_length, i::kDontThrow, &page_size, + &initial_pages, &max_pages) + .IsNothing()) { + i::V8::FatalProcessOutOfMemory(nullptr, + "v8::ArrayBuffer::NewResizableBackingStore"); + } + std::unique_ptr backing_store = + i::BackingStore::TryAllocateAndPartiallyCommitMemory( + nullptr, byte_length, max_byte_length, page_size, initial_pages, + max_pages, i::WasmMemoryFlag::kNotWasm, i::SharedFlag::kNotShared); + if (!backing_store) { + i::V8::FatalProcessOutOfMemory(nullptr, + "v8::ArrayBuffer::NewResizableBackingStore"); + } + return std::unique_ptr( + static_cast(backing_store.release())); +} + Local v8::ArrayBufferView::Buffer() { i::Handle obj = Utils::OpenHandle(this); i::Handle buffer; diff --git a/src/objects/backing-store.cc b/src/objects/backing-store.cc index 1265accb24..019de9525d 100644 --- a/src/objects/backing-store.cc +++ b/src/objects/backing-store.cc @@ -347,8 +347,10 @@ std::unique_ptr BackingStore::TryAllocateAndPartiallyCommitMemory( // Collect garbage and retry. did_retry = true; // TODO(wasm): try Heap::EagerlyFreeExternalMemory() first? - isolate->heap()->MemoryPressureNotification( - MemoryPressureLevel::kCritical, true); + if (isolate != nullptr) { + isolate->heap()->MemoryPressureNotification( + MemoryPressureLevel::kCritical, true); + } } return false; }; @@ -368,7 +370,9 @@ std::unique_ptr BackingStore::TryAllocateAndPartiallyCommitMemory( }; if (!gc_retry(allocate_pages)) { // Page allocator could not reserve enough pages. - RecordStatus(isolate, AllocationStatus::kOtherFailure); + if (isolate != nullptr) { + RecordStatus(isolate, AllocationStatus::kOtherFailure); + } TRACE_BS("BSw:try failed to allocate pages\n"); return {}; } @@ -403,8 +407,10 @@ std::unique_ptr BackingStore::TryAllocateAndPartiallyCommitMemory( DebugCheckZero(buffer_start, byte_length); // touch the bytes. - RecordStatus(isolate, did_retry ? AllocationStatus::kSuccessAfterRetry - : AllocationStatus::kSuccess); + if (isolate != nullptr) { + RecordStatus(isolate, did_retry ? AllocationStatus::kSuccessAfterRetry + : AllocationStatus::kSuccess); + } const bool is_wasm_memory = wasm_memory != WasmMemoryFlag::kNotWasm; ResizableFlag resizable = diff --git a/src/objects/backing-store.h b/src/objects/backing-store.h index 699e2c1f31..66840a46ca 100644 --- a/src/objects/backing-store.h +++ b/src/objects/backing-store.h @@ -62,6 +62,10 @@ class V8_EXPORT_PRIVATE BackingStore : public BackingStoreBase { #endif // V8_ENABLE_WEBASSEMBLY // Tries to allocate `maximum_pages` of memory and commit `initial_pages`. + // + // If {isolate} is not null, initial failure to allocate the backing store may + // trigger GC, after which the allocation is retried. If {isolate} is null, no + // GC will be triggered. static std::unique_ptr TryAllocateAndPartiallyCommitMemory( Isolate* isolate, size_t byte_length, size_t max_byte_length, size_t page_size, size_t initial_pages, size_t maximum_pages, diff --git a/test/cctest/test-api-array-buffer.cc b/test/cctest/test-api-array-buffer.cc index 667dc1c6e3..5977ecd230 100644 --- a/test/cctest/test-api-array-buffer.cc +++ b/test/cctest/test-api-array-buffer.cc @@ -449,11 +449,29 @@ THREADED_TEST(ArrayBuffer_NewBackingStore) { std::shared_ptr backing_store = v8::ArrayBuffer::NewBackingStore(isolate, 100); CHECK(!backing_store->IsShared()); + CHECK(!backing_store->IsResizableByUserJavaScript()); Local ab = v8::ArrayBuffer::New(isolate, backing_store); CHECK_EQ(backing_store.get(), ab->GetBackingStore().get()); CHECK_EQ(backing_store->Data(), ab->Data()); } +THREADED_TEST(ArrayBuffer_NewResizableBackingStore) { + FLAG_SCOPE(harmony_rab_gsab); + + LocalContext env; + v8::Isolate* isolate = env->GetIsolate(); + v8::HandleScope handle_scope(isolate); + std::shared_ptr backing_store = + v8::ArrayBuffer::NewResizableBackingStore(32, 1024); + CHECK(!backing_store->IsShared()); + CHECK(backing_store->IsResizableByUserJavaScript()); + CHECK_EQ(1024, backing_store->MaxByteLength()); + Local ab = v8::ArrayBuffer::New(isolate, backing_store); + CHECK_EQ(backing_store.get(), ab->GetBackingStore().get()); + CHECK_EQ(backing_store->Data(), ab->Data()); + CHECK_EQ(backing_store->MaxByteLength(), ab->MaxByteLength()); +} + THREADED_TEST(SharedArrayBuffer_NewBackingStore) { LocalContext env; v8::Isolate* isolate = env->GetIsolate(); @@ -461,6 +479,7 @@ THREADED_TEST(SharedArrayBuffer_NewBackingStore) { std::shared_ptr backing_store = v8::SharedArrayBuffer::NewBackingStore(isolate, 100); CHECK(backing_store->IsShared()); + CHECK(!backing_store->IsResizableByUserJavaScript()); Local ab = v8::SharedArrayBuffer::New(isolate, backing_store); CHECK_EQ(backing_store.get(), ab->GetBackingStore().get()); @@ -819,7 +838,6 @@ TEST(ArrayBuffer_Resizable) { v8::Isolate* isolate = env->GetIsolate(); v8::HandleScope handle_scope(isolate); - // TODO(v8:11111): Expose ability to create resizable buffers to API? const char rab_source[] = "new ArrayBuffer(32, { maxByteLength: 1024 });"; v8::Local rab = CompileRun(rab_source).As(); CHECK(rab->GetBackingStore()->IsResizableByUserJavaScript());