Commit 41738ca9 authored by Michael Lippautz's avatar Michael Lippautz Committed by V8 LUCI CQ

[heap] Fix marking in per-context mode

Per-context mode marking segregates worklists per context. Upon doing
so, Worklist::Local's move ctor was invoked which cleared the back
pointer to worklist. This break switching to that context which
happens in rare secnarios.

Rework Local marking worklists avoiding the move ctor which is also
removed.

Bug: chromium:1355545
Change-Id: If0e8c7f08df564b2a1e27e4a3fc5a6a40e46ee46
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3845630
Commit-Queue: Michael Lippautz <mlippautz@chromium.org>
Reviewed-by: 's avatarDominik Inführ <dinfuehr@chromium.org>
Cr-Commit-Position: refs/heads/main@{#82636}
parent b5145bb6
...@@ -307,8 +307,10 @@ class Worklist<EntryType, MinSegmentSize>::Local final { ...@@ -307,8 +307,10 @@ class Worklist<EntryType, MinSegmentSize>::Local final {
explicit Local(Worklist<EntryType, MinSegmentSize>* worklist); explicit Local(Worklist<EntryType, MinSegmentSize>* worklist);
~Local(); ~Local();
Local(Local&&) V8_NOEXCEPT; // Moving needs to specify whether the `worklist_` pointer is preserved or
Local& operator=(Local&&) V8_NOEXCEPT; // not.
Local(Local&&) V8_NOEXCEPT = delete;
Local& operator=(Local&&) V8_NOEXCEPT = delete;
// Having multiple copies of the same local view may be unsafe. // Having multiple copies of the same local view may be unsafe.
Local(const Local&) = delete; Local(const Local&) = delete;
...@@ -382,35 +384,6 @@ Worklist<EntryType, MinSegmentSize>::Local::~Local() { ...@@ -382,35 +384,6 @@ Worklist<EntryType, MinSegmentSize>::Local::~Local() {
DeleteSegment(pop_segment_); DeleteSegment(pop_segment_);
} }
template <typename EntryType, uint16_t MinSegmentSize>
Worklist<EntryType, MinSegmentSize>::Local::Local(
Worklist<EntryType, MinSegmentSize>::Local&& other) V8_NOEXCEPT {
worklist_ = other.worklist_;
push_segment_ = other.push_segment_;
pop_segment_ = other.pop_segment_;
other.worklist_ = nullptr;
other.push_segment_ = nullptr;
other.pop_segment_ = nullptr;
}
template <typename EntryType, uint16_t MinSegmentSize>
typename Worklist<EntryType, MinSegmentSize>::Local&
Worklist<EntryType, MinSegmentSize>::Local::operator=(
Worklist<EntryType, MinSegmentSize>::Local&& other) V8_NOEXCEPT {
if (this != &other) {
DCHECK_NULL(worklist_);
DCHECK_NULL(push_segment_);
DCHECK_NULL(pop_segment_);
worklist_ = other.worklist_;
push_segment_ = other.push_segment_;
pop_segment_ = other.pop_segment_;
other.worklist_ = nullptr;
other.push_segment_ = nullptr;
other.pop_segment_ = nullptr;
}
return *this;
}
template <typename EntryType, uint16_t MinSegmentSize> template <typename EntryType, uint16_t MinSegmentSize>
void Worklist<EntryType, MinSegmentSize>::Local::Push(EntryType entry) { void Worklist<EntryType, MinSegmentSize>::Local::Push(EntryType entry) {
if (V8_UNLIKELY(push_segment_->IsFull())) { if (V8_UNLIKELY(push_segment_->IsFull())) {
......
...@@ -779,6 +779,7 @@ void ConcurrentMarking::Run(JobDelegate* delegate, ...@@ -779,6 +779,7 @@ void ConcurrentMarking::Run(JobDelegate* delegate,
size_t ConcurrentMarking::GetMaxConcurrency(size_t worker_count) { size_t ConcurrentMarking::GetMaxConcurrency(size_t worker_count) {
size_t marking_items = marking_worklists_->shared()->Size(); size_t marking_items = marking_worklists_->shared()->Size();
marking_items += marking_worklists_->other()->Size();
for (auto& worklist : marking_worklists_->context_worklists()) for (auto& worklist : marking_worklists_->context_worklists())
marking_items += worklist.worklist->Size(); marking_items += worklist.worklist->Size();
return std::min<size_t>( return std::min<size_t>(
......
...@@ -20,19 +20,15 @@ void MarkingWorklists::Update(Callback callback) { ...@@ -20,19 +20,15 @@ void MarkingWorklists::Update(Callback callback) {
on_hold_.Update(callback); on_hold_.Update(callback);
wrapper_.Update(callback); wrapper_.Update(callback);
other_.Update(callback); other_.Update(callback);
for (auto cw : context_worklists_) { for (auto& cw : context_worklists_) {
if (cw.context == kSharedContext || cw.context == kOtherContext) {
// These contexts were updated above.
continue;
}
cw.worklist->Update(callback); cw.worklist->Update(callback);
} }
} }
void MarkingWorklists::Local::Push(HeapObject object) { active_.Push(object); } void MarkingWorklists::Local::Push(HeapObject object) { active_->Push(object); }
bool MarkingWorklists::Local::Pop(HeapObject* object) { bool MarkingWorklists::Local::Pop(HeapObject* object) {
if (active_.Pop(object)) return true; if (active_->Pop(object)) return true;
if (!is_per_context_mode_) return false; if (!is_per_context_mode_) return false;
// The active worklist is empty. Find any other non-empty worklist and // The active worklist is empty. Find any other non-empty worklist and
// switch the active worklist to it. // switch the active worklist to it.
...@@ -78,17 +74,9 @@ Address MarkingWorklists::Local::SwitchToContext(Address context) { ...@@ -78,17 +74,9 @@ Address MarkingWorklists::Local::SwitchToContext(Address context) {
return SwitchToContextSlow(context); return SwitchToContextSlow(context);
} }
Address MarkingWorklists::Local::SwitchToShared() { void MarkingWorklists::Local::SwitchToContextImpl(
return SwitchToContext(kSharedContext);
}
void MarkingWorklists::Local::SwitchToContext(
Address context, MarkingWorklist::Local* worklist) { Address context, MarkingWorklist::Local* worklist) {
// Save the current worklist. active_ = worklist;
*active_owner_ = std::move(active_);
// Switch to the new worklist.
active_owner_ = worklist;
active_ = std::move(*worklist);
active_context_ = context; active_context_ = context;
} }
......
...@@ -26,11 +26,7 @@ void MarkingWorklists::Clear() { ...@@ -26,11 +26,7 @@ void MarkingWorklists::Clear() {
on_hold_.Clear(); on_hold_.Clear();
wrapper_.Clear(); wrapper_.Clear();
other_.Clear(); other_.Clear();
for (auto cw : context_worklists_) { for (auto& cw : context_worklists_) {
if (cw.context == kSharedContext || cw.context == kOtherContext) {
// These contexts were cleared above.
continue;
}
cw.worklist->Clear(); cw.worklist->Clear();
} }
ReleaseContextWorklists(); ReleaseContextWorklists();
...@@ -43,24 +39,17 @@ void MarkingWorklists::Print() { ...@@ -43,24 +39,17 @@ void MarkingWorklists::Print() {
void MarkingWorklists::CreateContextWorklists( void MarkingWorklists::CreateContextWorklists(
const std::vector<Address>& contexts) { const std::vector<Address>& contexts) {
DCHECK(worklists_.empty());
DCHECK(context_worklists_.empty()); DCHECK(context_worklists_.empty());
if (contexts.empty()) return; if (contexts.empty()) return;
worklists_.reserve(contexts.size());
context_worklists_.reserve(contexts.size() + 2); context_worklists_.reserve(contexts.size());
context_worklists_.push_back({kSharedContext, &shared_});
context_worklists_.push_back({kOtherContext, &other_});
for (Address context : contexts) { for (Address context : contexts) {
MarkingWorklist* worklist = new MarkingWorklist(); context_worklists_.push_back(
worklists_.push_back(std::unique_ptr<MarkingWorklist>(worklist)); {context, std::make_unique<MarkingWorklist>()});
context_worklists_.push_back({context, worklist});
} }
} }
void MarkingWorklists::ReleaseContextWorklists() { void MarkingWorklists::ReleaseContextWorklists() { context_worklists_.clear(); }
context_worklists_.clear();
worklists_.clear();
}
void MarkingWorklists::PrintWorklist(const char* worklist_name, void MarkingWorklists::PrintWorklist(const char* worklist_name,
MarkingWorklist* worklist) { MarkingWorklist* worklist) {
...@@ -93,34 +82,44 @@ constexpr Address MarkingWorklists::Local::kSharedContext; ...@@ -93,34 +82,44 @@ constexpr Address MarkingWorklists::Local::kSharedContext;
constexpr Address MarkingWorklists::Local::kOtherContext; constexpr Address MarkingWorklists::Local::kOtherContext;
constexpr std::nullptr_t MarkingWorklists::Local::kNoCppMarkingState; constexpr std::nullptr_t MarkingWorklists::Local::kNoCppMarkingState;
namespace {
inline std::unordered_map<Address, std::unique_ptr<MarkingWorklist::Local>>
GetLocalPerContextMarkingWorklists(bool is_per_context_mode,
MarkingWorklists* global) {
if (!is_per_context_mode) return {};
std::unordered_map<Address, std::unique_ptr<MarkingWorklist::Local>>
worklist_by_context;
worklist_by_context.reserve(global->context_worklists().size());
for (auto& cw : global->context_worklists()) {
worklist_by_context[cw.context] =
std::make_unique<MarkingWorklist::Local>(cw.worklist.get());
}
return worklist_by_context;
}
} // namespace
MarkingWorklists::Local::Local( MarkingWorklists::Local::Local(
MarkingWorklists* global, MarkingWorklists* global,
std::unique_ptr<CppMarkingState> cpp_marking_state) std::unique_ptr<CppMarkingState> cpp_marking_state)
: on_hold_(global->on_hold()), : active_(&shared_),
shared_(global->shared()),
on_hold_(global->on_hold()),
wrapper_(global->wrapper()), wrapper_(global->wrapper()),
cpp_marking_state_(std::move(cpp_marking_state)) { active_context_(kSharedContext),
if (global->context_worklists().empty()) { is_per_context_mode_(!global->context_worklists().empty()),
MarkingWorklist::Local shared(global->shared()); worklist_by_context_(
active_ = std::move(shared); GetLocalPerContextMarkingWorklists(is_per_context_mode_, global)),
active_context_ = kSharedContext; other_(global->other()),
active_owner_ = nullptr; cpp_marking_state_(std::move(cpp_marking_state)) {}
} else {
is_per_context_mode_ = true;
worklist_by_context_.reserve(global->context_worklists().size());
for (auto& cw : global->context_worklists()) {
worklist_by_context_[cw.context] =
std::make_unique<MarkingWorklist::Local>(cw.worklist);
}
active_owner_ = worklist_by_context_[kSharedContext].get();
active_ = std::move(*active_owner_);
active_context_ = kSharedContext;
}
}
void MarkingWorklists::Local::Publish() { void MarkingWorklists::Local::Publish() {
active_.Publish(); shared_.Publish();
on_hold_.Publish(); on_hold_.Publish();
wrapper_.Publish(); wrapper_.Publish();
other_.Publish();
if (is_per_context_mode_) { if (is_per_context_mode_) {
for (auto& cw : worklist_by_context_) { for (auto& cw : worklist_by_context_) {
if (cw.first != active_context_) { if (cw.first != active_context_) {
...@@ -134,17 +133,21 @@ void MarkingWorklists::Local::Publish() { ...@@ -134,17 +133,21 @@ void MarkingWorklists::Local::Publish() {
bool MarkingWorklists::Local::IsEmpty() { bool MarkingWorklists::Local::IsEmpty() {
// This function checks the on_hold_ worklist, so it works only for the main // This function checks the on_hold_ worklist, so it works only for the main
// thread. // thread.
if (!active_.IsLocalEmpty() || !on_hold_.IsLocalEmpty() || if (!active_->IsLocalEmpty() || !on_hold_.IsLocalEmpty() ||
!active_.IsGlobalEmpty() || !on_hold_.IsGlobalEmpty()) { !active_->IsGlobalEmpty() || !on_hold_.IsGlobalEmpty()) {
return false; return false;
} }
if (!is_per_context_mode_) { if (!is_per_context_mode_) {
return true; return true;
} }
if (!shared_.IsLocalEmpty() || !other_.IsLocalEmpty() ||
!shared_.IsGlobalEmpty() || !other_.IsGlobalEmpty()) {
return false;
}
for (auto& cw : worklist_by_context_) { for (auto& cw : worklist_by_context_) {
if (cw.first != active_context_ && if (cw.first != active_context_ &&
!(cw.second->IsLocalEmpty() && cw.second->IsGlobalEmpty())) { !(cw.second->IsLocalEmpty() && cw.second->IsGlobalEmpty())) {
SwitchToContext(cw.first, cw.second.get()); SwitchToContextImpl(cw.first, cw.second.get());
return false; return false;
} }
} }
...@@ -160,38 +163,31 @@ bool MarkingWorklists::Local::IsWrapperEmpty() const { ...@@ -160,38 +163,31 @@ bool MarkingWorklists::Local::IsWrapperEmpty() const {
} }
void MarkingWorklists::Local::ShareWork() { void MarkingWorklists::Local::ShareWork() {
if (!active_.IsLocalEmpty() && active_.IsGlobalEmpty()) { if (!active_->IsLocalEmpty() && active_->IsGlobalEmpty()) {
active_.Publish(); active_->Publish();
} }
if (is_per_context_mode_ && active_context_ != kSharedContext) { if (is_per_context_mode_ && active_context_ != kSharedContext) {
MarkingWorklist::Local* shared = worklist_by_context_[kSharedContext].get(); if (!shared_.IsLocalEmpty() && shared_.IsGlobalEmpty()) {
if (!shared->IsLocalEmpty() && shared->IsGlobalEmpty()) { shared_.Publish();
shared->Publish();
} }
} }
} }
void MarkingWorklists::Local::MergeOnHold() { void MarkingWorklists::Local::MergeOnHold() { shared_.Merge(&on_hold_); }
MarkingWorklist::Local* shared =
active_context_ == kSharedContext
? &active_
: worklist_by_context_[kSharedContext].get();
shared->Merge(&on_hold_);
}
bool MarkingWorklists::Local::PopContext(HeapObject* object) { bool MarkingWorklists::Local::PopContext(HeapObject* object) {
DCHECK(is_per_context_mode_); DCHECK(is_per_context_mode_);
// As an optimization we first check only the local segments to avoid locks. // As an optimization we first check only the local segments to avoid locks.
for (auto& cw : worklist_by_context_) { for (auto& cw : worklist_by_context_) {
if (cw.first != active_context_ && !cw.second->IsLocalEmpty()) { if (cw.first != active_context_ && !cw.second->IsLocalEmpty()) {
SwitchToContext(cw.first, cw.second.get()); SwitchToContextImpl(cw.first, cw.second.get());
return active_.Pop(object); return active_->Pop(object);
} }
} }
// All local segments are empty. Check global segments. // All local segments are empty. Check global segments.
for (auto& cw : worklist_by_context_) { for (auto& cw : worklist_by_context_) {
if (cw.first != active_context_ && cw.second->Pop(object)) { if (cw.first != active_context_ && cw.second->Pop(object)) {
SwitchToContext(cw.first, cw.second.get()); SwitchToContextImpl(cw.first, cw.second.get());
return true; return true;
} }
} }
...@@ -203,14 +199,24 @@ bool MarkingWorklists::Local::PopContext(HeapObject* object) { ...@@ -203,14 +199,24 @@ bool MarkingWorklists::Local::PopContext(HeapObject* object) {
Address MarkingWorklists::Local::SwitchToContextSlow(Address context) { Address MarkingWorklists::Local::SwitchToContextSlow(Address context) {
const auto& it = worklist_by_context_.find(context); const auto& it = worklist_by_context_.find(context);
if (V8_UNLIKELY(it == worklist_by_context_.end())) { if (V8_UNLIKELY(it == worklist_by_context_.end())) {
// This context was created during marking or is not being measured, // The context passed is not an actual context:
// so we don't have a specific worklist for it. // - Shared context that should use the explicit worklist.
SwitchToContext(kOtherContext, worklist_by_context_[kOtherContext].get()); // - This context was created during marking and should use the other
// bucket.
if (context == kSharedContext) {
SwitchToContextImpl(kSharedContext, &shared_);
} else {
SwitchToContextImpl(kOtherContext, &other_);
}
} else { } else {
SwitchToContext(it->first, it->second.get()); SwitchToContextImpl(it->first, it->second.get());
} }
return active_context_; return active_context_;
} }
Address MarkingWorklists::Local::SwitchToSharedForTesting() {
return SwitchToContext(kSharedContext);
}
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
...@@ -62,7 +62,7 @@ using WrapperTracingWorklist = ::heap::base::Worklist<HeapObject, 16>; ...@@ -62,7 +62,7 @@ using WrapperTracingWorklist = ::heap::base::Worklist<HeapObject, 16>;
// a stable across Scavenges and stay valid throughout the marking phase. // a stable across Scavenges and stay valid throughout the marking phase.
struct ContextWorklistPair { struct ContextWorklistPair {
Address context; Address context;
MarkingWorklist* worklist; std::unique_ptr<MarkingWorklist> worklist;
}; };
// A helper class that owns all global marking worklists. // A helper class that owns all global marking worklists.
...@@ -73,8 +73,8 @@ class V8_EXPORT_PRIVATE MarkingWorklists final { ...@@ -73,8 +73,8 @@ class V8_EXPORT_PRIVATE MarkingWorklists final {
// - kSharedContext is for objects that are not attributed to any context. // - kSharedContext is for objects that are not attributed to any context.
// - kOtherContext is for objects that are attributed to contexts that are // - kOtherContext is for objects that are attributed to contexts that are
// not being measured. // not being measured.
static const Address kSharedContext = 0; static constexpr Address kSharedContext = 0;
static const Address kOtherContext = 8; static constexpr Address kOtherContext = 8;
MarkingWorklists() = default; MarkingWorklists() = default;
...@@ -90,6 +90,7 @@ class V8_EXPORT_PRIVATE MarkingWorklists final { ...@@ -90,6 +90,7 @@ class V8_EXPORT_PRIVATE MarkingWorklists final {
MarkingWorklist* shared() { return &shared_; } MarkingWorklist* shared() { return &shared_; }
MarkingWorklist* on_hold() { return &on_hold_; } MarkingWorklist* on_hold() { return &on_hold_; }
MarkingWorklist* other() { return &other_; }
WrapperTracingWorklist* wrapper() { return &wrapper_; } WrapperTracingWorklist* wrapper() { return &wrapper_; }
// A list of (context, worklist) pairs that was set up at the start of // A list of (context, worklist) pairs that was set up at the start of
...@@ -112,6 +113,7 @@ class V8_EXPORT_PRIVATE MarkingWorklists final { ...@@ -112,6 +113,7 @@ class V8_EXPORT_PRIVATE MarkingWorklists final {
void PrintWorklist(const char* worklist_name, MarkingWorklist* worklist); void PrintWorklist(const char* worklist_name, MarkingWorklist* worklist);
// Worklist used for most objects. // Worklist used for most objects.
// TODO(mlippautz): Rename to "default".
MarkingWorklist shared_; MarkingWorklist shared_;
// Concurrent marking uses this worklist to bail out of marking objects // Concurrent marking uses this worklist to bail out of marking objects
...@@ -125,11 +127,8 @@ class V8_EXPORT_PRIVATE MarkingWorklists final { ...@@ -125,11 +127,8 @@ class V8_EXPORT_PRIVATE MarkingWorklists final {
// transitive closure. // transitive closure.
WrapperTracingWorklist wrapper_; WrapperTracingWorklist wrapper_;
// Per-context worklists. // Per-context worklists. Objects are in the `shared_` worklist by default.
std::vector<ContextWorklistPair> context_worklists_; std::vector<ContextWorklistPair> context_worklists_;
// This is used only for lifetime management of the per-context worklists.
std::vector<std::unique_ptr<MarkingWorklist>> worklists_;
// Worklist used for objects that are attributed to contexts that are // Worklist used for objects that are attributed to contexts that are
// not being measured. // not being measured.
MarkingWorklist other_; MarkingWorklist other_;
...@@ -187,27 +186,31 @@ class V8_EXPORT_PRIVATE MarkingWorklists::Local final { ...@@ -187,27 +186,31 @@ class V8_EXPORT_PRIVATE MarkingWorklists::Local final {
// Returns the context of the active worklist. // Returns the context of the active worklist.
Address Context() const { return active_context_; } Address Context() const { return active_context_; }
inline Address SwitchToContext(Address context); inline Address SwitchToContext(Address context);
inline Address SwitchToShared();
bool IsPerContextMode() const { return is_per_context_mode_; } bool IsPerContextMode() const { return is_per_context_mode_; }
CppMarkingState* cpp_marking_state() const { CppMarkingState* cpp_marking_state() const {
return cpp_marking_state_.get(); return cpp_marking_state_.get();
} }
Address SwitchToSharedForTesting();
private: private:
inline void SwitchToContextImpl(Address context,
MarkingWorklist::Local* worklist);
bool PopContext(HeapObject* object); bool PopContext(HeapObject* object);
Address SwitchToContextSlow(Address context); Address SwitchToContextSlow(Address context);
inline void SwitchToContext(Address context,
MarkingWorklist::Local* worklist); // Points to either `shared_`, `other_` or to a per-context worklist.
MarkingWorklist::Local* active_;
MarkingWorklist::Local shared_;
MarkingWorklist::Local on_hold_; MarkingWorklist::Local on_hold_;
WrapperTracingWorklist::Local wrapper_; WrapperTracingWorklist::Local wrapper_;
MarkingWorklist::Local active_;
Address active_context_; Address active_context_;
MarkingWorklist::Local* active_owner_; const bool is_per_context_mode_;
bool is_per_context_mode_ = false; const std::unordered_map<Address, std::unique_ptr<MarkingWorklist::Local>>
std::unordered_map<Address, std::unique_ptr<MarkingWorklist::Local>>
worklist_by_context_; worklist_by_context_;
MarkingWorklist::Local other_;
std::unique_ptr<CppMarkingState> cpp_marking_state_; std::unique_ptr<CppMarkingState> cpp_marking_state_;
}; };
......
...@@ -87,7 +87,7 @@ TEST_F(MarkingWorklistTest, ContextWorklistsPushPop) { ...@@ -87,7 +87,7 @@ TEST_F(MarkingWorklistTest, ContextWorklistsPushPop) {
ReadOnlyRoots(i_isolate()->heap()).undefined_value(); ReadOnlyRoots(i_isolate()->heap()).undefined_value();
worklists.SwitchToContext(context); worklists.SwitchToContext(context);
worklists.Push(pushed_object); worklists.Push(pushed_object);
worklists.SwitchToShared(); worklists.SwitchToSharedForTesting();
HeapObject popped_object; HeapObject popped_object;
EXPECT_TRUE(worklists.Pop(&popped_object)); EXPECT_TRUE(worklists.Pop(&popped_object));
EXPECT_EQ(popped_object, pushed_object); EXPECT_EQ(popped_object, pushed_object);
...@@ -104,7 +104,7 @@ TEST_F(MarkingWorklistTest, ContextWorklistsEmpty) { ...@@ -104,7 +104,7 @@ TEST_F(MarkingWorklistTest, ContextWorklistsEmpty) {
worklists.SwitchToContext(context); worklists.SwitchToContext(context);
worklists.Push(pushed_object); worklists.Push(pushed_object);
EXPECT_FALSE(worklists.IsEmpty()); EXPECT_FALSE(worklists.IsEmpty());
worklists.SwitchToShared(); worklists.SwitchToSharedForTesting();
EXPECT_FALSE(worklists.IsEmpty()); EXPECT_FALSE(worklists.IsEmpty());
HeapObject popped_object; HeapObject popped_object;
EXPECT_TRUE(worklists.Pop(&popped_object)); EXPECT_TRUE(worklists.Pop(&popped_object));
......
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