Commit 718fbb89 authored by Omer Katz's avatar Omer Katz Committed by Commit Bot

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: 's avatarMichael Lippautz <mlippautz@chromium.org>
Cr-Commit-Position: refs/heads/master@{#70525}
parent 58b4f729
...@@ -4338,6 +4338,7 @@ v8_source_set("cppgc_base") { ...@@ -4338,6 +4338,7 @@ v8_source_set("cppgc_base") {
"include/cppgc/common.h", "include/cppgc/common.h",
"include/cppgc/custom-space.h", "include/cppgc/custom-space.h",
"include/cppgc/default-platform.h", "include/cppgc/default-platform.h",
"include/cppgc/ephemeron-pair.h",
"include/cppgc/garbage-collected.h", "include/cppgc/garbage-collected.h",
"include/cppgc/heap.h", "include/cppgc/heap.h",
"include/cppgc/internal/api-constants.h", "include/cppgc/internal/api-constants.h",
......
// 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 @@ ...@@ -5,6 +5,7 @@
#ifndef INCLUDE_CPPGC_VISITOR_H_ #ifndef INCLUDE_CPPGC_VISITOR_H_
#define INCLUDE_CPPGC_VISITOR_H_ #define INCLUDE_CPPGC_VISITOR_H_
#include "cppgc/ephemeron-pair.h"
#include "cppgc/garbage-collected.h" #include "cppgc/garbage-collected.h"
#include "cppgc/internal/logging.h" #include "cppgc/internal/logging.h"
#include "cppgc/internal/pointer-policies.h" #include "cppgc/internal/pointer-policies.h"
...@@ -123,6 +124,30 @@ class V8_EXPORT Visitor { ...@@ -123,6 +124,30 @@ class V8_EXPORT Visitor {
RegisterWeakCallback(&WeakCallbackMethodDelegate<T, method>, object); 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. * Registers a weak callback that is invoked during garbage collection.
* *
...@@ -157,6 +182,7 @@ class V8_EXPORT Visitor { ...@@ -157,6 +182,7 @@ class V8_EXPORT Visitor {
virtual void VisitRoot(const void*, TraceDescriptor, const SourceLocation&) {} virtual void VisitRoot(const void*, TraceDescriptor, const SourceLocation&) {}
virtual void VisitWeakRoot(const void* self, TraceDescriptor, WeakCallback, virtual void VisitWeakRoot(const void* self, TraceDescriptor, WeakCallback,
const void* weak_root, const SourceLocation&) {} const void* weak_root, const SourceLocation&) {}
virtual void VisitEphemeron(const void* key, TraceDescriptor value_desc) {}
private: private:
template <typename T, void (T::*method)(const LivenessBroker&)> template <typename T, void (T::*method)(const LivenessBroker&)>
......
...@@ -32,6 +32,11 @@ void UnifiedHeapMarkingVisitorBase::VisitWeak(const void* object, ...@@ -32,6 +32,11 @@ void UnifiedHeapMarkingVisitorBase::VisitWeak(const void* object,
weak_member); weak_member);
} }
void UnifiedHeapMarkingVisitorBase::VisitEphemeron(const void* key,
TraceDescriptor value_desc) {
marking_state_.ProcessEphemeron(key, value_desc);
}
void UnifiedHeapMarkingVisitorBase::RegisterWeakCallback(WeakCallback callback, void UnifiedHeapMarkingVisitorBase::RegisterWeakCallback(WeakCallback callback,
const void* object) { const void* object) {
marking_state_.RegisterWeakCallback(callback, object); marking_state_.RegisterWeakCallback(callback, object);
......
...@@ -43,6 +43,7 @@ class V8_EXPORT_PRIVATE UnifiedHeapMarkingVisitorBase : public JSVisitor { ...@@ -43,6 +43,7 @@ class V8_EXPORT_PRIVATE UnifiedHeapMarkingVisitorBase : public JSVisitor {
// C++ handling. // C++ handling.
void Visit(const void*, TraceDescriptor) final; void Visit(const void*, TraceDescriptor) final;
void VisitWeak(const void*, TraceDescriptor, WeakCallback, const void*) final; void VisitWeak(const void*, TraceDescriptor, WeakCallback, const void*) final;
void VisitEphemeron(const void*, TraceDescriptor) final;
void RegisterWeakCallback(WeakCallback, const void*) final; void RegisterWeakCallback(WeakCallback, const void*) final;
// JS handling. // JS handling.
......
...@@ -145,6 +145,18 @@ void ConcurrentMarkingTask::ProcessWorklists( ...@@ -145,6 +145,18 @@ void ConcurrentMarkingTask::ProcessWorklists(
})) { })) {
return; 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 ( } while (
!concurrent_marking_state.marking_worklist().IsLocalAndGlobalEmpty()); !concurrent_marking_state.marking_worklist().IsLocalAndGlobalEmpty());
} }
......
...@@ -11,6 +11,9 @@ ...@@ -11,6 +11,9 @@
namespace cppgc { namespace cppgc {
namespace internal { namespace internal {
// static
constexpr size_t IncrementalMarkingSchedule::kInvalidLastEstimatedLiveBytes;
const double IncrementalMarkingSchedule::kEstimatedMarkingTimeMs = 500.0; const double IncrementalMarkingSchedule::kEstimatedMarkingTimeMs = 500.0;
const size_t IncrementalMarkingSchedule::kMinimumMarkedBytesPerIncrementalStep = const size_t IncrementalMarkingSchedule::kMinimumMarkedBytesPerIncrementalStep =
64 * kKB; 64 * kKB;
...@@ -52,6 +55,7 @@ double IncrementalMarkingSchedule::GetElapsedTimeInMs( ...@@ -52,6 +55,7 @@ double IncrementalMarkingSchedule::GetElapsedTimeInMs(
size_t IncrementalMarkingSchedule::GetNextIncrementalStepDuration( size_t IncrementalMarkingSchedule::GetNextIncrementalStepDuration(
size_t estimated_live_bytes) { size_t estimated_live_bytes) {
last_estimated_live_bytes_ = estimated_live_bytes;
DCHECK(!incremental_marking_start_time_.IsNull()); DCHECK(!incremental_marking_start_time_.IsNull());
double elapsed_time_in_ms = double elapsed_time_in_ms =
GetElapsedTimeInMs(incremental_marking_start_time_); GetElapsedTimeInMs(incremental_marking_start_time_);
...@@ -73,5 +77,17 @@ size_t IncrementalMarkingSchedule::GetNextIncrementalStepDuration( ...@@ -73,5 +77,17 @@ size_t IncrementalMarkingSchedule::GetNextIncrementalStepDuration(
expected_marked_bytes - actual_marked_bytes); 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 internal
} // namespace cppgc } // namespace cppgc
...@@ -35,6 +35,8 @@ class V8_EXPORT_PRIVATE IncrementalMarkingSchedule { ...@@ -35,6 +35,8 @@ class V8_EXPORT_PRIVATE IncrementalMarkingSchedule {
elapsed_time_for_testing_ = elapsed_time; elapsed_time_for_testing_ = elapsed_time;
} }
bool ShouldFlushEphemeronPairs();
private: private:
double GetElapsedTimeInMs(v8::base::TimeTicks); double GetElapsedTimeInMs(v8::base::TimeTicks);
...@@ -46,6 +48,11 @@ class V8_EXPORT_PRIVATE IncrementalMarkingSchedule { ...@@ -46,6 +48,11 @@ class V8_EXPORT_PRIVATE IncrementalMarkingSchedule {
// Using -1 as sentinel to denote // Using -1 as sentinel to denote
static constexpr double kNoSetElapsedTimeForTesting = -1; static constexpr double kNoSetElapsedTimeForTesting = -1;
double elapsed_time_for_testing_ = kNoSetElapsedTimeForTesting; 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 } // namespace internal
......
...@@ -173,6 +173,20 @@ MarkerBase::~MarkerBase() { ...@@ -173,6 +173,20 @@ MarkerBase::~MarkerBase() {
for (HeapObjectHeader* object : objects) DCHECK(object->IsMarked()); for (HeapObjectHeader* object : objects) DCHECK(object->IsMarked());
#else #else
marking_worklists_.not_fully_constructed_worklist()->Clear(); 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 #endif
} }
} }
...@@ -215,6 +229,7 @@ void MarkerBase::EnterAtomicPause(MarkingConfig::StackState stack_state) { ...@@ -215,6 +229,7 @@ void MarkerBase::EnterAtomicPause(MarkingConfig::StackState stack_state) {
VisitRoots(config_.stack_state); VisitRoots(config_.stack_state);
if (config_.stack_state == MarkingConfig::StackState::kNoHeapPointers) { if (config_.stack_state == MarkingConfig::StackState::kNoHeapPointers) {
mutator_marking_state_.FlushNotFullyConstructedObjects(); mutator_marking_state_.FlushNotFullyConstructedObjects();
DCHECK(marking_worklists_.not_fully_constructed_worklist()->IsEmpty());
} else { } else {
MarkNotFullyConstructedObjects(); MarkNotFullyConstructedObjects();
} }
...@@ -318,8 +333,9 @@ bool MarkerBase::AdvanceMarkingWithDeadline(v8::base::TimeDelta max_duration) { ...@@ -318,8 +333,9 @@ bool MarkerBase::AdvanceMarkingWithDeadline(v8::base::TimeDelta max_duration) {
is_done = ProcessWorklistsWithDeadline( is_done = ProcessWorklistsWithDeadline(
mutator_marking_state_.marked_bytes() + step_size_in_bytes, mutator_marking_state_.marked_bytes() + step_size_in_bytes,
v8::base::TimeTicks::Now() + max_duration); v8::base::TimeTicks::Now() + max_duration);
schedule_.UpdateIncrementalMarkedBytes(
mutator_marking_state_.marked_bytes());
} }
schedule_.UpdateIncrementalMarkedBytes(mutator_marking_state_.marked_bytes());
mutator_marking_state_.Publish(); mutator_marking_state_.Publish();
if (!is_done) { if (!is_done) {
// If marking is atomic, |is_done| should always be true. // If marking is atomic, |is_done| should always be true.
...@@ -336,6 +352,11 @@ bool MarkerBase::AdvanceMarkingWithDeadline(v8::base::TimeDelta max_duration) { ...@@ -336,6 +352,11 @@ bool MarkerBase::AdvanceMarkingWithDeadline(v8::base::TimeDelta max_duration) {
bool MarkerBase::ProcessWorklistsWithDeadline( bool MarkerBase::ProcessWorklistsWithDeadline(
size_t marked_bytes_deadline, v8::base::TimeTicks time_deadline) { size_t marked_bytes_deadline, v8::base::TimeTicks time_deadline) {
do { 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 // Bailout objects may be complicated to trace and thus might take longer
// than other objects. Therefore we reduce the interval between deadline // than other objects. Therefore we reduce the interval between deadline
// checks to guarantee the deadline is not exceeded. // checks to guarantee the deadline is not exceeded.
...@@ -387,6 +408,16 @@ bool MarkerBase::ProcessWorklistsWithDeadline( ...@@ -387,6 +408,16 @@ bool MarkerBase::ProcessWorklistsWithDeadline(
})) { })) {
return false; 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()); } while (!mutator_marking_state_.marking_worklist().IsLocalAndGlobalEmpty());
return true; return true;
} }
......
...@@ -16,7 +16,14 @@ void MutatorMarkingState::FlushNotFullyConstructedObjects() { ...@@ -16,7 +16,14 @@ void MutatorMarkingState::FlushNotFullyConstructedObjects() {
if (MarkNoPush(*object)) if (MarkNoPush(*object))
previously_not_fully_constructed_worklist_.Push(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 } // namespace internal
......
...@@ -30,6 +30,8 @@ class MarkingStateBase { ...@@ -30,6 +30,8 @@ class MarkingStateBase {
WeakCallback, const void*); WeakCallback, const void*);
inline void RegisterWeakCallback(WeakCallback, const void*); inline void RegisterWeakCallback(WeakCallback, const void*);
inline void ProcessEphemeron(const void*, TraceDescriptor);
inline void AccountMarkedBytes(const HeapObjectHeader&); inline void AccountMarkedBytes(const HeapObjectHeader&);
inline void AccountMarkedBytes(size_t); inline void AccountMarkedBytes(size_t);
size_t marked_bytes() const { return marked_bytes_; } size_t marked_bytes() const { return marked_bytes_; }
...@@ -40,6 +42,8 @@ class MarkingStateBase { ...@@ -40,6 +42,8 @@ class MarkingStateBase {
weak_callback_worklist_.Publish(); weak_callback_worklist_.Publish();
write_barrier_worklist_.Publish(); write_barrier_worklist_.Publish();
concurrent_marking_bailout_worklist_.Publish(); concurrent_marking_bailout_worklist_.Publish();
discovered_ephemeron_pairs_worklist_.Publish();
ephemeron_pairs_for_processing_worklist_.Publish();
} }
MarkingWorklists::MarkingWorklist::Local& marking_worklist() { MarkingWorklists::MarkingWorklist::Local& marking_worklist() {
...@@ -63,6 +67,14 @@ class MarkingStateBase { ...@@ -63,6 +67,14 @@ class MarkingStateBase {
concurrent_marking_bailout_worklist() { concurrent_marking_bailout_worklist() {
return 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: protected:
inline void MarkAndPush(HeapObjectHeader&, TraceDescriptor); inline void MarkAndPush(HeapObjectHeader&, TraceDescriptor);
...@@ -82,6 +94,10 @@ class MarkingStateBase { ...@@ -82,6 +94,10 @@ class MarkingStateBase {
MarkingWorklists::WriteBarrierWorklist::Local write_barrier_worklist_; MarkingWorklists::WriteBarrierWorklist::Local write_barrier_worklist_;
MarkingWorklists::ConcurrentMarkingBailoutWorklist::Local MarkingWorklists::ConcurrentMarkingBailoutWorklist::Local
concurrent_marking_bailout_worklist_; concurrent_marking_bailout_worklist_;
MarkingWorklists::EphemeronPairsWorklist::Local
discovered_ephemeron_pairs_worklist_;
MarkingWorklists::EphemeronPairsWorklist::Local
ephemeron_pairs_for_processing_worklist_;
size_t marked_bytes_ = 0; size_t marked_bytes_ = 0;
}; };
...@@ -100,7 +116,11 @@ MarkingStateBase::MarkingStateBase(HeapBase& heap, ...@@ -100,7 +116,11 @@ MarkingStateBase::MarkingStateBase(HeapBase& heap,
weak_callback_worklist_(marking_worklists.weak_callback_worklist()), weak_callback_worklist_(marking_worklists.weak_callback_worklist()),
write_barrier_worklist_(marking_worklists.write_barrier_worklist()), write_barrier_worklist_(marking_worklists.write_barrier_worklist()),
concurrent_marking_bailout_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) { void MarkingStateBase::MarkAndPush(const void* object, TraceDescriptor desc) {
...@@ -150,6 +170,24 @@ void MarkingStateBase::RegisterWeakReferenceIfNeeded(const void* object, ...@@ -150,6 +170,24 @@ void MarkingStateBase::RegisterWeakReferenceIfNeeded(const void* object,
RegisterWeakCallback(weak_callback, parameter); 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) { void MarkingStateBase::AccountMarkedBytes(const HeapObjectHeader& header) {
AccountMarkedBytes( AccountMarkedBytes(
header.IsLargeObject<HeapObjectHeader::AccessMode::kAtomic>() header.IsLargeObject<HeapObjectHeader::AccessMode::kAtomic>()
...@@ -177,6 +215,10 @@ class MutatorMarkingState : public MarkingStateBase { ...@@ -177,6 +215,10 @@ class MutatorMarkingState : public MarkingStateBase {
// previously_not_full_constructed_worklists_. // previously_not_full_constructed_worklists_.
void FlushNotFullyConstructedObjects(); void FlushNotFullyConstructedObjects();
// Moves ephemeron pairs in discovered_ephemeron_pairs_worklist_ to
// ephemeron_pairs_for_processing_worklist_.
void FlushDiscoveredEphemeronPairs();
inline void InvokeWeakRootsCallbackIfNeeded(const void*, TraceDescriptor, inline void InvokeWeakRootsCallbackIfNeeded(const void*, TraceDescriptor,
WeakCallback, const void*); WeakCallback, const void*);
}; };
...@@ -206,11 +248,6 @@ void MutatorMarkingState::InvokeWeakRootsCallbackIfNeeded( ...@@ -206,11 +248,6 @@ void MutatorMarkingState::InvokeWeakRootsCallbackIfNeeded(
weak_callback(LivenessBrokerFactory::Create(), parameter); weak_callback(LivenessBrokerFactory::Create(), parameter);
} }
void MarkingStateBase::RegisterWeakCallback(WeakCallback callback,
const void* object) {
weak_callback_worklist_.Push({callback, object});
}
class ConcurrentMarkingState : public MarkingStateBase { class ConcurrentMarkingState : public MarkingStateBase {
public: public:
ConcurrentMarkingState(HeapBase& heap, MarkingWorklists& marking_worklists) ConcurrentMarkingState(HeapBase& heap, MarkingWorklists& marking_worklists)
......
...@@ -25,6 +25,11 @@ void MarkingVisitorBase::VisitWeak(const void* object, TraceDescriptor desc, ...@@ -25,6 +25,11 @@ void MarkingVisitorBase::VisitWeak(const void* object, TraceDescriptor desc,
weak_member); weak_member);
} }
void MarkingVisitorBase::VisitEphemeron(const void* key,
TraceDescriptor value_desc) {
marking_state_.ProcessEphemeron(key, value_desc);
}
void MarkingVisitorBase::RegisterWeakCallback(WeakCallback callback, void MarkingVisitorBase::RegisterWeakCallback(WeakCallback callback,
const void* object) { const void* object) {
marking_state_.RegisterWeakCallback(callback, object); marking_state_.RegisterWeakCallback(callback, object);
......
...@@ -28,6 +28,7 @@ class V8_EXPORT_PRIVATE MarkingVisitorBase : public VisitorBase { ...@@ -28,6 +28,7 @@ class V8_EXPORT_PRIVATE MarkingVisitorBase : public VisitorBase {
protected: protected:
void Visit(const void*, TraceDescriptor) final; void Visit(const void*, TraceDescriptor) final;
void VisitWeak(const void*, TraceDescriptor, WeakCallback, const void*) final; void VisitWeak(const void*, TraceDescriptor, WeakCallback, const void*) final;
void VisitEphemeron(const void*, TraceDescriptor) final;
void RegisterWeakCallback(WeakCallback, const void*) final; void RegisterWeakCallback(WeakCallback, const void*) final;
MarkingStateBase& marking_state_; MarkingStateBase& marking_state_;
......
...@@ -17,6 +17,8 @@ void MarkingWorklists::ClearForTesting() { ...@@ -17,6 +17,8 @@ void MarkingWorklists::ClearForTesting() {
write_barrier_worklist_.Clear(); write_barrier_worklist_.Clear();
weak_callback_worklist_.Clear(); weak_callback_worklist_.Clear();
concurrent_marking_bailout_worklist_.Clear(); concurrent_marking_bailout_worklist_.Clear();
discovered_ephemeron_pairs_worklist_.Clear();
ephemeron_pairs_for_processing_worklist_.Clear();
} }
void MarkingWorklists::NotFullyConstructedWorklist::Push( void MarkingWorklists::NotFullyConstructedWorklist::Push(
......
...@@ -33,6 +33,11 @@ class MarkingWorklists { ...@@ -33,6 +33,11 @@ class MarkingWorklists {
size_t bailedout_size; size_t bailedout_size;
}; };
struct EphemeronPairItem {
const void* key;
TraceDescriptor value_desc;
};
// Segment size of 512 entries necessary to avoid throughput regressions. // Segment size of 512 entries necessary to avoid throughput regressions.
// Since the work list is currently a temporary object this is not a problem. // Since the work list is currently a temporary object this is not a problem.
using MarkingWorklist = using MarkingWorklist =
...@@ -46,6 +51,8 @@ class MarkingWorklists { ...@@ -46,6 +51,8 @@ class MarkingWorklists {
using ConcurrentMarkingBailoutWorklist = using ConcurrentMarkingBailoutWorklist =
heap::base::Worklist<ConcurrentMarkingBailoutItem, heap::base::Worklist<ConcurrentMarkingBailoutItem,
64 /* local entries */>; 64 /* local entries */>;
using EphemeronPairsWorklist =
heap::base::Worklist<EphemeronPairItem, 64 /* local entries */>;
class V8_EXPORT_PRIVATE NotFullyConstructedWorklist { class V8_EXPORT_PRIVATE NotFullyConstructedWorklist {
public: public:
...@@ -85,6 +92,12 @@ class MarkingWorklists { ...@@ -85,6 +92,12 @@ class MarkingWorklists {
ConcurrentMarkingBailoutWorklist* concurrent_marking_bailout_worklist() { ConcurrentMarkingBailoutWorklist* concurrent_marking_bailout_worklist() {
return &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(); void ClearForTesting();
...@@ -96,6 +109,8 @@ class MarkingWorklists { ...@@ -96,6 +109,8 @@ class MarkingWorklists {
WriteBarrierWorklist write_barrier_worklist_; WriteBarrierWorklist write_barrier_worklist_;
WeakCallbackWorklist weak_callback_worklist_; WeakCallbackWorklist weak_callback_worklist_;
ConcurrentMarkingBailoutWorklist concurrent_marking_bailout_worklist_; ConcurrentMarkingBailoutWorklist concurrent_marking_bailout_worklist_;
EphemeronPairsWorklist discovered_ephemeron_pairs_worklist_;
EphemeronPairsWorklist ephemeron_pairs_for_processing_worklist_;
}; };
} // namespace internal } // namespace internal
......
...@@ -84,6 +84,7 @@ v8_source_set("cppgc_unittests_sources") { ...@@ -84,6 +84,7 @@ v8_source_set("cppgc_unittests_sources") {
"heap/cppgc/concurrent-sweeper-unittest.cc", "heap/cppgc/concurrent-sweeper-unittest.cc",
"heap/cppgc/cross-thread-persistent-unittest.cc", "heap/cppgc/cross-thread-persistent-unittest.cc",
"heap/cppgc/custom-spaces-unittest.cc", "heap/cppgc/custom-spaces-unittest.cc",
"heap/cppgc/ephemeron-pair-unittest.cc",
"heap/cppgc/finalizer-trait-unittest.cc", "heap/cppgc/finalizer-trait-unittest.cc",
"heap/cppgc/free-list-unittest.cc", "heap/cppgc/free-list-unittest.cc",
"heap/cppgc/garbage-collected-unittest.cc", "heap/cppgc/garbage-collected-unittest.cc",
......
// 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
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