• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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