[heap] Add read-only heap sharing behind a flag

Piggybacking of splitting heap setup, this change adds a shared
read-only heap and a flag to enable it.

Also makes CallOnce use std::function instead of a raw function
pointer so the CL can use lambdas with CallOnce.

Bug: v8:7464
Change-Id: I9a97fb1baa6badca39a7381de3fd9e01f5969340
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1518180
Reviewed-by: Hannes Payer <hpayer@chromium.org>
Reviewed-by: Dan Elphick <delphick@chromium.org>
Commit-Queue: Maciej Goszczycki <goszczycki@google.com>
Cr-Commit-Position: refs/heads/master@{#60241}
This commit is contained in:
Maciej Goszczycki 2019-03-14 13:43:30 +00:00 committed by Commit Bot
parent 04bb707e52
commit e3aad1c84b
8 changed files with 91 additions and 14 deletions

View File

@ -171,6 +171,9 @@ declare_args() {
# setting the "check_v8_header_includes" gclient variable to run a
# specific hook).
v8_check_header_includes = false
# Enable sharing read-only space across isolates.
v8_enable_shared_ro_heap = false
}
# We reuse the snapshot toolchain for building torque and other generators to
@ -214,6 +217,9 @@ assert(!v8_enable_lite_mode || v8_enable_embedded_builtins,
assert(!v8_enable_lite_mode || v8_use_snapshot,
"Lite mode requires a snapshot build")
assert(v8_use_snapshot || !v8_enable_shared_ro_heap,
"Nosnapshot builds are not supported with shared read-only heap enabled")
v8_random_seed = "314159265"
v8_toolset_for_shell = "host"
@ -404,6 +410,9 @@ config("features") {
if (v8_use_siphash) {
defines += [ "V8_USE_SIPHASH" ]
}
if (v8_enable_shared_ro_heap) {
defines += [ "V8_SHARED_RO_HEAP" ]
}
}
config("toolchain") {

View File

@ -74,7 +74,6 @@ enum : uint8_t {
ONCE_STATE_DONE = 2
};
typedef void (*NoArgFunction)();
typedef void (*PointerArgFunction)(void* arg);
template <typename T>
@ -85,13 +84,12 @@ struct OneArgFunction {
V8_BASE_EXPORT void CallOnceImpl(OnceType* once,
std::function<void()> init_func);
inline void CallOnce(OnceType* once, NoArgFunction init_func) {
inline void CallOnce(OnceType* once, std::function<void()> init_func) {
if (once->load(std::memory_order_acquire) != ONCE_STATE_DONE) {
CallOnceImpl(once, init_func);
}
}
template <typename Arg>
inline void CallOnce(OnceType* once,
typename OneArgFunction<Arg*>::type init_func, Arg* arg) {

View File

@ -3550,14 +3550,21 @@ const char* Heap::GarbageCollectionReasonToString(
}
bool Heap::Contains(HeapObject value) {
// Check RO_SPACE first because IsOutsideAllocatedSpace cannot account for a
// shared RO_SPACE.
// TODO(goszczycki): Exclude read-only space. Use ReadOnlyHeap::Contains where
// appropriate.
if (read_only_space_ != nullptr && read_only_space_->Contains(value)) {
return true;
}
if (memory_allocator()->IsOutsideAllocatedSpace(value->address())) {
return false;
}
return HasBeenSetUp() &&
(new_space_->ToSpaceContains(value) || old_space_->Contains(value) ||
code_space_->Contains(value) || map_space_->Contains(value) ||
lo_space_->Contains(value) || read_only_space_->Contains(value) ||
code_lo_space_->Contains(value) || new_lo_space_->Contains(value));
lo_space_->Contains(value) || code_lo_space_->Contains(value) ||
new_lo_space_->Contains(value));
}
bool Heap::InSpace(HeapObject value, AllocationSpace space) {
@ -4550,8 +4557,9 @@ void Heap::SetUp() {
}
void Heap::SetUpFromReadOnlyHeap(ReadOnlyHeap* ro_heap) {
DCHECK_NULL(read_only_space_);
DCHECK_NOT_NULL(ro_heap);
DCHECK_IMPLIES(read_only_space_ != nullptr,
read_only_space_ == ro_heap->read_only_space());
read_only_heap_ = ro_heap;
space_[RO_SPACE] = read_only_space_ = ro_heap->read_only_space();
}

View File

@ -4,29 +4,72 @@
#include "src/heap/read-only-heap.h"
#include <cstring>
#include "src/base/once.h"
#include "src/heap/heap-inl.h"
#include "src/heap/spaces.h"
#include "src/snapshot/read-only-deserializer.h"
namespace v8 {
namespace internal {
#ifdef V8_SHARED_RO_HEAP
V8_DECLARE_ONCE(setup_ro_heap_once);
ReadOnlyHeap* shared_ro_heap = nullptr;
#endif
// static
void ReadOnlyHeap::SetUp(Isolate* isolate, ReadOnlyDeserializer* des) {
#ifdef V8_SHARED_RO_HEAP
void* isolate_ro_roots = reinterpret_cast<void*>(
isolate->roots_table().read_only_roots_begin().address());
base::CallOnce(&setup_ro_heap_once, [isolate, des, isolate_ro_roots]() {
shared_ro_heap = Init(isolate, des);
if (des != nullptr) {
std::memcpy(shared_ro_heap->read_only_roots_, isolate_ro_roots,
kEntriesCount * sizeof(Address));
}
});
isolate->heap()->SetUpFromReadOnlyHeap(shared_ro_heap);
if (des != nullptr) {
std::memcpy(isolate_ro_roots, shared_ro_heap->read_only_roots_,
kEntriesCount * sizeof(Address));
}
#else
Init(isolate, des);
#endif // V8_SHARED_RO_HEAP
}
void ReadOnlyHeap::OnCreateHeapObjectsComplete() {
DCHECK(!deserializing_);
#ifdef V8_SHARED_RO_HEAP
read_only_space_->Forget();
#endif
read_only_space_->MarkAsReadOnly();
}
// static
ReadOnlyHeap* ReadOnlyHeap::Init(Isolate* isolate, ReadOnlyDeserializer* des) {
auto* ro_heap = new ReadOnlyHeap(new ReadOnlySpace(isolate->heap()));
isolate->heap()->SetUpFromReadOnlyHeap(ro_heap);
if (des != nullptr) {
des->DeserializeInto(isolate);
ro_heap->deserializing_ = true;
#ifdef V8_SHARED_RO_HEAP
ro_heap->read_only_space_->Forget();
#endif
ro_heap->read_only_space_->MarkAsReadOnly();
}
}
void ReadOnlyHeap::OnCreateHeapObjectsComplete() {
read_only_space_->MarkAsReadOnly();
return ro_heap;
}
void ReadOnlyHeap::OnHeapTearDown() {
#ifndef V8_SHARED_RO_HEAP
delete read_only_space_;
delete this;
#endif
}
// static

View File

@ -9,7 +9,6 @@
#include "src/heap/heap.h"
#include "src/objects.h"
#include "src/roots.h"
#include "src/snapshot/read-only-deserializer.h"
namespace v8 {
namespace internal {
@ -18,16 +17,19 @@ class ReadOnlySpace;
class ReadOnlyDeserializer;
// This class transparently manages read-only space, roots and cache creation
// and destruction. Eventually this will allow sharing these artifacts between
// isolates.
// and destruction.
class ReadOnlyHeap final {
public:
static constexpr size_t kEntriesCount =
static_cast<size_t>(RootIndex::kReadOnlyRootsCount);
// If necessary create read-only heap and initialize its artifacts (if the
// deserializer is provided).
// TODO(goszczycki): Ideally we'd create this without needing a heap.
static void SetUp(Isolate* isolate, ReadOnlyDeserializer* des);
// Indicate that all read-only space objects have been created and will not
// be written to.
// be written to. This is not thread safe, and should really only be used as
// part of mksnapshot or when read-only heap sharing is disabled.
void OnCreateHeapObjectsComplete();
// Indicate that the current isolate no longer requires the read-only heap and
// it may be safely disposed of.
@ -42,9 +44,16 @@ class ReadOnlyHeap final {
ReadOnlySpace* read_only_space() const { return read_only_space_; }
private:
static ReadOnlyHeap* Init(Isolate* isolate, ReadOnlyDeserializer* des);
bool deserializing_ = false;
ReadOnlySpace* read_only_space_ = nullptr;
std::vector<Object> read_only_object_cache_;
#ifdef V8_SHARED_RO_HEAP
Address read_only_roots_[kEntriesCount];
#endif
explicit ReadOnlyHeap(ReadOnlySpace* ro_space) : read_only_space_(ro_space) {}
DISALLOW_COPY_AND_ASSIGN(ReadOnlyHeap);
};

View File

@ -3305,6 +3305,12 @@ void ReadOnlyPage::MakeHeaderRelocatable() {
}
}
void ReadOnlySpace::Forget() {
for (Page* p : *this) {
heap()->memory_allocator()->PreFreeMemory(p);
}
}
void ReadOnlySpace::SetPermissionsForPages(PageAllocator::Permission access) {
MemoryAllocator* memory_allocator = heap()->memory_allocator();
for (Page* p : *this) {

View File

@ -2992,6 +2992,9 @@ class ReadOnlySpace : public PagedSpace {
void ClearStringPaddingIfNeeded();
void MarkAsReadOnly();
// Make the heap forget the space for memory bookkeeping purposes
// (e.g. prevent space's memory from registering as leaked).
void Forget();
// During boot the free_space_map is created, and afterwards we may need
// to write it into the free list nodes that were already created.

View File

@ -497,6 +497,7 @@ class RootsTable {
friend class Isolate;
friend class Heap;
friend class Factory;
friend class ReadOnlyHeap;
friend class ReadOnlyRoots;
friend class RootsSerializer;
};