cppgc: Introduce HeapVisitor
HeapVisitor is a simple visitor implementing both visitation and accept interfaces. HeapVisitor is aimed to replace functions that traverse the heap in Blink: PrepareForSweep, Sweep, FinalizeSweep, MakeConsistentForGC, ObjectPayloadSize, VerifyObjectStartBitmap, CollectStatistics, etc. This CL also introduces a Heap::ObjectPayloadSize function. Bug: chromium:1056170 Change-Id: I85a2b8b572486bea29704fc436b002d8f7405201 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2165733 Commit-Queue: Anton Bikineev <bikineev@chromium.org> Reviewed-by: Michael Lippautz <mlippautz@chromium.org> Reviewed-by: Omer Katz <omerkatz@chromium.org> Reviewed-by: Ulan Degenbaev <ulan@chromium.org> Cr-Commit-Position: refs/heads/master@{#67458}
This commit is contained in:
parent
ec5149ef1e
commit
f2ece54b3e
1
BUILD.gn
1
BUILD.gn
@ -4033,6 +4033,7 @@ v8_source_set("cppgc_base") {
|
|||||||
"src/heap/cppgc/heap-page.h",
|
"src/heap/cppgc/heap-page.h",
|
||||||
"src/heap/cppgc/heap-space.cc",
|
"src/heap/cppgc/heap-space.cc",
|
||||||
"src/heap/cppgc/heap-space.h",
|
"src/heap/cppgc/heap-space.h",
|
||||||
|
"src/heap/cppgc/heap-visitor.h",
|
||||||
"src/heap/cppgc/heap.cc",
|
"src/heap/cppgc/heap.cc",
|
||||||
"src/heap/cppgc/heap.h",
|
"src/heap/cppgc/heap.h",
|
||||||
"src/heap/cppgc/liveness-broker.cc",
|
"src/heap/cppgc/liveness-broker.cc",
|
||||||
|
@ -80,6 +80,19 @@ NormalPage::NormalPage(Heap* heap, BaseSpace* space)
|
|||||||
|
|
||||||
NormalPage::~NormalPage() = default;
|
NormalPage::~NormalPage() = default;
|
||||||
|
|
||||||
|
NormalPage::iterator NormalPage::begin() {
|
||||||
|
const auto& lab = NormalPageSpace::From(space())->linear_allocation_buffer();
|
||||||
|
return iterator(reinterpret_cast<HeapObjectHeader*>(PayloadStart()),
|
||||||
|
lab.start(), lab.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
NormalPage::const_iterator NormalPage::begin() const {
|
||||||
|
const auto& lab = NormalPageSpace::From(space())->linear_allocation_buffer();
|
||||||
|
return const_iterator(
|
||||||
|
reinterpret_cast<const HeapObjectHeader*>(PayloadStart()), lab.start(),
|
||||||
|
lab.size());
|
||||||
|
}
|
||||||
|
|
||||||
Address NormalPage::PayloadStart() {
|
Address NormalPage::PayloadStart() {
|
||||||
return AlignAddress((reinterpret_cast<Address>(this + 1)),
|
return AlignAddress((reinterpret_cast<Address>(this + 1)),
|
||||||
kAllocationGranularity);
|
kAllocationGranularity);
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
#include "src/base/iterator.h"
|
#include "src/base/iterator.h"
|
||||||
#include "src/base/macros.h"
|
#include "src/base/macros.h"
|
||||||
|
#include "src/heap/cppgc/globals.h"
|
||||||
#include "src/heap/cppgc/heap-object-header.h"
|
#include "src/heap/cppgc/heap-object-header.h"
|
||||||
|
|
||||||
namespace cppgc {
|
namespace cppgc {
|
||||||
@ -49,7 +50,15 @@ class V8_EXPORT_PRIVATE NormalPage final : public BasePage {
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
class IteratorImpl : v8::base::iterator<std::forward_iterator_tag, T> {
|
class IteratorImpl : v8::base::iterator<std::forward_iterator_tag, T> {
|
||||||
public:
|
public:
|
||||||
explicit IteratorImpl(T* p) : p_(p) {}
|
explicit IteratorImpl(T* p, ConstAddress lab_start = nullptr,
|
||||||
|
size_t lab_size = 0)
|
||||||
|
: p_(p), lab_start_(lab_start), lab_size_(lab_size) {
|
||||||
|
DCHECK(p);
|
||||||
|
DCHECK_EQ(0, (lab_size & (sizeof(T) - 1)));
|
||||||
|
if (reinterpret_cast<ConstAddress>(p_) == lab_start_) {
|
||||||
|
p_ += (lab_size_ / sizeof(T));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
T& operator*() { return *p_; }
|
T& operator*() { return *p_; }
|
||||||
const T& operator*() const { return *p_; }
|
const T& operator*() const { return *p_; }
|
||||||
@ -58,19 +67,26 @@ class V8_EXPORT_PRIVATE NormalPage final : public BasePage {
|
|||||||
bool operator!=(IteratorImpl other) const { return !(*this == other); }
|
bool operator!=(IteratorImpl other) const { return !(*this == other); }
|
||||||
|
|
||||||
IteratorImpl& operator++() {
|
IteratorImpl& operator++() {
|
||||||
p_ += (p_->GetSize() / sizeof(T));
|
const size_t size = p_->GetSize();
|
||||||
|
DCHECK_EQ(0, (size & (sizeof(T) - 1)));
|
||||||
|
p_ += (size / sizeof(T));
|
||||||
|
if (reinterpret_cast<ConstAddress>(p_) == lab_start_) {
|
||||||
|
p_ += (lab_size_ / sizeof(T));
|
||||||
|
}
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
IteratorImpl operator++(int) {
|
IteratorImpl operator++(int) {
|
||||||
IteratorImpl temp(*this);
|
IteratorImpl temp(*this);
|
||||||
p_ += (p_->GetSize() / sizeof(T));
|
++(*this);
|
||||||
return temp;
|
return temp;
|
||||||
}
|
}
|
||||||
|
|
||||||
T* base() { return p_; }
|
T* base() const { return p_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
T* p_;
|
T* p_;
|
||||||
|
ConstAddress lab_start_;
|
||||||
|
size_t lab_size_;
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -83,13 +99,17 @@ class V8_EXPORT_PRIVATE NormalPage final : public BasePage {
|
|||||||
// corresponding space (i.e. be swept when called).
|
// corresponding space (i.e. be swept when called).
|
||||||
static void Destroy(NormalPage*);
|
static void Destroy(NormalPage*);
|
||||||
|
|
||||||
iterator begin() {
|
static NormalPage* From(BasePage* page) {
|
||||||
return iterator(reinterpret_cast<HeapObjectHeader*>(PayloadStart()));
|
DCHECK(!page->is_large());
|
||||||
|
return static_cast<NormalPage*>(page);
|
||||||
}
|
}
|
||||||
const_iterator begin() const {
|
static const NormalPage* From(const BasePage* page) {
|
||||||
return const_iterator(
|
return From(const_cast<BasePage*>(page));
|
||||||
reinterpret_cast<const HeapObjectHeader*>(PayloadStart()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
iterator begin();
|
||||||
|
const_iterator begin() const;
|
||||||
|
|
||||||
iterator end() {
|
iterator end() {
|
||||||
return iterator(reinterpret_cast<HeapObjectHeader*>(PayloadEnd()));
|
return iterator(reinterpret_cast<HeapObjectHeader*>(PayloadEnd()));
|
||||||
}
|
}
|
||||||
@ -118,6 +138,14 @@ class V8_EXPORT_PRIVATE LargePage final : public BasePage {
|
|||||||
// corresponding space (i.e. be swept when called).
|
// corresponding space (i.e. be swept when called).
|
||||||
static void Destroy(LargePage*);
|
static void Destroy(LargePage*);
|
||||||
|
|
||||||
|
static LargePage* From(BasePage* page) {
|
||||||
|
DCHECK(page->is_large());
|
||||||
|
return static_cast<LargePage*>(page);
|
||||||
|
}
|
||||||
|
static const LargePage* From(const BasePage* page) {
|
||||||
|
return From(const_cast<BasePage*>(page));
|
||||||
|
}
|
||||||
|
|
||||||
HeapObjectHeader* ObjectHeader();
|
HeapObjectHeader* ObjectHeader();
|
||||||
const HeapObjectHeader* ObjectHeader() const;
|
const HeapObjectHeader* ObjectHeader() const;
|
||||||
|
|
||||||
|
@ -60,27 +60,35 @@ class V8_EXPORT_PRIVATE NormalPageSpace final : public BaseSpace {
|
|||||||
public:
|
public:
|
||||||
class LinearAllocationBuffer {
|
class LinearAllocationBuffer {
|
||||||
public:
|
public:
|
||||||
void* Allocate(size_t alloc_size) {
|
Address Allocate(size_t alloc_size) {
|
||||||
DCHECK_GE(size_, alloc_size);
|
DCHECK_GE(size_, alloc_size);
|
||||||
void* result = start_;
|
Address result = start_;
|
||||||
start_ += alloc_size;
|
start_ += alloc_size;
|
||||||
size_ -= alloc_size;
|
size_ -= alloc_size;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Set(void* ptr, size_t size) {
|
void Set(Address ptr, size_t size) {
|
||||||
start_ = static_cast<uint8_t*>(ptr);
|
start_ = ptr;
|
||||||
size_ = size;
|
size_ = size;
|
||||||
}
|
}
|
||||||
|
|
||||||
void* start() const { return start_; }
|
Address start() const { return start_; }
|
||||||
size_t size() const { return size_; }
|
size_t size() const { return size_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint8_t* start_ = nullptr;
|
Address start_ = nullptr;
|
||||||
size_t size_ = 0;
|
size_t size_ = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static NormalPageSpace* From(BaseSpace* space) {
|
||||||
|
DCHECK(!space->is_large());
|
||||||
|
return static_cast<NormalPageSpace*>(space);
|
||||||
|
}
|
||||||
|
static const NormalPageSpace* From(const BaseSpace* space) {
|
||||||
|
return From(const_cast<BaseSpace*>(space));
|
||||||
|
}
|
||||||
|
|
||||||
NormalPageSpace(RawHeap* heap, size_t index);
|
NormalPageSpace(RawHeap* heap, size_t index);
|
||||||
|
|
||||||
LinearAllocationBuffer& linear_allocation_buffer() { return current_lab_; }
|
LinearAllocationBuffer& linear_allocation_buffer() { return current_lab_; }
|
||||||
@ -98,6 +106,14 @@ class V8_EXPORT_PRIVATE NormalPageSpace final : public BaseSpace {
|
|||||||
|
|
||||||
class V8_EXPORT_PRIVATE LargePageSpace final : public BaseSpace {
|
class V8_EXPORT_PRIVATE LargePageSpace final : public BaseSpace {
|
||||||
public:
|
public:
|
||||||
|
static LargePageSpace* From(BaseSpace* space) {
|
||||||
|
DCHECK(space->is_large());
|
||||||
|
return static_cast<LargePageSpace*>(space);
|
||||||
|
}
|
||||||
|
static const LargePageSpace* From(const BaseSpace* space) {
|
||||||
|
return From(const_cast<BaseSpace*>(space));
|
||||||
|
}
|
||||||
|
|
||||||
LargePageSpace(RawHeap* heap, size_t index);
|
LargePageSpace(RawHeap* heap, size_t index);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
88
src/heap/cppgc/heap-visitor.h
Normal file
88
src/heap/cppgc/heap-visitor.h
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
// Copyright 2020 the V8 project authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#ifndef V8_HEAP_CPPGC_HEAP_VISITOR_H_
|
||||||
|
#define V8_HEAP_CPPGC_HEAP_VISITOR_H_
|
||||||
|
|
||||||
|
#include "src/heap/cppgc/heap-page.h"
|
||||||
|
#include "src/heap/cppgc/heap-space.h"
|
||||||
|
#include "src/heap/cppgc/raw-heap.h"
|
||||||
|
|
||||||
|
namespace cppgc {
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
// Visitor for heap, which also implements the accept (traverse) interface.
|
||||||
|
// Implements preorder traversal of the heap. The order of traversal is defined.
|
||||||
|
// Implemented as a CRTP visitor to avoid virtual calls and support better
|
||||||
|
// inlining.
|
||||||
|
template <typename Derived>
|
||||||
|
class HeapVisitor {
|
||||||
|
public:
|
||||||
|
void Traverse(RawHeap* heap) {
|
||||||
|
if (VisitHeapImpl(heap)) return;
|
||||||
|
for (auto& space : *heap) {
|
||||||
|
Traverse(space.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Traverse(BaseSpace* space) {
|
||||||
|
const bool is_stopped =
|
||||||
|
space->is_large()
|
||||||
|
? VisitLargePageSpaceImpl(LargePageSpace::From(space))
|
||||||
|
: VisitNormalPageSpaceImpl(NormalPageSpace::From(space));
|
||||||
|
if (is_stopped) return;
|
||||||
|
for (auto* page : *space) {
|
||||||
|
Traverse(page);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Traverse(BasePage* page) {
|
||||||
|
if (page->is_large()) {
|
||||||
|
auto* large_page = LargePage::From(page);
|
||||||
|
if (VisitLargePageImpl(large_page)) return;
|
||||||
|
VisitHeapObjectHeaderImpl(large_page->ObjectHeader());
|
||||||
|
} else {
|
||||||
|
auto* normal_page = NormalPage::From(page);
|
||||||
|
if (VisitNormalPageImpl(normal_page)) return;
|
||||||
|
for (auto& header : *normal_page) {
|
||||||
|
VisitHeapObjectHeaderImpl(&header);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// Visitor functions return true if no deeper processing is required.
|
||||||
|
// Users are supposed to override functions that need special treatment.
|
||||||
|
bool VisitHeap(RawHeap*) { return false; }
|
||||||
|
bool VisitNormalPageSpace(NormalPageSpace*) { return false; }
|
||||||
|
bool VisitLargePageSpace(LargePageSpace*) { return false; }
|
||||||
|
bool VisitNormalPage(NormalPage*) { return false; }
|
||||||
|
bool VisitLargePage(LargePage*) { return false; }
|
||||||
|
bool VisitHeapObjectHeader(HeapObjectHeader*) { return false; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Derived& ToDerived() { return static_cast<Derived&>(*this); }
|
||||||
|
|
||||||
|
bool VisitHeapImpl(RawHeap* heap) { return ToDerived().VisitHeap(heap); }
|
||||||
|
bool VisitNormalPageSpaceImpl(NormalPageSpace* space) {
|
||||||
|
return ToDerived().VisitNormalPageSpace(space);
|
||||||
|
}
|
||||||
|
bool VisitLargePageSpaceImpl(LargePageSpace* space) {
|
||||||
|
return ToDerived().VisitLargePageSpace(space);
|
||||||
|
}
|
||||||
|
bool VisitNormalPageImpl(NormalPage* page) {
|
||||||
|
return ToDerived().VisitNormalPage(page);
|
||||||
|
}
|
||||||
|
bool VisitLargePageImpl(LargePage* page) {
|
||||||
|
return ToDerived().VisitLargePage(page);
|
||||||
|
}
|
||||||
|
bool VisitHeapObjectHeaderImpl(HeapObjectHeader* hoh) {
|
||||||
|
return ToDerived().VisitHeapObjectHeader(hoh);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace cppgc
|
||||||
|
|
||||||
|
#endif // V8_HEAP_CPPGC_HEAP_VISITOR_H_
|
@ -8,7 +8,9 @@
|
|||||||
|
|
||||||
#include "src/base/platform/platform.h"
|
#include "src/base/platform/platform.h"
|
||||||
#include "src/heap/cppgc/heap-object-header-inl.h"
|
#include "src/heap/cppgc/heap-object-header-inl.h"
|
||||||
|
#include "src/heap/cppgc/heap-object-header.h"
|
||||||
#include "src/heap/cppgc/heap-page.h"
|
#include "src/heap/cppgc/heap-page.h"
|
||||||
|
#include "src/heap/cppgc/heap-visitor.h"
|
||||||
#include "src/heap/cppgc/stack.h"
|
#include "src/heap/cppgc/stack.h"
|
||||||
|
|
||||||
namespace cppgc {
|
namespace cppgc {
|
||||||
@ -25,6 +27,35 @@ constexpr bool NeedsConservativeStackScan(Heap::GCConfig config) {
|
|||||||
return config.stack_state == Heap::GCConfig::StackState::kNonEmpty;
|
return config.stack_state == Heap::GCConfig::StackState::kNonEmpty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ObjectSizeCounter : public HeapVisitor<ObjectSizeCounter> {
|
||||||
|
friend class HeapVisitor<ObjectSizeCounter>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
size_t GetSize(RawHeap* heap) {
|
||||||
|
Traverse(heap);
|
||||||
|
return accumulated_size_;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static size_t ObjectSize(const HeapObjectHeader* header) {
|
||||||
|
const size_t size =
|
||||||
|
header->IsLargeObject()
|
||||||
|
? static_cast<const LargePage*>(BasePage::FromPayload(header))
|
||||||
|
->PayloadSize()
|
||||||
|
: header->GetSize();
|
||||||
|
DCHECK_GE(size, sizeof(HeapObjectHeader));
|
||||||
|
return size - sizeof(HeapObjectHeader);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VisitHeapObjectHeader(HeapObjectHeader* header) {
|
||||||
|
if (header->IsFree()) return true;
|
||||||
|
accumulated_size_ += ObjectSize(header);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t accumulated_size_ = 0;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
// static
|
// static
|
||||||
@ -93,6 +124,10 @@ void Heap::CollectGarbage(GCConfig config) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t Heap::ObjectPayloadSize() const {
|
||||||
|
return ObjectSizeCounter().GetSize(const_cast<RawHeap*>(&raw_heap()));
|
||||||
|
}
|
||||||
|
|
||||||
Heap::NoGCScope::NoGCScope(Heap* heap) : heap_(heap) { heap_->no_gc_scope_++; }
|
Heap::NoGCScope::NoGCScope(Heap* heap) : heap_(heap) { heap_->no_gc_scope_++; }
|
||||||
|
|
||||||
Heap::NoGCScope::~NoGCScope() { heap_->no_gc_scope_--; }
|
Heap::NoGCScope::~NoGCScope() { heap_->no_gc_scope_--; }
|
||||||
|
@ -98,6 +98,8 @@ class V8_EXPORT_PRIVATE Heap final : public cppgc::Heap {
|
|||||||
PageBackend* page_backend() { return page_backend_.get(); }
|
PageBackend* page_backend() { return page_backend_.get(); }
|
||||||
const PageBackend* page_backend() const { return page_backend_.get(); }
|
const PageBackend* page_backend() const { return page_backend_.get(); }
|
||||||
|
|
||||||
|
size_t ObjectPayloadSize() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool in_no_gc_scope() const { return no_gc_scope_ > 0; }
|
bool in_no_gc_scope() const { return no_gc_scope_ > 0; }
|
||||||
bool is_allocation_allowed() const { return no_allocation_scope_ == 0; }
|
bool is_allocation_allowed() const { return no_allocation_scope_ == 0; }
|
||||||
|
@ -20,9 +20,8 @@ void* ObjectAllocator::AllocateObject(size_t size, GCInfoIndex gcinfo) {
|
|||||||
const size_t allocation_size =
|
const size_t allocation_size =
|
||||||
RoundUp(size + sizeof(HeapObjectHeader), kAllocationGranularity);
|
RoundUp(size + sizeof(HeapObjectHeader), kAllocationGranularity);
|
||||||
const RawHeap::SpaceType type = GetSpaceIndexForSize(allocation_size);
|
const RawHeap::SpaceType type = GetSpaceIndexForSize(allocation_size);
|
||||||
return AllocateObjectOnSpace(
|
return AllocateObjectOnSpace(NormalPageSpace::From(raw_heap_->Space(type)),
|
||||||
static_cast<NormalPageSpace*>(raw_heap_->Space(type)), allocation_size,
|
allocation_size, gcinfo);
|
||||||
gcinfo);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
|
@ -46,8 +46,8 @@ void* ObjectAllocator::OutOfLineAllocate(NormalPageSpace* space, size_t size,
|
|||||||
|
|
||||||
// 1. If this allocation is big enough, allocate a large object.
|
// 1. If this allocation is big enough, allocate a large object.
|
||||||
if (size >= kLargeObjectSizeThreshold) {
|
if (size >= kLargeObjectSizeThreshold) {
|
||||||
auto* large_space = static_cast<LargePageSpace*>(
|
auto* large_space =
|
||||||
raw_heap_->Space(RawHeap::SpaceType::kLarge));
|
LargePageSpace::From(raw_heap_->Space(RawHeap::SpaceType::kLarge));
|
||||||
return AllocateLargeObject(raw_heap_, large_space, size, gcinfo);
|
return AllocateLargeObject(raw_heap_, large_space, size, gcinfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,7 +84,7 @@ void* ObjectAllocator::AllocateFromFreeList(NormalPageSpace* space, size_t size,
|
|||||||
space->free_list().Add({current_lab.start(), current_lab.size()});
|
space->free_list().Add({current_lab.start(), current_lab.size()});
|
||||||
}
|
}
|
||||||
|
|
||||||
current_lab.Set(entry.address, entry.size);
|
current_lab.Set(static_cast<Address>(entry.address), entry.size);
|
||||||
return AllocateObjectOnSpace(space, size, gcinfo);
|
return AllocateObjectOnSpace(space, size, gcinfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,7 +3,13 @@
|
|||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
#include "src/heap/cppgc/heap.h"
|
#include "src/heap/cppgc/heap.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <iterator>
|
||||||
|
#include <numeric>
|
||||||
|
|
||||||
#include "include/cppgc/allocation.h"
|
#include "include/cppgc/allocation.h"
|
||||||
|
#include "src/heap/cppgc/globals.h"
|
||||||
#include "test/unittests/heap/cppgc/tests.h"
|
#include "test/unittests/heap/cppgc/tests.h"
|
||||||
#include "testing/gtest/include/gtest/gtest.h"
|
#include "testing/gtest/include/gtest/gtest.h"
|
||||||
|
|
||||||
@ -34,6 +40,13 @@ class Foo : public GarbageCollected<Foo> {
|
|||||||
|
|
||||||
size_t Foo::destructor_callcount;
|
size_t Foo::destructor_callcount;
|
||||||
|
|
||||||
|
template <size_t Size>
|
||||||
|
class GCed : public GarbageCollected<Foo> {
|
||||||
|
public:
|
||||||
|
void Visit(cppgc::Visitor*) {}
|
||||||
|
char buf[Size];
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
TEST_F(GCHeapTest, PreciseGCReclaimsObjectOnStack) {
|
TEST_F(GCHeapTest, PreciseGCReclaimsObjectOnStack) {
|
||||||
@ -57,7 +70,7 @@ const void* ConservativeGCReturningObject(cppgc::Heap* heap,
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
TEST_F(GCHeapTest, ConservaitveGCRetainsObjectOnStack) {
|
TEST_F(GCHeapTest, ConservativeGCRetainsObjectOnStack) {
|
||||||
Foo* volatile object = MakeGarbageCollected<Foo>(GetHeap());
|
Foo* volatile object = MakeGarbageCollected<Foo>(GetHeap());
|
||||||
EXPECT_EQ(0u, Foo::destructor_callcount);
|
EXPECT_EQ(0u, Foo::destructor_callcount);
|
||||||
EXPECT_EQ(object, ConservativeGCReturningObject(GetHeap(), object));
|
EXPECT_EQ(object, ConservativeGCReturningObject(GetHeap(), object));
|
||||||
@ -68,5 +81,35 @@ TEST_F(GCHeapTest, ConservaitveGCRetainsObjectOnStack) {
|
|||||||
EXPECT_EQ(1u, Foo::destructor_callcount);
|
EXPECT_EQ(1u, Foo::destructor_callcount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(GCHeapTest, ObjectPayloadSize) {
|
||||||
|
static constexpr size_t kNumberOfObjectsPerArena = 16;
|
||||||
|
static constexpr size_t kObjectSizes[] = {1, 32, 64, 128,
|
||||||
|
2 * kLargeObjectSizeThreshold};
|
||||||
|
|
||||||
|
Heap::From(GetHeap())->CollectGarbage();
|
||||||
|
|
||||||
|
for (size_t k = 0; k < kNumberOfObjectsPerArena; ++k) {
|
||||||
|
MakeGarbageCollected<GCed<kObjectSizes[0]>>(GetHeap());
|
||||||
|
MakeGarbageCollected<GCed<kObjectSizes[1]>>(GetHeap());
|
||||||
|
MakeGarbageCollected<GCed<kObjectSizes[2]>>(GetHeap());
|
||||||
|
MakeGarbageCollected<GCed<kObjectSizes[3]>>(GetHeap());
|
||||||
|
MakeGarbageCollected<GCed<kObjectSizes[4]>>(GetHeap());
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t aligned_object_sizes[arraysize(kObjectSizes)];
|
||||||
|
std::transform(std::cbegin(kObjectSizes), std::cend(kObjectSizes),
|
||||||
|
std::begin(aligned_object_sizes), [](size_t size) {
|
||||||
|
return RoundUp(size, kAllocationGranularity);
|
||||||
|
});
|
||||||
|
const size_t expected_size = std::accumulate(
|
||||||
|
std::cbegin(aligned_object_sizes), std::cend(aligned_object_sizes), 0u,
|
||||||
|
[](size_t acc, size_t size) {
|
||||||
|
return acc + kNumberOfObjectsPerArena * size;
|
||||||
|
});
|
||||||
|
// TODO(chromium:1056170): Change to EXPECT_EQ when proper sweeping is
|
||||||
|
// implemented.
|
||||||
|
EXPECT_LE(expected_size, Heap::From(GetHeap())->ObjectPayloadSize());
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
} // namespace cppgc
|
} // namespace cppgc
|
||||||
|
Loading…
Reference in New Issue
Block a user