// Copyright 2019 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 "src/heap/marking-worklist.h" #include #include #include #include "src/heap/cppgc-js/cpp-heap.h" #include "src/heap/cppgc-js/cpp-marking-state.h" #include "src/heap/marking-worklist-inl.h" #include "src/objects/heap-object-inl.h" #include "src/objects/heap-object.h" #include "src/objects/instance-type-inl.h" #include "src/objects/instance-type.h" #include "src/objects/map.h" #include "src/objects/objects-definitions.h" namespace v8 { namespace internal { MarkingWorklists::~MarkingWorklists() { DCHECK(shared_.IsEmpty()); DCHECK(on_hold_.IsEmpty()); DCHECK(other_.IsEmpty()); DCHECK(worklists_.empty()); DCHECK(context_worklists_.empty()); } void MarkingWorklists::Clear() { shared_.Clear(); on_hold_.Clear(); wrapper_.Clear(); other_.Clear(); for (auto cw : context_worklists_) { if (cw.context == kSharedContext || cw.context == kOtherContext) { // These contexts were cleared above. continue; } cw.worklist->Clear(); } ReleaseContextWorklists(); } void MarkingWorklists::Print() { PrintWorklist("shared", &shared_); PrintWorklist("on_hold", &on_hold_); } void MarkingWorklists::CreateContextWorklists( const std::vector
& contexts) { DCHECK(worklists_.empty()); DCHECK(context_worklists_.empty()); if (contexts.empty()) return; worklists_.reserve(contexts.size()); context_worklists_.reserve(contexts.size() + 2); context_worklists_.push_back({kSharedContext, &shared_}); context_worklists_.push_back({kOtherContext, &other_}); for (Address context : contexts) { MarkingWorklist* worklist = new MarkingWorklist(); worklists_.push_back(std::unique_ptr(worklist)); context_worklists_.push_back({context, worklist}); } } void MarkingWorklists::ReleaseContextWorklists() { context_worklists_.clear(); worklists_.clear(); } void MarkingWorklists::PrintWorklist(const char* worklist_name, MarkingWorklist* worklist) { #ifdef DEBUG std::map count; int total_count = 0; worklist->Iterate([&count, &total_count](HeapObject obj) { ++total_count; count[obj.map().instance_type()]++; }); std::vector> rank; rank.reserve(count.size()); for (const auto& i : count) { rank.emplace_back(i.second, i.first); } std::map instance_type_name; #define INSTANCE_TYPE_NAME(name) instance_type_name[name] = #name; INSTANCE_TYPE_LIST(INSTANCE_TYPE_NAME) #undef INSTANCE_TYPE_NAME std::sort(rank.begin(), rank.end(), std::greater>()); PrintF("Worklist %s: %d\n", worklist_name, total_count); for (auto i : rank) { PrintF(" [%s]: %d\n", instance_type_name[i.second].c_str(), i.first); } #endif } constexpr Address MarkingWorklists::Local::kSharedContext; constexpr Address MarkingWorklists::Local::kOtherContext; constexpr std::nullptr_t MarkingWorklists::Local::kNoCppMarkingState; MarkingWorklists::Local::Local( MarkingWorklists* global, std::unique_ptr cpp_marking_state) : on_hold_(global->on_hold()), wrapper_(global->wrapper()), is_per_context_mode_(false), cpp_marking_state_(std::move(cpp_marking_state)) { if (global->context_worklists().empty()) { MarkingWorklist::Local shared(global->shared()); active_ = std::move(shared); active_context_ = kSharedContext; active_owner_ = nullptr; } 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(cw.worklist); } active_owner_ = worklist_by_context_[kSharedContext].get(); active_ = std::move(*active_owner_); active_context_ = kSharedContext; } } MarkingWorklists::Local::~Local() { DCHECK(active_.IsLocalEmpty()); if (is_per_context_mode_) { for (auto& cw : worklist_by_context_) { if (cw.first != active_context_) { DCHECK(cw.second->IsLocalEmpty()); } } } } void MarkingWorklists::Local::Publish() { active_.Publish(); on_hold_.Publish(); wrapper_.Publish(); if (is_per_context_mode_) { for (auto& cw : worklist_by_context_) { if (cw.first != active_context_) { cw.second->Publish(); } } } PublishWrapper(); } bool MarkingWorklists::Local::IsEmpty() { // This function checks the on_hold_ worklist, so it works only for the main // thread. if (!active_.IsLocalEmpty() || !on_hold_.IsLocalEmpty() || !active_.IsGlobalEmpty() || !on_hold_.IsGlobalEmpty()) { return false; } if (!is_per_context_mode_) { return true; } for (auto& cw : worklist_by_context_) { if (cw.first != active_context_ && !(cw.second->IsLocalEmpty() && cw.second->IsGlobalEmpty())) { SwitchToContext(cw.first, cw.second.get()); return false; } } return true; } bool MarkingWorklists::Local::IsWrapperEmpty() const { if (cpp_marking_state_) { DCHECK(wrapper_.IsLocalAndGlobalEmpty()); return cpp_marking_state_->IsLocalEmpty(); } return wrapper_.IsLocalAndGlobalEmpty(); } void MarkingWorklists::Local::ShareWork() { if (!active_.IsLocalEmpty() && active_.IsGlobalEmpty()) { active_.Publish(); } if (is_per_context_mode_ && active_context_ != kSharedContext) { MarkingWorklist::Local* shared = worklist_by_context_[kSharedContext].get(); if (!shared->IsLocalEmpty() && shared->IsGlobalEmpty()) { shared->Publish(); } } } void MarkingWorklists::Local::MergeOnHold() { MarkingWorklist::Local* shared = active_context_ == kSharedContext ? &active_ : worklist_by_context_[kSharedContext].get(); shared->Merge(&on_hold_); } bool MarkingWorklists::Local::PopContext(HeapObject* object) { DCHECK(is_per_context_mode_); // As an optimization we first check only the local segments to avoid locks. for (auto& cw : worklist_by_context_) { if (cw.first != active_context_ && !cw.second->IsLocalEmpty()) { SwitchToContext(cw.first, cw.second.get()); return active_.Pop(object); } } // All local segments are empty. Check global segments. for (auto& cw : worklist_by_context_) { if (cw.first != active_context_ && cw.second->Pop(object)) { SwitchToContext(cw.first, cw.second.get()); return true; } } // All worklists are empty. Switch to the default shared worklist. SwitchToContext(kSharedContext); return false; } Address MarkingWorklists::Local::SwitchToContextSlow(Address context) { const auto& it = worklist_by_context_.find(context); if (V8_UNLIKELY(it == worklist_by_context_.end())) { // This context was created during marking or is not being measured, // so we don't have a specific worklist for it. SwitchToContext(kOtherContext, worklist_by_context_[kOtherContext].get()); } else { SwitchToContext(it->first, it->second.get()); } return active_context_; } } // namespace internal } // namespace v8