Commit 2d1727f5 authored by Michael Lippautz's avatar Michael Lippautz Committed by Commit Bot

heap: Rework ephemeron trace traits and heap snapshot

This patch unifies weak and strong handling of ephemerons and adds support for
"reverse" ephemerons (strong key w/ weak value) to the heap snapshot.

It also removes dependencies on Trait::kWeakHandlingFlag in ephemeron tracing.

Bug: 1019191
Change-Id: Ic3429a6fb45663ab7fe3c59790a72384066a1892
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1897834
Commit-Queue: Michael Lippautz <mlippautz@chromium.org>
Reviewed-by: default avatarOmer Katz <omerkatz@chromium.org>
Reviewed-by: default avatarKentaro Hara <haraken@chromium.org>
Cr-Commit-Position: refs/heads/master@{#712628}
parent 6d423ab2
...@@ -348,18 +348,20 @@ class GC_PLUGIN_IGNORE( ...@@ -348,18 +348,20 @@ class GC_PLUGIN_IGNORE(
key_tracing_callback_(key_tracing_callback), key_tracing_callback_(key_tracing_callback),
value_tracing_callback_(value_tracing_callback) {} value_tracing_callback_(value_tracing_callback) {}
void Process(V8EmbedderGraphBuilder* builder) { bool Process(V8EmbedderGraphBuilder* builder) {
Traceable key = nullptr; Traceable key = nullptr;
{ {
TraceKeysScope scope(builder, &key); TraceKeysScope scope(builder, &key);
key_tracing_callback_(builder, const_cast<void*>(key_)); key_tracing_callback_(builder, const_cast<void*>(key_));
} }
DCHECK(key); DCHECK(key);
DCHECK(builder->GetStateNotNull(key)); if (!builder->StateExists(key))
return false;
{ {
TraceValuesScope scope(builder, key); TraceValuesScope scope(builder, key);
value_tracing_callback_(builder, const_cast<void*>(value_)); value_tracing_callback_(builder, const_cast<void*>(value_));
} }
return true;
} }
private: private:
...@@ -378,6 +380,10 @@ class GC_PLUGIN_IGNORE( ...@@ -378,6 +380,10 @@ class GC_PLUGIN_IGNORE(
return states_.at(traceable); return states_.at(traceable);
} }
bool StateExists(Traceable traceable) const {
return states_.Contains(traceable);
}
State* GetStateNotNull(Traceable traceable) { State* GetStateNotNull(Traceable traceable) {
CHECK(states_.Contains(traceable)); CHECK(states_.Contains(traceable));
return states_.at(traceable); return states_.at(traceable);
...@@ -772,6 +778,12 @@ void V8EmbedderGraphBuilder::VisitTransitiveClosure() { ...@@ -772,6 +778,12 @@ void V8EmbedderGraphBuilder::VisitTransitiveClosure() {
// tracing can record new ephemerons, and tracing an ephemeron can add // tracing can record new ephemerons, and tracing an ephemeron can add
// items to the regular worklist, we need to repeatedly process the worklist // items to the regular worklist, we need to repeatedly process the worklist
// until a fixed point is reached. // until a fixed point is reached.
// Because snapshots are processed in stages, there may be ephemerons that
// where key's do not have yet a state associated with them which prohibits
// them from being processed. Such ephemerons are stashed for later
// processing.
Deque<std::unique_ptr<EphemeronItem>> unprocessed_ephemerons_;
do { do {
// Step 1: Go through all items in the worklist using depth-first search. // Step 1: Go through all items in the worklist using depth-first search.
while (!worklist_.empty()) { while (!worklist_.empty()) {
...@@ -780,15 +792,29 @@ void V8EmbedderGraphBuilder::VisitTransitiveClosure() { ...@@ -780,15 +792,29 @@ void V8EmbedderGraphBuilder::VisitTransitiveClosure() {
item->Process(this); item->Process(this);
} }
// Step 2: Go through ephemeron items. Only process an ephemeron item if // Step 2: Go through ephemeron items.
// its key was already observed.
// Re-add unprocessed ephemerons from last loop iteration.
while (!unprocessed_ephemerons_.empty()) {
ephemeron_worklist_.push_back(unprocessed_ephemerons_.TakeFirst());
}
// Only process an ephemeron item if its key was already observed.
while (!ephemeron_worklist_.empty()) { while (!ephemeron_worklist_.empty()) {
std::unique_ptr<EphemeronItem> item = std::unique_ptr<EphemeronItem> item =
std::move(ephemeron_worklist_.front()); std::move(ephemeron_worklist_.front());
ephemeron_worklist_.pop_front(); ephemeron_worklist_.pop_front();
item->Process(this); if (!item->Process(this)) {
unprocessed_ephemerons_.push_back(std::move(item));
}
} }
} while (!worklist_.empty()); } while (!worklist_.empty());
// Re-add unprocessed ephemerons. A later invocation of VisitTransitiveClosure
// must process them.
while (!unprocessed_ephemerons_.empty()) {
ephemeron_worklist_.push_back(unprocessed_ephemerons_.TakeFirst());
}
} }
} // namespace } // namespace
......
...@@ -289,58 +289,42 @@ struct TraceTrait<base::Optional<T>> { ...@@ -289,58 +289,42 @@ struct TraceTrait<base::Optional<T>> {
} }
}; };
// The parameter Strongify serve as an override for weak handling. If it is set // Reorders parameters for use in blink::Visitor::VisitEphemeronKeyValuePair.
// to true, we ignore the kWeakHandlingFlag provided by the traits and always template <typename _KeyType,
// trace strongly (i.e. using kNoWeakHandling). typename _ValueType,
template <typename Key, typename _KeyTraits,
typename Value, typename _ValueTraits,
typename KeyTraits, bool = WTF::IsWeak<_ValueType>::value>
typename ValueTraits, struct EphemeronKeyValuePair {
bool Strongify> using KeyType = _KeyType;
struct TraceKeyValuePairTraits { using ValueType = _ValueType;
static constexpr bool kKeyIsWeak = using KeyTraits = _KeyTraits;
KeyTraits::kWeakHandlingFlag == WTF::kWeakHandling; using ValueTraits = _ValueTraits;
static constexpr bool kValueIsWeak =
ValueTraits::kWeakHandlingFlag == WTF::kWeakHandling; EphemeronKeyValuePair(KeyType* k, ValueType* v) : key(k), value(v) {}
KeyType* key;
static bool IsAlive(Key& key, Value& value) { ValueType* value;
return (blink::TraceCollectionIfEnabled < Strongify };
? WTF::kNoWeakHandling
: KeyTraits::kWeakHandlingFlag, template <typename _KeyType,
Key, KeyTraits > ::IsAlive(key)) && typename _ValueType,
blink::TraceCollectionIfEnabled < Strongify typename _KeyTraits,
? WTF::kNoWeakHandling typename _ValueTraits>
: ValueTraits::kWeakHandlingFlag, struct EphemeronKeyValuePair<_KeyType,
Value, ValueTraits > ::IsAlive(value); _ValueType,
} _KeyTraits,
_ValueTraits,
// Trace the value only if the key is alive. true> : EphemeronKeyValuePair<_ValueType,
template <bool is_ephemeron = kKeyIsWeak && !kValueIsWeak> _KeyType,
static bool Trace(Visitor* visitor, Key& key, Value& value) { _ValueTraits,
const bool key_is_dead = blink::TraceCollectionIfEnabled < Strongify _KeyTraits,
? WTF::kNoWeakHandling false> {
: KeyTraits::kWeakHandlingFlag, EphemeronKeyValuePair(_KeyType* k, _ValueType* v)
Key, KeyTraits > ::Trace(visitor, &key); : EphemeronKeyValuePair<_ValueType,
if (key_is_dead && !Strongify) _KeyType,
return true; _ValueTraits,
return TraceCollectionIfEnabled < Strongify _KeyTraits,
? WTF::kNoWeakHandling false>(v, k) {}
: ValueTraits::kWeakHandlingFlag,
Value, ValueTraits > ::Trace(visitor, &value);
}
// Specializations for ephemerons:
template <>
static bool Trace<true>(Visitor* visitor, Key& key, Value& value) {
return visitor->VisitEphemeronKeyValuePair(
&key, &value,
TraceCollectionIfEnabled < Strongify ? WTF::kNoWeakHandling
: KeyTraits::kWeakHandlingFlag,
Key, KeyTraits > ::Trace,
TraceCollectionIfEnabled < Strongify ? WTF::kNoWeakHandling
: ValueTraits::kWeakHandlingFlag,
Value, ValueTraits > ::Trace);
}
}; };
} // namespace blink } // namespace blink
...@@ -558,77 +542,75 @@ template <typename Key, typename Value, typename Traits> ...@@ -558,77 +542,75 @@ template <typename Key, typename Value, typename Traits>
struct TraceInCollectionTrait<kNoWeakHandling, struct TraceInCollectionTrait<kNoWeakHandling,
KeyValuePair<Key, Value>, KeyValuePair<Key, Value>,
Traits> { Traits> {
using EphemeronHelper =
blink::EphemeronKeyValuePair<Key,
Value,
typename Traits::KeyTraits,
typename Traits::ValueTraits>;
static bool Trace(blink::Visitor* visitor, KeyValuePair<Key, Value>& self) { static bool Trace(blink::Visitor* visitor, KeyValuePair<Key, Value>& self) {
static_assert(IsTraceableInCollectionTrait<Traits>::value || if (WTF::IsWeak<Key>::value != WTF::IsWeak<Value>::value) {
Traits::kWeakHandlingFlag == WTF::kWeakHandling, // Strongification of Weak/Strong and Strong/Weak.
"T should not be traced"); EphemeronHelper helper(&self.key, &self.value);
blink::TraceKeyValuePairTraits<Key, Value, typename Traits::KeyTraits, visitor->VisitEphemeronKeyValuePair(
typename Traits::ValueTraits, helper.key, helper.value,
true>::Trace(visitor, self.key, self.value); blink::TraceCollectionIfEnabled<
kNoWeakHandling, typename EphemeronHelper::KeyType,
typename EphemeronHelper::KeyTraits>::Trace,
blink::TraceCollectionIfEnabled<
kNoWeakHandling, typename EphemeronHelper::ValueType,
typename EphemeronHelper::ValueTraits>::Trace);
} else {
// Strongification of Strong/Strong or Weak/Weak. Order does not matter
// here.
blink::TraceCollectionIfEnabled<
kNoWeakHandling, Key, typename Traits::KeyTraits>::Trace(visitor,
&self.key);
blink::TraceCollectionIfEnabled<
kNoWeakHandling, Value,
typename Traits::ValueTraits>::Trace(visitor, &self.value);
}
return false; return false;
} }
}; };
template <typename Key, typename Value, typename Traits> template <typename Key, typename Value, typename Traits>
struct TraceInCollectionTrait<kWeakHandling, KeyValuePair<Key, Value>, Traits> { struct TraceInCollectionTrait<kWeakHandling, KeyValuePair<Key, Value>, Traits> {
static constexpr bool kKeyIsWeak = using EphemeronHelper =
Traits::KeyTraits::kWeakHandlingFlag == kWeakHandling; blink::EphemeronKeyValuePair<Key,
static constexpr bool kValueIsWeak = Value,
Traits::ValueTraits::kWeakHandlingFlag == kWeakHandling; typename Traits::KeyTraits,
static const bool kKeyHasStrongRefs = typename Traits::ValueTraits>;
IsTraceableInCollectionTrait<typename Traits::KeyTraits>::value;
static const bool kValueHasStrongRefs = template <typename T>
IsTraceableInCollectionTrait<typename Traits::ValueTraits>::value; static constexpr WeakHandlingFlag GetWeaknessFlag() {
return WTF::IsWeak<T>::value ? kWeakHandling : kNoWeakHandling;
}
static bool IsAlive(KeyValuePair<Key, Value>& self) { static bool IsAlive(KeyValuePair<Key, Value>& self) {
static_assert(!kKeyIsWeak || !kValueIsWeak || !kKeyHasStrongRefs || // Needed for Weak/Weak, Strong/Weak (reverse ephemeron), and Weak/Strong
!kValueHasStrongRefs, // (ephemeron). Order of invocation does not matter as tracing weak key or
"this configuration is disallowed to avoid unexpected leaks"); // value does not have any side effects.
if ((kValueIsWeak && !kKeyIsWeak) || return blink::TraceCollectionIfEnabled<
(kValueIsWeak && kKeyIsWeak && !kValueHasStrongRefs)) { GetWeaknessFlag<Key>(), Key,
// Check value first. The only difference between checking the key first typename Traits::KeyTraits>::IsAlive(self.key) &&
// or the checking the value first is the order in which we pass them. blink::TraceCollectionIfEnabled<
return blink::TraceKeyValuePairTraits< GetWeaknessFlag<Value>(), Value,
Value, Key, typename Traits::ValueTraits, typename Traits::KeyTraits, typename Traits::ValueTraits>::IsAlive(self.value);
false>::IsAlive(self.value, self.key);
}
// Check key first.
return blink::TraceKeyValuePairTraits<
Key, Value, typename Traits::KeyTraits, typename Traits::ValueTraits,
false>::IsAlive(self.key, self.value);
} }
static bool Trace(blink::Visitor* visitor, KeyValuePair<Key, Value>& self) { static bool Trace(blink::Visitor* visitor, KeyValuePair<Key, Value>& self) {
// This is the core of the ephemeron-like functionality. If there is EphemeronHelper helper(&self.key, &self.value);
// weakness on the key side then we first check whether there are return visitor->VisitEphemeronKeyValuePair(
// dead weak pointers on that side, and if there are we don't mark the helper.key, helper.value,
// value side (yet). Conversely if there is weakness on the value side blink::TraceCollectionIfEnabled<
// we check that first and don't mark the key side yet if we find dead GetWeaknessFlag<typename EphemeronHelper::KeyType>(),
// weak pointers. typename EphemeronHelper::KeyType,
// Corner case: If there is weakness on both the key and value side, typename EphemeronHelper::KeyTraits>::Trace,
// and there are also strong pointers on the both sides then we could blink::TraceCollectionIfEnabled<
// unexpectedly leak. The scenario is that the weak pointer on the key GetWeaknessFlag<typename EphemeronHelper::ValueType>(),
// side is alive, which causes the strong pointer on the key side to be typename EphemeronHelper::ValueType,
// marked. If that then results in the object pointed to by the weak typename EphemeronHelper::ValueTraits>::Trace);
// pointer on the value side being marked live, then the whole
// key-value entry is leaked. To avoid unexpected leaking, we disallow
// this case, but if you run into this assert, please reach out to Blink
// reviewers, and we may relax it.
static_assert(!kKeyIsWeak || !kValueIsWeak || !kKeyHasStrongRefs ||
!kValueHasStrongRefs,
"this configuration is disallowed to avoid unexpected leaks");
if ((kValueIsWeak && !kKeyIsWeak) ||
(kValueIsWeak && kKeyIsWeak && !kValueHasStrongRefs)) {
// Check value first. The only difference between checking the key first
// or the checking the value first is the order in which we pass them.
return blink::TraceKeyValuePairTraits<
Value, Key, typename Traits::ValueTraits, typename Traits::KeyTraits,
false>::Trace(visitor, self.value, self.key);
}
// Check key first.
return blink::TraceKeyValuePairTraits<
Key, Value, typename Traits::KeyTraits, typename Traits::ValueTraits,
false>::Trace(visitor, self.key, self.value);
} }
}; };
......
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