| 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 |