[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 <syg@chromium.org>
Reviewed-by: Adam Klein <adamk@chromium.org>
Cr-Commit-Position: refs/heads/main@{#84826}
This commit is contained in:
Shu-yu Guo 2022-12-13 16:54:42 -08:00 committed by V8 LUCI CQ
parent c3568fdb87
commit 4757205b3c
5 changed files with 84 additions and 6 deletions

View File

@ -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<BackingStore> NewResizableBackingStore(
size_t byte_length, size_t max_byte_length);
/**
* Returns true if this ArrayBuffer may be detached.
*/

View File

@ -8350,6 +8350,41 @@ std::unique_ptr<v8::BackingStore> v8::ArrayBuffer::NewBackingStore(
static_cast<v8::BackingStore*>(backing_store.release()));
}
// static
std::unique_ptr<BackingStore> 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<i::BackingStoreBase> 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<v8::BackingStore>(
static_cast<v8::BackingStore*>(backing_store.release()));
}
Local<ArrayBuffer> v8::ArrayBufferView::Buffer() {
i::Handle<i::JSArrayBufferView> obj = Utils::OpenHandle(this);
i::Handle<i::JSArrayBuffer> buffer;

View File

@ -347,8 +347,10 @@ std::unique_ptr<BackingStore> 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> 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> 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 =

View File

@ -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<BackingStore> TryAllocateAndPartiallyCommitMemory(
Isolate* isolate, size_t byte_length, size_t max_byte_length,
size_t page_size, size_t initial_pages, size_t maximum_pages,

View File

@ -449,11 +449,29 @@ THREADED_TEST(ArrayBuffer_NewBackingStore) {
std::shared_ptr<v8::BackingStore> backing_store =
v8::ArrayBuffer::NewBackingStore(isolate, 100);
CHECK(!backing_store->IsShared());
CHECK(!backing_store->IsResizableByUserJavaScript());
Local<v8::ArrayBuffer> 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<v8::BackingStore> backing_store =
v8::ArrayBuffer::NewResizableBackingStore(32, 1024);
CHECK(!backing_store->IsShared());
CHECK(backing_store->IsResizableByUserJavaScript());
CHECK_EQ(1024, backing_store->MaxByteLength());
Local<v8::ArrayBuffer> 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<v8::BackingStore> backing_store =
v8::SharedArrayBuffer::NewBackingStore(isolate, 100);
CHECK(backing_store->IsShared());
CHECK(!backing_store->IsResizableByUserJavaScript());
Local<v8::SharedArrayBuffer> 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<v8::ArrayBuffer> rab = CompileRun(rab_source).As<v8::ArrayBuffer>();
CHECK(rab->GetBackingStore()->IsResizableByUserJavaScript());