Commit 8c0d9618 authored by Bill Budge's avatar Bill Budge Committed by Commit Bot

[page allocator] Allow some partition Realloc's to return nullptr

- Modifies partition allocator, adding a TryRealloc method that
  returns nullptr on failure, rather than OOM crashing.
- Modifies the single call site in Blink for buffer resizing during
  serialization.

Bug: chromium:857387
Change-Id: I9bf8d3ced837a9dd4f96a953bb52ccad3078dfa1
Reviewed-on: https://chromium-review.googlesource.com/1138859Reviewed-by: default avatarKentaro Hara <haraken@chromium.org>
Reviewed-by: default avatarMichael Lippautz <mlippautz@chromium.org>
Reviewed-by: default avatarTom Sepez <tsepez@chromium.org>
Commit-Queue: Bill Budge <bbudge@chromium.org>
Cr-Commit-Position: refs/heads/master@{#577561}
parent 3905d1db
......@@ -341,6 +341,13 @@ void* PartitionRootGeneric::Realloc(void* ptr,
return PartitionReallocGenericFlags(this, 0, ptr, new_size, type_name);
}
void* PartitionRootGeneric::TryRealloc(void* ptr,
size_t new_size,
const char* type_name) {
return PartitionReallocGenericFlags(this, PartitionAllocReturnNull, ptr,
new_size, type_name);
}
static size_t PartitionPurgePage(internal::PartitionPage* page, bool discard) {
const internal::PartitionBucket* bucket = page->bucket;
size_t slot_size = bucket->slot_size;
......
......@@ -148,6 +148,9 @@ struct BASE_EXPORT PartitionRootGeneric : public internal::PartitionRootBase {
ALWAYS_INLINE void Free(void* ptr);
NOINLINE void* Realloc(void* ptr, size_t new_size, const char* type_name);
// Overload that may return nullptr if reallocation isn't possible. In this
// case, |ptr| remains valid.
NOINLINE void* TryRealloc(void* ptr, size_t new_size, const char* type_name);
ALWAYS_INLINE size_t ActualSize(size_t size);
......
......@@ -182,7 +182,13 @@ class PartitionAllocTest : public testing::Test {
}
}
void DoReturnNullTest(size_t allocSize, bool use_realloc) {
enum ReturnNullTestMode {
kPartitionAllocGenericFlags,
kPartitionReallocGenericFlags,
kPartitionRootGenericTryRealloc,
};
void DoReturnNullTest(size_t allocSize, ReturnNullTestMode mode) {
// TODO(crbug.com/678782): Where necessary and possible, disable the
// platform's OOM-killing behavior. OOM-killing makes this test flaky on
// low-memory devices.
......@@ -202,17 +208,29 @@ class PartitionAllocTest : public testing::Test {
int i;
for (i = 0; i < numAllocations; ++i) {
if (use_realloc) {
ptrs[i] = PartitionAllocGenericFlags(
generic_allocator.root(), PartitionAllocReturnNull, 1, type_name);
ptrs[i] = PartitionReallocGenericFlags(generic_allocator.root(),
switch (mode) {
case kPartitionAllocGenericFlags: {
ptrs[i] = PartitionAllocGenericFlags(generic_allocator.root(),
PartitionAllocReturnNull,
ptrs[i], allocSize, type_name);
} else {
ptrs[i] = PartitionAllocGenericFlags(generic_allocator.root(),
PartitionAllocReturnNull,
allocSize, type_name);
allocSize, type_name);
break;
}
case kPartitionReallocGenericFlags: {
ptrs[i] = PartitionAllocGenericFlags(
generic_allocator.root(), PartitionAllocReturnNull, 1, type_name);
ptrs[i] = PartitionReallocGenericFlags(generic_allocator.root(),
PartitionAllocReturnNull,
ptrs[i], allocSize, type_name);
break;
}
case kPartitionRootGenericTryRealloc: {
ptrs[i] = PartitionAllocGenericFlags(
generic_allocator.root(), PartitionAllocReturnNull, 1, type_name);
ptrs[i] = generic_allocator.root()->TryRealloc(ptrs[i], allocSize,
type_name);
}
}
if (!i)
EXPECT_TRUE(ptrs[0]);
if (!ptrs[i]) {
......@@ -1315,23 +1333,41 @@ TEST_F(PartitionAllocTest, LostFreePagesBug) {
// process, so they won't pollute other tests.
TEST_F(PartitionAllocDeathTest, RepeatedAllocReturnNullDirect) {
// A direct-mapped allocation size.
EXPECT_DEATH(DoReturnNullTest(32 * 1024 * 1024, false), "DoReturnNullTest");
EXPECT_DEATH(DoReturnNullTest(32 * 1024 * 1024, kPartitionAllocGenericFlags),
"DoReturnNullTest");
}
// Repeating above test with Realloc
TEST_F(PartitionAllocDeathTest, RepeatedReallocReturnNullDirect) {
EXPECT_DEATH(DoReturnNullTest(32 * 1024 * 1024, true), "DoReturnNullTest");
EXPECT_DEATH(
DoReturnNullTest(32 * 1024 * 1024, kPartitionReallocGenericFlags),
"DoReturnNullTest");
}
// Repeating above test with TryRealloc
TEST_F(PartitionAllocDeathTest, RepeatedTryReallocReturnNullDirect) {
EXPECT_DEATH(
DoReturnNullTest(32 * 1024 * 1024, kPartitionRootGenericTryRealloc),
"DoReturnNullTest");
}
// Test "return null" with a 512 kB block size.
TEST_F(PartitionAllocDeathTest, RepeatedAllocReturnNull) {
// A single-slot but non-direct-mapped allocation size.
EXPECT_DEATH(DoReturnNullTest(512 * 1024, false), "DoReturnNullTest");
EXPECT_DEATH(DoReturnNullTest(512 * 1024, kPartitionAllocGenericFlags),
"DoReturnNullTest");
}
// Repeating above test with Realloc.
TEST_F(PartitionAllocDeathTest, RepeatedReallocReturnNull) {
EXPECT_DEATH(DoReturnNullTest(512 * 1024, true), "DoReturnNullTest");
EXPECT_DEATH(DoReturnNullTest(512 * 1024, kPartitionReallocGenericFlags),
"DoReturnNullTest");
}
// Repeating above test with TryRealloc.
TEST_F(PartitionAllocDeathTest, RepeatedTryReallocReturnNull) {
EXPECT_DEATH(DoReturnNullTest(512 * 1024, kPartitionRootGenericTryRealloc),
"DoReturnNullTest");
}
#endif // !defined(ARCH_CPU_64_BITS) || (defined(OS_POSIX) &&
......
......@@ -581,8 +581,8 @@ void* V8ScriptValueSerializer::ReallocateBufferMemory(void* old_buffer,
size_t size,
size_t* actual_size) {
*actual_size = WTF::Partitions::BufferActualSize(size);
return WTF::Partitions::BufferRealloc(old_buffer, *actual_size,
"SerializedScriptValue buffer");
return WTF::Partitions::BufferTryRealloc(old_buffer, *actual_size,
"SerializedScriptValue buffer");
}
void V8ScriptValueSerializer::FreeBufferMemory(void* buffer) {
......
......@@ -93,6 +93,9 @@ class CORE_EXPORT V8ScriptValueSerializer
v8::Maybe<uint32_t> GetWasmModuleTransferId(
v8::Isolate*,
v8::Local<v8::WasmCompiledModule>) override;
// Reallocates memory at |ptr| to the new size and returns the new pointer or
// nullptr on failure. |actual_size| will hold the actual size of allocation
// requested.
void* ReallocateBufferMemory(void* old_buffer,
size_t,
size_t* actual_size) override;
......
......@@ -101,6 +101,11 @@ class WTF_EXPORT Partitions {
const char* type_name) {
return BufferPartition()->Realloc(p, n, type_name);
}
ALWAYS_INLINE static void* BufferTryRealloc(void* p,
size_t n,
const char* type_name) {
return BufferPartition()->TryRealloc(p, n, type_name);
}
ALWAYS_INLINE static void BufferFree(void* p) { BufferPartition()->Free(p); }
ALWAYS_INLINE static size_t BufferActualSize(size_t n) {
return BufferPartition()->ActualSize(n);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment