cppgc: Support ephemeron tracing
Cppgc exposes EphemeronPair that contains a WeakMember key and a Member value and can be used to denote ephemeron semantics in the standalone library. Tracing EphemeronPairs goes through TraceEphemeron that is exposed on the api for the blink usecase. Bug: chromium:1056170 Change-Id: I9fbaa284fa2034248cdf36ea8b0cd5be6a55f676 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2467842 Commit-Queue: Omer Katz <omerkatz@chromium.org> Reviewed-by: Michael Lippautz <mlippautz@chromium.org> Cr-Commit-Position: refs/heads/master@{#70525}
This commit is contained in:
parent
58b4f729a1
commit
718fbb89ef
1
BUILD.gn
1
BUILD.gn
@ -4338,6 +4338,7 @@ v8_source_set("cppgc_base") {
|
||||
"include/cppgc/common.h",
|
||||
"include/cppgc/custom-space.h",
|
||||
"include/cppgc/default-platform.h",
|
||||
"include/cppgc/ephemeron-pair.h",
|
||||
"include/cppgc/garbage-collected.h",
|
||||
"include/cppgc/heap.h",
|
||||
"include/cppgc/internal/api-constants.h",
|
||||
|
25
include/cppgc/ephemeron-pair.h
Normal file
25
include/cppgc/ephemeron-pair.h
Normal file
@ -0,0 +1,25 @@
|
||||
// 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 INCLUDE_CPPGC_EPHEMERON_PAIR_H_
|
||||
#define INCLUDE_CPPGC_EPHEMERON_PAIR_H_
|
||||
|
||||
#include "cppgc/member.h"
|
||||
|
||||
namespace cppgc {
|
||||
|
||||
/**
|
||||
* An ephemeron pair is used to conditionally retain an object.
|
||||
* The |value| will be kept alive only if the |key| is alive.
|
||||
*/
|
||||
template <typename K, typename V>
|
||||
struct EphemeronPair {
|
||||
EphemeronPair(K* k, V* v) : key(k), value(v) {}
|
||||
WeakMember<K> key;
|
||||
Member<V> value;
|
||||
};
|
||||
|
||||
} // namespace cppgc
|
||||
|
||||
#endif // INCLUDE_CPPGC_EPHEMERON_PAIR_H_
|
@ -5,6 +5,7 @@
|
||||
#ifndef INCLUDE_CPPGC_VISITOR_H_
|
||||
#define INCLUDE_CPPGC_VISITOR_H_
|
||||
|
||||
#include "cppgc/ephemeron-pair.h"
|
||||
#include "cppgc/garbage-collected.h"
|
||||
#include "cppgc/internal/logging.h"
|
||||
#include "cppgc/internal/pointer-policies.h"
|
||||
@ -123,6 +124,30 @@ class V8_EXPORT Visitor {
|
||||
RegisterWeakCallback(&WeakCallbackMethodDelegate<T, method>, object);
|
||||
}
|
||||
|
||||
/**
|
||||
* Trace method for EphemeronPair.
|
||||
*
|
||||
* \param ephemeron_pair EphemeronPair reference weakly retaining a key object
|
||||
* and strongly retaining a value object in case the key object is alive.
|
||||
*/
|
||||
template <typename K, typename V>
|
||||
void Trace(const EphemeronPair<K, V>& ephemeron_pair) {
|
||||
TraceEphemeron(ephemeron_pair.key, ephemeron_pair.value.GetRawAtomic());
|
||||
}
|
||||
|
||||
/**
|
||||
* Trace method for ephemerons. Used for tracing raw ephemeron in which the
|
||||
* key and value are kept separately.
|
||||
*
|
||||
* \param key WeakMember reference weakly retaining a key object.
|
||||
* \param value Member reference weakly retaining a value object.
|
||||
*/
|
||||
template <typename K, typename V>
|
||||
void TraceEphemeron(const WeakMember<K>& key, const V* value) {
|
||||
TraceDescriptor value_desc = TraceTrait<V>::GetTraceDescriptor(value);
|
||||
VisitEphemeron(key, value_desc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a weak callback that is invoked during garbage collection.
|
||||
*
|
||||
@ -157,6 +182,7 @@ class V8_EXPORT Visitor {
|
||||
virtual void VisitRoot(const void*, TraceDescriptor, const SourceLocation&) {}
|
||||
virtual void VisitWeakRoot(const void* self, TraceDescriptor, WeakCallback,
|
||||
const void* weak_root, const SourceLocation&) {}
|
||||
virtual void VisitEphemeron(const void* key, TraceDescriptor value_desc) {}
|
||||
|
||||
private:
|
||||
template <typename T, void (T::*method)(const LivenessBroker&)>
|
||||
|
@ -32,6 +32,11 @@ void UnifiedHeapMarkingVisitorBase::VisitWeak(const void* object,
|
||||
weak_member);
|
||||
}
|
||||
|
||||
void UnifiedHeapMarkingVisitorBase::VisitEphemeron(const void* key,
|
||||
TraceDescriptor value_desc) {
|
||||
marking_state_.ProcessEphemeron(key, value_desc);
|
||||
}
|
||||
|
||||
void UnifiedHeapMarkingVisitorBase::RegisterWeakCallback(WeakCallback callback,
|
||||
const void* object) {
|
||||
marking_state_.RegisterWeakCallback(callback, object);
|
||||
|
@ -43,6 +43,7 @@ class V8_EXPORT_PRIVATE UnifiedHeapMarkingVisitorBase : public JSVisitor {
|
||||
// C++ handling.
|
||||
void Visit(const void*, TraceDescriptor) final;
|
||||
void VisitWeak(const void*, TraceDescriptor, WeakCallback, const void*) final;
|
||||
void VisitEphemeron(const void*, TraceDescriptor) final;
|
||||
void RegisterWeakCallback(WeakCallback, const void*) final;
|
||||
|
||||
// JS handling.
|
||||
|
@ -145,6 +145,18 @@ void ConcurrentMarkingTask::ProcessWorklists(
|
||||
})) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!DrainWorklistWithYielding(
|
||||
job_delegate, concurrent_marking_state,
|
||||
concurrent_marker_.incremental_marking_schedule(),
|
||||
concurrent_marking_state.ephemeron_pairs_for_processing_worklist(),
|
||||
[&concurrent_marking_state](
|
||||
const MarkingWorklists::EphemeronPairItem& item) {
|
||||
concurrent_marking_state.ProcessEphemeron(item.key,
|
||||
item.value_desc);
|
||||
})) {
|
||||
return;
|
||||
}
|
||||
} while (
|
||||
!concurrent_marking_state.marking_worklist().IsLocalAndGlobalEmpty());
|
||||
}
|
||||
|
@ -11,6 +11,9 @@
|
||||
namespace cppgc {
|
||||
namespace internal {
|
||||
|
||||
// static
|
||||
constexpr size_t IncrementalMarkingSchedule::kInvalidLastEstimatedLiveBytes;
|
||||
|
||||
const double IncrementalMarkingSchedule::kEstimatedMarkingTimeMs = 500.0;
|
||||
const size_t IncrementalMarkingSchedule::kMinimumMarkedBytesPerIncrementalStep =
|
||||
64 * kKB;
|
||||
@ -52,6 +55,7 @@ double IncrementalMarkingSchedule::GetElapsedTimeInMs(
|
||||
|
||||
size_t IncrementalMarkingSchedule::GetNextIncrementalStepDuration(
|
||||
size_t estimated_live_bytes) {
|
||||
last_estimated_live_bytes_ = estimated_live_bytes;
|
||||
DCHECK(!incremental_marking_start_time_.IsNull());
|
||||
double elapsed_time_in_ms =
|
||||
GetElapsedTimeInMs(incremental_marking_start_time_);
|
||||
@ -73,5 +77,17 @@ size_t IncrementalMarkingSchedule::GetNextIncrementalStepDuration(
|
||||
expected_marked_bytes - actual_marked_bytes);
|
||||
}
|
||||
|
||||
constexpr double
|
||||
IncrementalMarkingSchedule::kEphemeronPairsFlushingRatioIncrements;
|
||||
bool IncrementalMarkingSchedule::ShouldFlushEphemeronPairs() {
|
||||
DCHECK_NE(kInvalidLastEstimatedLiveBytes, last_estimated_live_bytes_);
|
||||
if (GetOverallMarkedBytes() <
|
||||
(ephemeron_pairs_flushing_ratio_target * last_estimated_live_bytes_))
|
||||
return false;
|
||||
ephemeron_pairs_flushing_ratio_target +=
|
||||
kEphemeronPairsFlushingRatioIncrements;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace cppgc
|
||||
|
@ -35,6 +35,8 @@ class V8_EXPORT_PRIVATE IncrementalMarkingSchedule {
|
||||
elapsed_time_for_testing_ = elapsed_time;
|
||||
}
|
||||
|
||||
bool ShouldFlushEphemeronPairs();
|
||||
|
||||
private:
|
||||
double GetElapsedTimeInMs(v8::base::TimeTicks);
|
||||
|
||||
@ -46,6 +48,11 @@ class V8_EXPORT_PRIVATE IncrementalMarkingSchedule {
|
||||
// Using -1 as sentinel to denote
|
||||
static constexpr double kNoSetElapsedTimeForTesting = -1;
|
||||
double elapsed_time_for_testing_ = kNoSetElapsedTimeForTesting;
|
||||
|
||||
static constexpr size_t kInvalidLastEstimatedLiveBytes = -1;
|
||||
size_t last_estimated_live_bytes_ = kInvalidLastEstimatedLiveBytes;
|
||||
double ephemeron_pairs_flushing_ratio_target = 0.25;
|
||||
static constexpr double kEphemeronPairsFlushingRatioIncrements = 0.25;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
@ -173,6 +173,20 @@ MarkerBase::~MarkerBase() {
|
||||
for (HeapObjectHeader* object : objects) DCHECK(object->IsMarked());
|
||||
#else
|
||||
marking_worklists_.not_fully_constructed_worklist()->Clear();
|
||||
#endif
|
||||
}
|
||||
|
||||
// |discovered_ephemeron_pairs_worklist_| may still hold ephemeron pairs with
|
||||
// dead keys.
|
||||
if (!marking_worklists_.discovered_ephemeron_pairs_worklist()->IsEmpty()) {
|
||||
#if DEBUG
|
||||
MarkingWorklists::EphemeronPairItem item;
|
||||
while (mutator_marking_state_.discovered_ephemeron_pairs_worklist().Pop(
|
||||
&item)) {
|
||||
DCHECK(!HeapObjectHeader::FromPayload(item.key).IsMarked());
|
||||
}
|
||||
#else
|
||||
marking_worklists_.discovered_ephemeron_pairs_worklist()->Clear();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@ -215,6 +229,7 @@ void MarkerBase::EnterAtomicPause(MarkingConfig::StackState stack_state) {
|
||||
VisitRoots(config_.stack_state);
|
||||
if (config_.stack_state == MarkingConfig::StackState::kNoHeapPointers) {
|
||||
mutator_marking_state_.FlushNotFullyConstructedObjects();
|
||||
DCHECK(marking_worklists_.not_fully_constructed_worklist()->IsEmpty());
|
||||
} else {
|
||||
MarkNotFullyConstructedObjects();
|
||||
}
|
||||
@ -318,8 +333,9 @@ bool MarkerBase::AdvanceMarkingWithDeadline(v8::base::TimeDelta max_duration) {
|
||||
is_done = ProcessWorklistsWithDeadline(
|
||||
mutator_marking_state_.marked_bytes() + step_size_in_bytes,
|
||||
v8::base::TimeTicks::Now() + max_duration);
|
||||
schedule_.UpdateIncrementalMarkedBytes(
|
||||
mutator_marking_state_.marked_bytes());
|
||||
}
|
||||
schedule_.UpdateIncrementalMarkedBytes(mutator_marking_state_.marked_bytes());
|
||||
mutator_marking_state_.Publish();
|
||||
if (!is_done) {
|
||||
// If marking is atomic, |is_done| should always be true.
|
||||
@ -336,6 +352,11 @@ bool MarkerBase::AdvanceMarkingWithDeadline(v8::base::TimeDelta max_duration) {
|
||||
bool MarkerBase::ProcessWorklistsWithDeadline(
|
||||
size_t marked_bytes_deadline, v8::base::TimeTicks time_deadline) {
|
||||
do {
|
||||
if ((config_.marking_type == MarkingConfig::MarkingType::kAtomic) ||
|
||||
schedule_.ShouldFlushEphemeronPairs()) {
|
||||
mutator_marking_state_.FlushDiscoveredEphemeronPairs();
|
||||
}
|
||||
|
||||
// Bailout objects may be complicated to trace and thus might take longer
|
||||
// than other objects. Therefore we reduce the interval between deadline
|
||||
// checks to guarantee the deadline is not exceeded.
|
||||
@ -387,6 +408,16 @@ bool MarkerBase::ProcessWorklistsWithDeadline(
|
||||
})) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!DrainWorklistWithBytesAndTimeDeadline(
|
||||
mutator_marking_state_, marked_bytes_deadline, time_deadline,
|
||||
mutator_marking_state_.ephemeron_pairs_for_processing_worklist(),
|
||||
[this](const MarkingWorklists::EphemeronPairItem& item) {
|
||||
mutator_marking_state_.ProcessEphemeron(item.key,
|
||||
item.value_desc);
|
||||
})) {
|
||||
return false;
|
||||
}
|
||||
} while (!mutator_marking_state_.marking_worklist().IsLocalAndGlobalEmpty());
|
||||
return true;
|
||||
}
|
||||
|
@ -16,7 +16,14 @@ void MutatorMarkingState::FlushNotFullyConstructedObjects() {
|
||||
if (MarkNoPush(*object))
|
||||
previously_not_fully_constructed_worklist_.Push(object);
|
||||
}
|
||||
DCHECK(not_fully_constructed_worklist_.IsEmpty());
|
||||
}
|
||||
|
||||
void MutatorMarkingState::FlushDiscoveredEphemeronPairs() {
|
||||
discovered_ephemeron_pairs_worklist_.Publish();
|
||||
if (!discovered_ephemeron_pairs_worklist_.IsGlobalEmpty()) {
|
||||
ephemeron_pairs_for_processing_worklist_.Merge(
|
||||
&discovered_ephemeron_pairs_worklist_);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
@ -30,6 +30,8 @@ class MarkingStateBase {
|
||||
WeakCallback, const void*);
|
||||
inline void RegisterWeakCallback(WeakCallback, const void*);
|
||||
|
||||
inline void ProcessEphemeron(const void*, TraceDescriptor);
|
||||
|
||||
inline void AccountMarkedBytes(const HeapObjectHeader&);
|
||||
inline void AccountMarkedBytes(size_t);
|
||||
size_t marked_bytes() const { return marked_bytes_; }
|
||||
@ -40,6 +42,8 @@ class MarkingStateBase {
|
||||
weak_callback_worklist_.Publish();
|
||||
write_barrier_worklist_.Publish();
|
||||
concurrent_marking_bailout_worklist_.Publish();
|
||||
discovered_ephemeron_pairs_worklist_.Publish();
|
||||
ephemeron_pairs_for_processing_worklist_.Publish();
|
||||
}
|
||||
|
||||
MarkingWorklists::MarkingWorklist::Local& marking_worklist() {
|
||||
@ -63,6 +67,14 @@ class MarkingStateBase {
|
||||
concurrent_marking_bailout_worklist() {
|
||||
return concurrent_marking_bailout_worklist_;
|
||||
}
|
||||
MarkingWorklists::EphemeronPairsWorklist::Local&
|
||||
discovered_ephemeron_pairs_worklist() {
|
||||
return discovered_ephemeron_pairs_worklist_;
|
||||
}
|
||||
MarkingWorklists::EphemeronPairsWorklist::Local&
|
||||
ephemeron_pairs_for_processing_worklist() {
|
||||
return ephemeron_pairs_for_processing_worklist_;
|
||||
}
|
||||
|
||||
protected:
|
||||
inline void MarkAndPush(HeapObjectHeader&, TraceDescriptor);
|
||||
@ -82,6 +94,10 @@ class MarkingStateBase {
|
||||
MarkingWorklists::WriteBarrierWorklist::Local write_barrier_worklist_;
|
||||
MarkingWorklists::ConcurrentMarkingBailoutWorklist::Local
|
||||
concurrent_marking_bailout_worklist_;
|
||||
MarkingWorklists::EphemeronPairsWorklist::Local
|
||||
discovered_ephemeron_pairs_worklist_;
|
||||
MarkingWorklists::EphemeronPairsWorklist::Local
|
||||
ephemeron_pairs_for_processing_worklist_;
|
||||
|
||||
size_t marked_bytes_ = 0;
|
||||
};
|
||||
@ -100,7 +116,11 @@ MarkingStateBase::MarkingStateBase(HeapBase& heap,
|
||||
weak_callback_worklist_(marking_worklists.weak_callback_worklist()),
|
||||
write_barrier_worklist_(marking_worklists.write_barrier_worklist()),
|
||||
concurrent_marking_bailout_worklist_(
|
||||
marking_worklists.concurrent_marking_bailout_worklist()) {
|
||||
marking_worklists.concurrent_marking_bailout_worklist()),
|
||||
discovered_ephemeron_pairs_worklist_(
|
||||
marking_worklists.discovered_ephemeron_pairs_worklist()),
|
||||
ephemeron_pairs_for_processing_worklist_(
|
||||
marking_worklists.ephemeron_pairs_for_processing_worklist()) {
|
||||
}
|
||||
|
||||
void MarkingStateBase::MarkAndPush(const void* object, TraceDescriptor desc) {
|
||||
@ -150,6 +170,24 @@ void MarkingStateBase::RegisterWeakReferenceIfNeeded(const void* object,
|
||||
RegisterWeakCallback(weak_callback, parameter);
|
||||
}
|
||||
|
||||
void MarkingStateBase::RegisterWeakCallback(WeakCallback callback,
|
||||
const void* object) {
|
||||
weak_callback_worklist_.Push({callback, object});
|
||||
}
|
||||
|
||||
void MarkingStateBase::ProcessEphemeron(const void* key,
|
||||
TraceDescriptor value_desc) {
|
||||
// Filter out already marked keys. The write barrier for WeakMember
|
||||
// ensures that any newly set value after this point is kept alive and does
|
||||
// not require the callback.
|
||||
if (HeapObjectHeader::FromPayload(key)
|
||||
.IsMarked<HeapObjectHeader::AccessMode::kAtomic>()) {
|
||||
MarkAndPush(value_desc.base_object_payload, value_desc);
|
||||
return;
|
||||
}
|
||||
discovered_ephemeron_pairs_worklist_.Push({key, value_desc});
|
||||
}
|
||||
|
||||
void MarkingStateBase::AccountMarkedBytes(const HeapObjectHeader& header) {
|
||||
AccountMarkedBytes(
|
||||
header.IsLargeObject<HeapObjectHeader::AccessMode::kAtomic>()
|
||||
@ -177,6 +215,10 @@ class MutatorMarkingState : public MarkingStateBase {
|
||||
// previously_not_full_constructed_worklists_.
|
||||
void FlushNotFullyConstructedObjects();
|
||||
|
||||
// Moves ephemeron pairs in discovered_ephemeron_pairs_worklist_ to
|
||||
// ephemeron_pairs_for_processing_worklist_.
|
||||
void FlushDiscoveredEphemeronPairs();
|
||||
|
||||
inline void InvokeWeakRootsCallbackIfNeeded(const void*, TraceDescriptor,
|
||||
WeakCallback, const void*);
|
||||
};
|
||||
@ -206,11 +248,6 @@ void MutatorMarkingState::InvokeWeakRootsCallbackIfNeeded(
|
||||
weak_callback(LivenessBrokerFactory::Create(), parameter);
|
||||
}
|
||||
|
||||
void MarkingStateBase::RegisterWeakCallback(WeakCallback callback,
|
||||
const void* object) {
|
||||
weak_callback_worklist_.Push({callback, object});
|
||||
}
|
||||
|
||||
class ConcurrentMarkingState : public MarkingStateBase {
|
||||
public:
|
||||
ConcurrentMarkingState(HeapBase& heap, MarkingWorklists& marking_worklists)
|
||||
|
@ -25,6 +25,11 @@ void MarkingVisitorBase::VisitWeak(const void* object, TraceDescriptor desc,
|
||||
weak_member);
|
||||
}
|
||||
|
||||
void MarkingVisitorBase::VisitEphemeron(const void* key,
|
||||
TraceDescriptor value_desc) {
|
||||
marking_state_.ProcessEphemeron(key, value_desc);
|
||||
}
|
||||
|
||||
void MarkingVisitorBase::RegisterWeakCallback(WeakCallback callback,
|
||||
const void* object) {
|
||||
marking_state_.RegisterWeakCallback(callback, object);
|
||||
|
@ -28,6 +28,7 @@ class V8_EXPORT_PRIVATE MarkingVisitorBase : public VisitorBase {
|
||||
protected:
|
||||
void Visit(const void*, TraceDescriptor) final;
|
||||
void VisitWeak(const void*, TraceDescriptor, WeakCallback, const void*) final;
|
||||
void VisitEphemeron(const void*, TraceDescriptor) final;
|
||||
void RegisterWeakCallback(WeakCallback, const void*) final;
|
||||
|
||||
MarkingStateBase& marking_state_;
|
||||
|
@ -17,6 +17,8 @@ void MarkingWorklists::ClearForTesting() {
|
||||
write_barrier_worklist_.Clear();
|
||||
weak_callback_worklist_.Clear();
|
||||
concurrent_marking_bailout_worklist_.Clear();
|
||||
discovered_ephemeron_pairs_worklist_.Clear();
|
||||
ephemeron_pairs_for_processing_worklist_.Clear();
|
||||
}
|
||||
|
||||
void MarkingWorklists::NotFullyConstructedWorklist::Push(
|
||||
|
@ -33,6 +33,11 @@ class MarkingWorklists {
|
||||
size_t bailedout_size;
|
||||
};
|
||||
|
||||
struct EphemeronPairItem {
|
||||
const void* key;
|
||||
TraceDescriptor value_desc;
|
||||
};
|
||||
|
||||
// Segment size of 512 entries necessary to avoid throughput regressions.
|
||||
// Since the work list is currently a temporary object this is not a problem.
|
||||
using MarkingWorklist =
|
||||
@ -46,6 +51,8 @@ class MarkingWorklists {
|
||||
using ConcurrentMarkingBailoutWorklist =
|
||||
heap::base::Worklist<ConcurrentMarkingBailoutItem,
|
||||
64 /* local entries */>;
|
||||
using EphemeronPairsWorklist =
|
||||
heap::base::Worklist<EphemeronPairItem, 64 /* local entries */>;
|
||||
|
||||
class V8_EXPORT_PRIVATE NotFullyConstructedWorklist {
|
||||
public:
|
||||
@ -85,6 +92,12 @@ class MarkingWorklists {
|
||||
ConcurrentMarkingBailoutWorklist* concurrent_marking_bailout_worklist() {
|
||||
return &concurrent_marking_bailout_worklist_;
|
||||
}
|
||||
EphemeronPairsWorklist* discovered_ephemeron_pairs_worklist() {
|
||||
return &discovered_ephemeron_pairs_worklist_;
|
||||
}
|
||||
EphemeronPairsWorklist* ephemeron_pairs_for_processing_worklist() {
|
||||
return &ephemeron_pairs_for_processing_worklist_;
|
||||
}
|
||||
|
||||
void ClearForTesting();
|
||||
|
||||
@ -96,6 +109,8 @@ class MarkingWorklists {
|
||||
WriteBarrierWorklist write_barrier_worklist_;
|
||||
WeakCallbackWorklist weak_callback_worklist_;
|
||||
ConcurrentMarkingBailoutWorklist concurrent_marking_bailout_worklist_;
|
||||
EphemeronPairsWorklist discovered_ephemeron_pairs_worklist_;
|
||||
EphemeronPairsWorklist ephemeron_pairs_for_processing_worklist_;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
@ -84,6 +84,7 @@ v8_source_set("cppgc_unittests_sources") {
|
||||
"heap/cppgc/concurrent-sweeper-unittest.cc",
|
||||
"heap/cppgc/cross-thread-persistent-unittest.cc",
|
||||
"heap/cppgc/custom-spaces-unittest.cc",
|
||||
"heap/cppgc/ephemeron-pair-unittest.cc",
|
||||
"heap/cppgc/finalizer-trait-unittest.cc",
|
||||
"heap/cppgc/free-list-unittest.cc",
|
||||
"heap/cppgc/garbage-collected-unittest.cc",
|
||||
|
112
test/unittests/heap/cppgc/ephemeron-pair-unittest.cc
Normal file
112
test/unittests/heap/cppgc/ephemeron-pair-unittest.cc
Normal file
@ -0,0 +1,112 @@
|
||||
// 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.
|
||||
|
||||
#include "include/cppgc/ephemeron-pair.h"
|
||||
|
||||
#include "include/cppgc/allocation.h"
|
||||
#include "include/cppgc/persistent.h"
|
||||
#include "src/heap/cppgc/heap-object-header.h"
|
||||
#include "src/heap/cppgc/marking-visitor.h"
|
||||
#include "src/heap/cppgc/stats-collector.h"
|
||||
#include "test/unittests/heap/cppgc/tests.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
|
||||
namespace cppgc {
|
||||
namespace internal {
|
||||
|
||||
namespace {
|
||||
class GCed : public GarbageCollected<GCed> {
|
||||
public:
|
||||
void Trace(cppgc::Visitor*) const {}
|
||||
};
|
||||
|
||||
class EphemeronHolder : public GarbageCollected<GCed> {
|
||||
public:
|
||||
EphemeronHolder(GCed* key, GCed* value) : ephemeron_pair_(key, value) {}
|
||||
void Trace(cppgc::Visitor* visitor) const { visitor->Trace(ephemeron_pair_); }
|
||||
|
||||
private:
|
||||
EphemeronPair<GCed, GCed> ephemeron_pair_;
|
||||
};
|
||||
|
||||
class EhpemeronPairTest : public testing::TestWithHeap {
|
||||
using MarkingConfig = Marker::MarkingConfig;
|
||||
|
||||
static constexpr Marker::MarkingConfig IncrementalPreciseMarkingConfig = {
|
||||
MarkingConfig::CollectionType::kMajor,
|
||||
MarkingConfig::StackState::kNoHeapPointers,
|
||||
MarkingConfig::MarkingType::kIncremental};
|
||||
|
||||
public:
|
||||
void FinishSteps() {
|
||||
while (!SingleStep()) {
|
||||
}
|
||||
}
|
||||
|
||||
void FinishMarking() {
|
||||
marker_->FinishMarking(MarkingConfig::StackState::kNoHeapPointers);
|
||||
// Pretend do finish sweeping as StatsCollector verifies that Notify*
|
||||
// methods are called in the right order.
|
||||
Heap::From(GetHeap())->stats_collector()->NotifySweepingCompleted();
|
||||
}
|
||||
|
||||
void InitializeMarker(HeapBase& heap, cppgc::Platform* platform) {
|
||||
marker_ = MarkerFactory::CreateAndStartMarking<Marker>(
|
||||
heap, platform, IncrementalPreciseMarkingConfig);
|
||||
}
|
||||
|
||||
Marker* marker() const { return marker_.get(); }
|
||||
|
||||
private:
|
||||
bool SingleStep() {
|
||||
return marker_->IncrementalMarkingStepForTesting(
|
||||
MarkingConfig::StackState::kNoHeapPointers);
|
||||
}
|
||||
|
||||
std::unique_ptr<Marker> marker_;
|
||||
};
|
||||
|
||||
// static
|
||||
constexpr Marker::MarkingConfig
|
||||
EhpemeronPairTest::IncrementalPreciseMarkingConfig;
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST_F(EhpemeronPairTest, ValueMarkedWhenKeyIsMarked) {
|
||||
GCed* key = MakeGarbageCollected<GCed>(GetAllocationHandle());
|
||||
GCed* value = MakeGarbageCollected<GCed>(GetAllocationHandle());
|
||||
Persistent<EphemeronHolder> holder =
|
||||
MakeGarbageCollected<EphemeronHolder>(GetAllocationHandle(), key, value);
|
||||
HeapObjectHeader::FromPayload(key).TryMarkAtomic();
|
||||
InitializeMarker(*Heap::From(GetHeap()), GetPlatformHandle().get());
|
||||
FinishMarking();
|
||||
EXPECT_TRUE(HeapObjectHeader::FromPayload(value).IsMarked());
|
||||
}
|
||||
|
||||
TEST_F(EhpemeronPairTest, ValueNotMarkedWhenKeyIsNotMarked) {
|
||||
GCed* key = MakeGarbageCollected<GCed>(GetAllocationHandle());
|
||||
GCed* value = MakeGarbageCollected<GCed>(GetAllocationHandle());
|
||||
Persistent<EphemeronHolder> holder =
|
||||
MakeGarbageCollected<EphemeronHolder>(GetAllocationHandle(), key, value);
|
||||
InitializeMarker(*Heap::From(GetHeap()), GetPlatformHandle().get());
|
||||
FinishMarking();
|
||||
EXPECT_FALSE(HeapObjectHeader::FromPayload(key).IsMarked());
|
||||
EXPECT_FALSE(HeapObjectHeader::FromPayload(value).IsMarked());
|
||||
}
|
||||
|
||||
TEST_F(EhpemeronPairTest, ValueNotMarkedBeforeKey) {
|
||||
GCed* key = MakeGarbageCollected<GCed>(GetAllocationHandle());
|
||||
GCed* value = MakeGarbageCollected<GCed>(GetAllocationHandle());
|
||||
Persistent<EphemeronHolder> holder =
|
||||
MakeGarbageCollected<EphemeronHolder>(GetAllocationHandle(), key, value);
|
||||
InitializeMarker(*Heap::From(GetHeap()), GetPlatformHandle().get());
|
||||
FinishSteps();
|
||||
EXPECT_FALSE(HeapObjectHeader::FromPayload(value).IsMarked());
|
||||
HeapObjectHeader::FromPayload(key).TryMarkAtomic();
|
||||
FinishMarking();
|
||||
EXPECT_TRUE(HeapObjectHeader::FromPayload(value).IsMarked());
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace cppgc
|
Loading…
Reference in New Issue
Block a user