1 // Copyright 2019 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "src/heap/marking-worklist.h"
6
7 #include <algorithm>
8 #include <map>
9
10 #include "src/heap/marking-worklist-inl.h"
11 #include "src/objects/heap-object-inl.h"
12 #include "src/objects/heap-object.h"
13 #include "src/objects/instance-type-inl.h"
14 #include "src/objects/instance-type.h"
15 #include "src/objects/map.h"
16 #include "src/objects/objects-definitions.h"
17
18 namespace v8 {
19 namespace internal {
20
~MarkingWorklists()21 MarkingWorklists::~MarkingWorklists() {
22 DCHECK(shared_.IsEmpty());
23 DCHECK(on_hold_.IsEmpty());
24 DCHECK(other_.IsEmpty());
25 DCHECK(worklists_.empty());
26 DCHECK(context_worklists_.empty());
27 }
28
Clear()29 void MarkingWorklists::Clear() {
30 shared_.Clear();
31 on_hold_.Clear();
32 embedder_.Clear();
33 other_.Clear();
34 for (auto cw : context_worklists_) {
35 if (cw.context == kSharedContext || cw.context == kOtherContext) {
36 // These contexts were cleared above.
37 continue;
38 }
39 cw.worklist->Clear();
40 }
41 ReleaseContextWorklists();
42 }
43
Print()44 void MarkingWorklists::Print() {
45 PrintWorklist("shared", &shared_);
46 PrintWorklist("on_hold", &on_hold_);
47 }
48
CreateContextWorklists(const std::vector<Address> & contexts)49 void MarkingWorklists::CreateContextWorklists(
50 const std::vector<Address>& contexts) {
51 DCHECK(worklists_.empty());
52 DCHECK(context_worklists_.empty());
53 if (contexts.empty()) return;
54 worklists_.reserve(contexts.size());
55 context_worklists_.reserve(contexts.size() + 2);
56 context_worklists_.push_back({kSharedContext, &shared_});
57 context_worklists_.push_back({kOtherContext, &other_});
58 for (Address context : contexts) {
59 MarkingWorklist* worklist = new MarkingWorklist();
60 worklists_.push_back(std::unique_ptr<MarkingWorklist>(worklist));
61 context_worklists_.push_back({context, worklist});
62 }
63 }
64
ReleaseContextWorklists()65 void MarkingWorklists::ReleaseContextWorklists() {
66 context_worklists_.clear();
67 worklists_.clear();
68 }
69
PrintWorklist(const char * worklist_name,MarkingWorklist * worklist)70 void MarkingWorklists::PrintWorklist(const char* worklist_name,
71 MarkingWorklist* worklist) {
72 #ifdef DEBUG
73 std::map<InstanceType, int> count;
74 int total_count = 0;
75 worklist->Iterate([&count, &total_count](HeapObject obj) {
76 ++total_count;
77 count[obj.map().instance_type()]++;
78 });
79 std::vector<std::pair<int, InstanceType>> rank;
80 rank.reserve(count.size());
81 for (const auto& i : count) {
82 rank.emplace_back(i.second, i.first);
83 }
84 std::map<InstanceType, std::string> instance_type_name;
85 #define INSTANCE_TYPE_NAME(name) instance_type_name[name] = #name;
86 INSTANCE_TYPE_LIST(INSTANCE_TYPE_NAME)
87 #undef INSTANCE_TYPE_NAME
88 std::sort(rank.begin(), rank.end(),
89 std::greater<std::pair<int, InstanceType>>());
90 PrintF("Worklist %s: %d\n", worklist_name, total_count);
91 for (auto i : rank) {
92 PrintF(" [%s]: %d\n", instance_type_name[i.second].c_str(), i.first);
93 }
94 #endif
95 }
96
97 const Address MarkingWorklists::Local::kSharedContext;
98 const Address MarkingWorklists::Local::kOtherContext;
99
Local(MarkingWorklists * global)100 MarkingWorklists::Local::Local(MarkingWorklists* global)
101 : on_hold_(global->on_hold()),
102 embedder_(global->embedder()),
103 is_per_context_mode_(false) {
104 if (global->context_worklists().empty()) {
105 MarkingWorklist::Local shared(global->shared());
106 active_ = std::move(shared);
107 active_context_ = kSharedContext;
108 active_owner_ = nullptr;
109 } else {
110 is_per_context_mode_ = true;
111 worklist_by_context_.reserve(global->context_worklists().size());
112 for (auto& cw : global->context_worklists()) {
113 worklist_by_context_[cw.context] =
114 std::make_unique<MarkingWorklist::Local>(cw.worklist);
115 }
116 active_owner_ = worklist_by_context_[kSharedContext].get();
117 active_ = std::move(*active_owner_);
118 active_context_ = kSharedContext;
119 }
120 }
121
~Local()122 MarkingWorklists::Local::~Local() {
123 DCHECK(active_.IsLocalEmpty());
124 if (is_per_context_mode_) {
125 for (auto& cw : worklist_by_context_) {
126 if (cw.first != active_context_) {
127 DCHECK(cw.second->IsLocalEmpty());
128 }
129 }
130 }
131 }
132
Publish()133 void MarkingWorklists::Local::Publish() {
134 active_.Publish();
135 on_hold_.Publish();
136 embedder_.Publish();
137 if (is_per_context_mode_) {
138 for (auto& cw : worklist_by_context_) {
139 if (cw.first != active_context_) {
140 cw.second->Publish();
141 }
142 }
143 }
144 }
145
IsEmpty()146 bool MarkingWorklists::Local::IsEmpty() {
147 // This function checks the on_hold_ worklist, so it works only for the main
148 // thread.
149 if (!active_.IsLocalEmpty() || !on_hold_.IsLocalEmpty() ||
150 !active_.IsGlobalEmpty() || !on_hold_.IsGlobalEmpty()) {
151 return false;
152 }
153 if (!is_per_context_mode_) {
154 return true;
155 }
156 for (auto& cw : worklist_by_context_) {
157 if (cw.first != active_context_ &&
158 !(cw.second->IsLocalEmpty() && cw.second->IsGlobalEmpty())) {
159 SwitchToContext(cw.first, cw.second.get());
160 return false;
161 }
162 }
163 return true;
164 }
165
IsEmbedderEmpty() const166 bool MarkingWorklists::Local::IsEmbedderEmpty() const {
167 return embedder_.IsLocalEmpty() && embedder_.IsGlobalEmpty();
168 }
169
ShareWork()170 void MarkingWorklists::Local::ShareWork() {
171 if (!active_.IsLocalEmpty() && active_.IsGlobalEmpty()) {
172 active_.Publish();
173 }
174 if (is_per_context_mode_ && active_context_ != kSharedContext) {
175 MarkingWorklist::Local* shared = worklist_by_context_[kSharedContext].get();
176 if (!shared->IsLocalEmpty() && shared->IsGlobalEmpty()) {
177 shared->Publish();
178 }
179 }
180 }
181
MergeOnHold()182 void MarkingWorklists::Local::MergeOnHold() {
183 MarkingWorklist::Local* shared =
184 active_context_ == kSharedContext
185 ? &active_
186 : worklist_by_context_[kSharedContext].get();
187 shared->Merge(&on_hold_);
188 }
189
PopContext(HeapObject * object)190 bool MarkingWorklists::Local::PopContext(HeapObject* object) {
191 DCHECK(is_per_context_mode_);
192 // As an optimization we first check only the local segments to avoid locks.
193 for (auto& cw : worklist_by_context_) {
194 if (cw.first != active_context_ && !cw.second->IsLocalEmpty()) {
195 SwitchToContext(cw.first, cw.second.get());
196 return active_.Pop(object);
197 }
198 }
199 // All local segments are empty. Check global segments.
200 for (auto& cw : worklist_by_context_) {
201 if (cw.first != active_context_ && cw.second->Pop(object)) {
202 SwitchToContext(cw.first, cw.second.get());
203 return true;
204 }
205 }
206 // All worklists are empty. Switch to the default shared worklist.
207 SwitchToContext(kSharedContext);
208 return false;
209 }
210
SwitchToContextSlow(Address context)211 Address MarkingWorklists::Local::SwitchToContextSlow(Address context) {
212 const auto& it = worklist_by_context_.find(context);
213 if (V8_UNLIKELY(it == worklist_by_context_.end())) {
214 // This context was created during marking or is not being measured,
215 // so we don't have a specific worklist for it.
216 SwitchToContext(kOtherContext, worklist_by_context_[kOtherContext].get());
217 } else {
218 SwitchToContext(it->first, it->second.get());
219 }
220 return active_context_;
221 }
222
223 } // namespace internal
224 } // namespace v8
225