OLD | NEW |
| (Empty) |
1 // Copyright 2016 The Chromium 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 "bindings/core/v8/ScriptWrappableVisitor.h" | |
6 | |
7 #include "bindings/core/v8/ActiveScriptWrappable.h" | |
8 #include "bindings/core/v8/DOMWrapperWorld.h" | |
9 #include "bindings/core/v8/ScopedPersistent.h" | |
10 #include "bindings/core/v8/ScriptWrappable.h" | |
11 #include "bindings/core/v8/ScriptWrappableVisitorVerifier.h" | |
12 #include "bindings/core/v8/V8AbstractEventListener.h" | |
13 #include "bindings/core/v8/WrapperTypeInfo.h" | |
14 #include "core/dom/DocumentStyleSheetCollection.h" | |
15 #include "core/dom/ElementRareData.h" | |
16 #include "core/dom/NodeListsNodeData.h" | |
17 #include "core/dom/NodeRareData.h" | |
18 #include "core/dom/StyleEngine.h" | |
19 #include "core/dom/shadow/ElementShadow.h" | |
20 #include "core/html/imports/HTMLImportsController.h" | |
21 #include "platform/heap/HeapCompact.h" | |
22 #include "platform/heap/HeapPage.h" | |
23 #include "platform/scheduler/child/web_scheduler.h" | |
24 #include "platform/wtf/AutoReset.h" | |
25 #include "public/platform/Platform.h" | |
26 | |
27 namespace blink { | |
28 | |
29 ScriptWrappableVisitor::~ScriptWrappableVisitor() {} | |
30 | |
31 void ScriptWrappableVisitor::TracePrologue() { | |
32 // This CHECK ensures that wrapper tracing is not started from scopes | |
33 // that forbid GC execution, e.g., constructors. | |
34 CHECK(ThreadState::Current()); | |
35 CHECK(!ThreadState::Current()->IsWrapperTracingForbidden()); | |
36 PerformCleanup(); | |
37 | |
38 CHECK(!tracing_in_progress_); | |
39 CHECK(!should_cleanup_); | |
40 CHECK(headers_to_unmark_.IsEmpty()); | |
41 CHECK(marking_deque_.IsEmpty()); | |
42 CHECK(verifier_deque_.IsEmpty()); | |
43 tracing_in_progress_ = true; | |
44 } | |
45 | |
46 void ScriptWrappableVisitor::EnterFinalPause() { | |
47 CHECK(ThreadState::Current()); | |
48 CHECK(!ThreadState::Current()->IsWrapperTracingForbidden()); | |
49 ActiveScriptWrappableBase::TraceActiveScriptWrappables(isolate_, this); | |
50 } | |
51 | |
52 void ScriptWrappableVisitor::TraceEpilogue() { | |
53 CHECK(ThreadState::Current()); | |
54 CHECK(!ThreadState::Current()->IsWrapperTracingForbidden()); | |
55 DCHECK(marking_deque_.IsEmpty()); | |
56 #if DCHECK_IS_ON() | |
57 ScriptWrappableVisitorVerifier verifier; | |
58 for (auto& marking_data : verifier_deque_) { | |
59 marking_data.TraceWrappers(&verifier); | |
60 } | |
61 #endif | |
62 | |
63 should_cleanup_ = true; | |
64 tracing_in_progress_ = false; | |
65 ScheduleIdleLazyCleanup(); | |
66 } | |
67 | |
68 void ScriptWrappableVisitor::AbortTracing() { | |
69 CHECK(ThreadState::Current()); | |
70 should_cleanup_ = true; | |
71 tracing_in_progress_ = false; | |
72 PerformCleanup(); | |
73 } | |
74 | |
75 size_t ScriptWrappableVisitor::NumberOfWrappersToTrace() { | |
76 CHECK(ThreadState::Current()); | |
77 return marking_deque_.size(); | |
78 } | |
79 | |
80 void ScriptWrappableVisitor::PerformCleanup() { | |
81 if (!should_cleanup_) | |
82 return; | |
83 | |
84 CHECK(!tracing_in_progress_); | |
85 for (auto header : headers_to_unmark_) { | |
86 // Dead objects residing in the marking deque may become invalid due to | |
87 // minor garbage collections and are therefore set to nullptr. We have | |
88 // to skip over such objects. | |
89 if (header) | |
90 header->UnmarkWrapperHeader(); | |
91 } | |
92 | |
93 headers_to_unmark_.clear(); | |
94 marking_deque_.clear(); | |
95 verifier_deque_.clear(); | |
96 should_cleanup_ = false; | |
97 } | |
98 | |
99 void ScriptWrappableVisitor::ScheduleIdleLazyCleanup() { | |
100 // Some threads (e.g. PPAPI thread) don't have a scheduler. | |
101 if (!Platform::Current()->CurrentThread()->Scheduler()) | |
102 return; | |
103 | |
104 if (idle_cleanup_task_scheduled_) | |
105 return; | |
106 | |
107 Platform::Current()->CurrentThread()->Scheduler()->PostIdleTask( | |
108 BLINK_FROM_HERE, WTF::Bind(&ScriptWrappableVisitor::PerformLazyCleanup, | |
109 WTF::Unretained(this))); | |
110 idle_cleanup_task_scheduled_ = true; | |
111 } | |
112 | |
113 void ScriptWrappableVisitor::PerformLazyCleanup(double deadline_seconds) { | |
114 idle_cleanup_task_scheduled_ = false; | |
115 | |
116 if (!should_cleanup_) | |
117 return; | |
118 | |
119 TRACE_EVENT1("blink_gc,devtools.timeline", | |
120 "ScriptWrappableVisitor::performLazyCleanup", | |
121 "idleDeltaInSeconds", | |
122 deadline_seconds - MonotonicallyIncreasingTime()); | |
123 | |
124 const int kDeadlineCheckInterval = 2500; | |
125 int processed_wrapper_count = 0; | |
126 for (auto it = headers_to_unmark_.rbegin(); | |
127 it != headers_to_unmark_.rend();) { | |
128 auto header = *it; | |
129 // Dead objects residing in the marking deque may become invalid due to | |
130 // minor garbage collections and are therefore set to nullptr. We have | |
131 // to skip over such objects. | |
132 if (header) | |
133 header->UnmarkWrapperHeader(); | |
134 | |
135 ++it; | |
136 headers_to_unmark_.pop_back(); | |
137 | |
138 processed_wrapper_count++; | |
139 if (processed_wrapper_count % kDeadlineCheckInterval == 0) { | |
140 if (deadline_seconds <= MonotonicallyIncreasingTime()) { | |
141 ScheduleIdleLazyCleanup(); | |
142 return; | |
143 } | |
144 } | |
145 } | |
146 | |
147 // Unmarked all headers. | |
148 CHECK(headers_to_unmark_.IsEmpty()); | |
149 marking_deque_.clear(); | |
150 verifier_deque_.clear(); | |
151 should_cleanup_ = false; | |
152 } | |
153 | |
154 void ScriptWrappableVisitor::RegisterV8Reference( | |
155 const std::pair<void*, void*>& internal_fields) { | |
156 if (!tracing_in_progress_) { | |
157 return; | |
158 } | |
159 | |
160 WrapperTypeInfo* wrapper_type_info = | |
161 reinterpret_cast<WrapperTypeInfo*>(internal_fields.first); | |
162 if (wrapper_type_info->gin_embedder != gin::GinEmbedder::kEmbedderBlink) { | |
163 return; | |
164 } | |
165 DCHECK(wrapper_type_info->wrapper_class_id == WrapperTypeInfo::kNodeClassId || | |
166 wrapper_type_info->wrapper_class_id == | |
167 WrapperTypeInfo::kObjectClassId); | |
168 | |
169 ScriptWrappable* script_wrappable = | |
170 reinterpret_cast<ScriptWrappable*>(internal_fields.second); | |
171 | |
172 wrapper_type_info->TraceWrappers(this, script_wrappable); | |
173 } | |
174 | |
175 void ScriptWrappableVisitor::RegisterV8References( | |
176 const std::vector<std::pair<void*, void*>>& | |
177 internal_fields_of_potential_wrappers) { | |
178 CHECK(ThreadState::Current()); | |
179 // TODO(hlopko): Visit the vector in the V8 instead of passing it over if | |
180 // there is no performance impact | |
181 for (auto& pair : internal_fields_of_potential_wrappers) { | |
182 RegisterV8Reference(pair); | |
183 } | |
184 } | |
185 | |
186 bool ScriptWrappableVisitor::AdvanceTracing( | |
187 double deadline_in_ms, | |
188 v8::EmbedderHeapTracer::AdvanceTracingActions actions) { | |
189 // Do not drain the marking deque in a state where we can generally not | |
190 // perform a GC. This makes sure that TraceTraits and friends find | |
191 // themselves in a well-defined environment, e.g., properly set up vtables. | |
192 CHECK(ThreadState::Current()); | |
193 CHECK(!ThreadState::Current()->IsWrapperTracingForbidden()); | |
194 CHECK(tracing_in_progress_); | |
195 WTF::AutoReset<bool>(&advancing_tracing_, true); | |
196 while (actions.force_completion == | |
197 v8::EmbedderHeapTracer::ForceCompletionAction::FORCE_COMPLETION || | |
198 WTF::MonotonicallyIncreasingTimeMS() < deadline_in_ms) { | |
199 if (marking_deque_.IsEmpty()) { | |
200 return false; | |
201 } | |
202 marking_deque_.TakeFirst().TraceWrappers(this); | |
203 } | |
204 return true; | |
205 } | |
206 | |
207 bool ScriptWrappableVisitor::MarkWrapperHeader(HeapObjectHeader* header) const { | |
208 if (header->IsWrapperHeaderMarked()) | |
209 return false; | |
210 | |
211 // Verify that no compactable & movable objects are slated for | |
212 // lazy unmarking. | |
213 DCHECK(!HeapCompact::IsCompactableArena( | |
214 PageFromObject(header)->Arena()->ArenaIndex())); | |
215 header->MarkWrapperHeader(); | |
216 headers_to_unmark_.push_back(header); | |
217 return true; | |
218 } | |
219 | |
220 void ScriptWrappableVisitor::MarkWrappersInAllWorlds( | |
221 const ScriptWrappable* script_wrappable) const { | |
222 DOMWrapperWorld::MarkWrappersInAllWorlds( | |
223 const_cast<ScriptWrappable*>(script_wrappable), this); | |
224 } | |
225 | |
226 void ScriptWrappableVisitor::WriteBarrier( | |
227 v8::Isolate* isolate, | |
228 const void* src_object, | |
229 const TraceWrapperV8Reference<v8::Value>* dst_object) { | |
230 if (!src_object || !dst_object || dst_object->IsEmpty()) { | |
231 return; | |
232 } | |
233 // We only require a write barrier if |srcObject| is already marked. Note | |
234 // that this implicitly disables the write barrier when the GC is not | |
235 // active as object will not be marked in this case. | |
236 if (!HeapObjectHeader::FromPayload(src_object)->IsWrapperHeaderMarked()) { | |
237 return; | |
238 } | |
239 CurrentVisitor(isolate)->MarkWrapper( | |
240 &(const_cast<TraceWrapperV8Reference<v8::Value>*>(dst_object)->Get())); | |
241 } | |
242 | |
243 void ScriptWrappableVisitor::WriteBarrier( | |
244 v8::Isolate* isolate, | |
245 const v8::Persistent<v8::Object>* dst_object) { | |
246 if (!dst_object || dst_object->IsEmpty()) { | |
247 return; | |
248 } | |
249 CurrentVisitor(isolate)->MarkWrapper(&(dst_object->As<v8::Value>())); | |
250 } | |
251 | |
252 void ScriptWrappableVisitor::TraceWrappers( | |
253 const TraceWrapperV8Reference<v8::Value>& traced_wrapper) const { | |
254 MarkWrapper( | |
255 &(const_cast<TraceWrapperV8Reference<v8::Value>&>(traced_wrapper).Get())); | |
256 } | |
257 | |
258 void ScriptWrappableVisitor::MarkWrapper( | |
259 const v8::PersistentBase<v8::Value>* handle) const { | |
260 // The write barrier may try to mark a wrapper because cleanup is still | |
261 // delayed. Bail out in this case. We also allow unconditional marking which | |
262 // requires us to bail out here when tracing is not in progress. | |
263 if (!tracing_in_progress_ || handle->IsEmpty()) | |
264 return; | |
265 handle->RegisterExternalReference(isolate_); | |
266 } | |
267 | |
268 void ScriptWrappableVisitor::DispatchTraceWrappers( | |
269 const TraceWrapperBase* wrapper_base) const { | |
270 wrapper_base->TraceWrappers(this); | |
271 } | |
272 | |
273 void ScriptWrappableVisitor::InvalidateDeadObjectsInMarkingDeque() { | |
274 for (auto it = marking_deque_.begin(); it != marking_deque_.end(); ++it) { | |
275 auto& marking_data = *it; | |
276 if (marking_data.ShouldBeInvalidated()) { | |
277 marking_data.Invalidate(); | |
278 } | |
279 } | |
280 for (auto it = verifier_deque_.begin(); it != verifier_deque_.end(); ++it) { | |
281 auto& marking_data = *it; | |
282 if (marking_data.ShouldBeInvalidated()) { | |
283 marking_data.Invalidate(); | |
284 } | |
285 } | |
286 for (auto it = headers_to_unmark_.begin(); it != headers_to_unmark_.end(); | |
287 ++it) { | |
288 auto header = *it; | |
289 if (header && !header->IsMarked()) { | |
290 *it = nullptr; | |
291 } | |
292 } | |
293 } | |
294 | |
295 void ScriptWrappableVisitor::InvalidateDeadObjectsInMarkingDeque( | |
296 v8::Isolate* isolate) { | |
297 ScriptWrappableVisitor* script_wrappable_visitor = | |
298 V8PerIsolateData::From(isolate)->GetScriptWrappableVisitor(); | |
299 if (script_wrappable_visitor) | |
300 script_wrappable_visitor->InvalidateDeadObjectsInMarkingDeque(); | |
301 } | |
302 | |
303 void ScriptWrappableVisitor::PerformCleanup(v8::Isolate* isolate) { | |
304 ScriptWrappableVisitor* script_wrappable_visitor = | |
305 V8PerIsolateData::From(isolate)->GetScriptWrappableVisitor(); | |
306 if (script_wrappable_visitor) | |
307 script_wrappable_visitor->PerformCleanup(); | |
308 } | |
309 | |
310 WrapperVisitor* ScriptWrappableVisitor::CurrentVisitor(v8::Isolate* isolate) { | |
311 return V8PerIsolateData::From(isolate)->GetScriptWrappableVisitor(); | |
312 } | |
313 | |
314 } // namespace blink | |
OLD | NEW |