| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "config.h" | 5 #include "config.h" |
| 6 #include "bindings/core/v8/ScriptStreamer.h" | 6 #include "bindings/core/v8/ScriptStreamer.h" |
| 7 | 7 |
| 8 #include "bindings/core/v8/ScriptStreamerThread.h" | 8 #include "bindings/core/v8/ScriptStreamerThread.h" |
| 9 #include "bindings/core/v8/V8ScriptRunner.h" | 9 #include "bindings/core/v8/V8ScriptRunner.h" |
| 10 #include "core/dom/Document.h" | 10 #include "core/dom/Document.h" |
| 11 #include "core/dom/Element.h" | 11 #include "core/dom/Element.h" |
| 12 #include "core/dom/PendingScript.h" | 12 #include "core/dom/PendingScript.h" |
| 13 #include "core/fetch/ScriptResource.h" | 13 #include "core/fetch/ScriptResource.h" |
| 14 #include "core/frame/Settings.h" | 14 #include "core/frame/Settings.h" |
| 15 #include "platform/SharedBuffer.h" | 15 #include "platform/SharedBuffer.h" |
| 16 #include "platform/TraceEvent.h" | |
| 17 #include "public/platform/Platform.h" | 16 #include "public/platform/Platform.h" |
| 18 #include "wtf/MainThread.h" | 17 #include "wtf/MainThread.h" |
| 19 #include "wtf/text/TextEncodingRegistry.h" | 18 #include "wtf/text/TextEncodingRegistry.h" |
| 20 | 19 |
| 21 namespace blink { | 20 namespace blink { |
| 22 | 21 |
| 23 // For passing data between the main thread (producer) and the streamer thread | 22 // For passing data between the main thread (producer) and the streamer thread |
| 24 // (consumer). The main thread prepares the data (copies it from Resource) and | 23 // (consumer). The main thread prepares the data (copies it from Resource) and |
| 25 // the streamer thread feeds it to V8. | 24 // the streamer thread feeds it to V8. |
| 26 class SourceStreamDataQueue { | 25 class SourceStreamDataQueue { |
| (...skipping 191 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 218 void ScriptStreamer::startStreaming(PendingScript& script, Settings* settings, S
criptState* scriptState, PendingScript::Type scriptType) | 217 void ScriptStreamer::startStreaming(PendingScript& script, Settings* settings, S
criptState* scriptState, PendingScript::Type scriptType) |
| 219 { | 218 { |
| 220 // We don't yet know whether the script will really be streamed. E.g., | 219 // We don't yet know whether the script will really be streamed. E.g., |
| 221 // suppressing streaming for short scripts is done later. Record only the | 220 // suppressing streaming for short scripts is done later. Record only the |
| 222 // sure negative cases here. | 221 // sure negative cases here. |
| 223 bool startedStreaming = startStreamingInternal(script, settings, scriptState
, scriptType); | 222 bool startedStreaming = startStreamingInternal(script, settings, scriptState
, scriptType); |
| 224 if (!startedStreaming) | 223 if (!startedStreaming) |
| 225 blink::Platform::current()->histogramEnumeration(startedStreamingHistogr
amName(scriptType), 0, 2); | 224 blink::Platform::current()->histogramEnumeration(startedStreamingHistogr
amName(scriptType), 0, 2); |
| 226 } | 225 } |
| 227 | 226 |
| 228 void ScriptStreamer::streamingCompleteOnBackgroundThread() | 227 void ScriptStreamer::streamingComplete() |
| 229 { | 228 { |
| 230 ASSERT(!isMainThread()); | 229 ASSERT(isMainThread()); |
| 231 MutexLocker locker(m_mutex); | 230 // It's possible that the corresponding Resource was deleted before V8 |
| 231 // finished streaming. In that case, the data or the notification is not |
| 232 // needed. In addition, if the streaming is suppressed, the non-streaming |
| 233 // code path will resume after the resource has loaded, before the |
| 234 // background task finishes. |
| 235 if (m_detached || m_streamingSuppressed) { |
| 236 deref(); |
| 237 return; |
| 238 } |
| 239 |
| 240 // We have now streamed the whole script to V8 and it has parsed the |
| 241 // script. We're ready for the next step: compiling and executing the |
| 242 // script. |
| 232 m_parsingFinished = true; | 243 m_parsingFinished = true; |
| 233 if (shouldBlockMainThread()) { | 244 |
| 234 // Normally, the main thread is waiting at this point, but it can also | 245 notifyFinishedToClient(); |
| 235 // happen that the load is not yet finished (e.g., a parse error). In | 246 |
| 236 // that case, notifyFinished will be called eventually and it will not | 247 // The background thread no longer holds an implicit reference. |
| 237 // wait on m_parsingFinishedCondition. | 248 deref(); |
| 238 m_parsingFinishedCondition.signal(); | |
| 239 } else { | |
| 240 callOnMainThread(WTF::bind(&ScriptStreamer::streamingComplete, this)); | |
| 241 } | |
| 242 } | 249 } |
| 243 | 250 |
| 244 void ScriptStreamer::cancel() | 251 void ScriptStreamer::cancel() |
| 245 { | 252 { |
| 246 ASSERT(isMainThread()); | 253 ASSERT(isMainThread()); |
| 247 // The upper layer doesn't need the script any more, but streaming might | 254 // The upper layer doesn't need the script any more, but streaming might |
| 248 // still be ongoing. Tell SourceStream to try to cancel it whenever it gets | 255 // still be ongoing. Tell SourceStream to try to cancel it whenever it gets |
| 249 // the control the next time. It can also be that V8 has already completed | 256 // the control the next time. It can also be that V8 has already completed |
| 250 // its operations and streamingComplete will be called soon. | 257 // its operations and streamingComplete will be called soon. |
| 251 m_detached = true; | 258 m_detached = true; |
| 252 m_resource = 0; | 259 m_resource = 0; |
| 253 m_stream->cancel(); | 260 m_stream->cancel(); |
| 254 } | 261 } |
| 255 | 262 |
| 256 void ScriptStreamer::suppressStreaming() | 263 void ScriptStreamer::suppressStreaming() |
| 257 { | 264 { |
| 258 MutexLocker locker(m_mutex); | |
| 259 ASSERT(!m_parsingFinished); | 265 ASSERT(!m_parsingFinished); |
| 260 ASSERT(!m_loadingFinished); | 266 ASSERT(!m_loadingFinished); |
| 261 m_streamingSuppressed = true; | 267 m_streamingSuppressed = true; |
| 262 } | 268 } |
| 263 | 269 |
| 264 void ScriptStreamer::notifyAppendData(ScriptResource* resource) | 270 void ScriptStreamer::notifyAppendData(ScriptResource* resource) |
| 265 { | 271 { |
| 266 ASSERT(isMainThread()); | 272 ASSERT(isMainThread()); |
| 267 ASSERT(m_resource == resource); | 273 ASSERT(m_resource == resource); |
| 268 if (m_streamingSuppressed) | 274 if (m_streamingSuppressed) |
| (...skipping 12 matching lines...) Expand all Loading... |
| 281 // new task shouldn't be queued before the running task completes, | 287 // new task shouldn't be queued before the running task completes, |
| 282 // because the running task can block and wait for data from the | 288 // because the running task can block and wait for data from the |
| 283 // network. | 289 // network. |
| 284 suppressStreaming(); | 290 suppressStreaming(); |
| 285 blink::Platform::current()->histogramEnumeration(histogramName, 0, 2
); | 291 blink::Platform::current()->histogramEnumeration(histogramName, 0, 2
); |
| 286 return; | 292 return; |
| 287 } | 293 } |
| 288 ASSERT(m_task); | 294 ASSERT(m_task); |
| 289 // ScriptStreamer needs to stay alive as long as the background task is | 295 // ScriptStreamer needs to stay alive as long as the background task is |
| 290 // running. This is taken care of with a manual ref() & deref() pair; | 296 // running. This is taken care of with a manual ref() & deref() pair; |
| 291 // the corresponding deref() is in streamingComplete or in | 297 // the corresponding deref() is in streamingComplete. |
| 292 // notifyFinished. | |
| 293 ref(); | 298 ref(); |
| 294 ScriptStreamingTask* task = new ScriptStreamingTask(m_task.release(), th
is); | 299 ScriptStreamingTask* task = new ScriptStreamingTask(m_task.release(), th
is); |
| 295 ScriptStreamerThread::shared()->postTask(task); | 300 ScriptStreamerThread::shared()->postTask(task); |
| 296 blink::Platform::current()->histogramEnumeration(histogramName, 1, 2); | 301 blink::Platform::current()->histogramEnumeration(histogramName, 1, 2); |
| 297 } | 302 } |
| 298 m_stream->didReceiveData(); | 303 m_stream->didReceiveData(); |
| 299 } | 304 } |
| 300 | 305 |
| 301 void ScriptStreamer::notifyFinished(Resource* resource) | 306 void ScriptStreamer::notifyFinished(Resource* resource) |
| 302 { | 307 { |
| 303 ASSERT(isMainThread()); | 308 ASSERT(isMainThread()); |
| 304 ASSERT(m_resource == resource); | 309 ASSERT(m_resource == resource); |
| 305 // A special case: empty and small scripts. We didn't receive enough data to | 310 // A special case: empty and small scripts. We didn't receive enough data to |
| 306 // start the streaming before this notification. In that case, there won't | 311 // start the streaming before this notification. In that case, there won't |
| 307 // be a "parsing complete" notification either, and we should not wait for | 312 // be a "parsing complete" notification either, and we should not wait for |
| 308 // it. | 313 // it. |
| 309 if (!m_haveEnoughDataForStreaming) { | 314 if (!m_haveEnoughDataForStreaming) { |
| 310 const char* histogramName = startedStreamingHistogramName(m_scriptType); | 315 const char* histogramName = startedStreamingHistogramName(m_scriptType); |
| 311 blink::Platform::current()->histogramEnumeration(histogramName, 0, 2); | 316 blink::Platform::current()->histogramEnumeration(histogramName, 0, 2); |
| 312 suppressStreaming(); | 317 suppressStreaming(); |
| 313 } | 318 } |
| 314 m_stream->didFinishLoading(); | 319 m_stream->didFinishLoading(); |
| 315 m_loadingFinished = true; | 320 m_loadingFinished = true; |
| 316 | |
| 317 if (shouldBlockMainThread()) { | |
| 318 // Make the main thead wait until the streaming is complete, to make | |
| 319 // sure that the script gets the main thread's attention as early as | |
| 320 // possible (for possible compiling, if the client wants to do it | |
| 321 // right away). Note that blocking here is not any worse than the | |
| 322 // non-streaming code path where the main thread eventually blocks | |
| 323 // to parse the script. | |
| 324 TRACE_EVENT0("v8", "v8.mainThreadWaitingForParserThread"); | |
| 325 MutexLocker locker(m_mutex); | |
| 326 if (!isFinished()) { | |
| 327 m_parsingFinishedCondition.wait(m_mutex); | |
| 328 } | |
| 329 ASSERT(isFinished()); | |
| 330 } | |
| 331 | |
| 332 notifyFinishedToClient(); | 321 notifyFinishedToClient(); |
| 333 | |
| 334 if (shouldBlockMainThread() && m_parsingFinished) { | |
| 335 // streamingComplete won't be called, so do the ramp-down work | |
| 336 // here. Since m_parsingFinished is true, we know that there was a | |
| 337 // background task and we need to deref(). | |
| 338 deref(); | |
| 339 } | |
| 340 } | 322 } |
| 341 | 323 |
| 342 ScriptStreamer::ScriptStreamer(ScriptResource* resource, v8::ScriptCompiler::Str
eamedSource::Encoding encoding, PendingScript::Type scriptType, ScriptStreamingM
ode mode) | 324 ScriptStreamer::ScriptStreamer(ScriptResource* resource, v8::ScriptCompiler::Str
eamedSource::Encoding encoding, PendingScript::Type scriptType) |
| 343 : m_resource(resource) | 325 : m_resource(resource) |
| 344 , m_detached(false) | 326 , m_detached(false) |
| 345 , m_stream(new SourceStream(this)) | 327 , m_stream(new SourceStream(this)) |
| 346 , m_source(m_stream, encoding) // m_source takes ownership of m_stream. | 328 , m_source(m_stream, encoding) // m_source takes ownership of m_stream. |
| 347 , m_client(0) | 329 , m_client(0) |
| 348 , m_loadingFinished(false) | 330 , m_loadingFinished(false) |
| 349 , m_parsingFinished(false) | 331 , m_parsingFinished(false) |
| 350 , m_haveEnoughDataForStreaming(false) | 332 , m_haveEnoughDataForStreaming(false) |
| 351 , m_streamingSuppressed(false) | 333 , m_streamingSuppressed(false) |
| 352 , m_scriptType(scriptType) | 334 , m_scriptType(scriptType) |
| 353 , m_scriptStreamingMode(mode) | |
| 354 { | 335 { |
| 355 } | 336 } |
| 356 | 337 |
| 357 void ScriptStreamer::streamingComplete() | |
| 358 { | |
| 359 // The background task is completed; do the necessary ramp-down in the main | |
| 360 // thread. | |
| 361 ASSERT(isMainThread()); | |
| 362 // In the blocking mode, the ramp-down is done in notifyFinished. | |
| 363 ASSERT(!shouldBlockMainThread()); | |
| 364 | |
| 365 // It's possible that the corresponding Resource was deleted before V8 | |
| 366 // finished streaming. In that case, the data or the notification is not | |
| 367 // needed. In addition, if the streaming is suppressed, the non-streaming | |
| 368 // code path will resume after the resource has loaded, before the | |
| 369 // background task finishes. | |
| 370 if (m_detached || m_streamingSuppressed) { | |
| 371 deref(); | |
| 372 return; | |
| 373 } | |
| 374 | |
| 375 // We have now streamed the whole script to V8 and it has parsed the | |
| 376 // script. We're ready for the next step: compiling and executing the | |
| 377 // script. | |
| 378 notifyFinishedToClient(); | |
| 379 | |
| 380 // The background thread no longer holds an implicit reference. | |
| 381 deref(); | |
| 382 } | |
| 383 | |
| 384 void ScriptStreamer::notifyFinishedToClient() | 338 void ScriptStreamer::notifyFinishedToClient() |
| 385 { | 339 { |
| 386 ASSERT(isMainThread()); | 340 ASSERT(isMainThread()); |
| 387 // Usually, the loading will be finished first, and V8 will still need some | 341 // Usually, the loading will be finished first, and V8 will still need some |
| 388 // time to catch up. But the other way is possible too: if V8 detects a | 342 // time to catch up. But the other way is possible too: if V8 detects a |
| 389 // parse error, the V8 side can complete before loading has finished. Send | 343 // parse error, the V8 side can complete before loading has finished. Send |
| 390 // the notification after both loading and V8 side operations have | 344 // the notification after both loading and V8 side operations have |
| 391 // completed. Here we also check that we have a client: it can happen that a | 345 // completed. Here we also check that we have a client: it can happen that a |
| 392 // function calling notifyFinishedToClient was already scheduled in the task | 346 // function calling notifyFinishedToClient was already scheduled in the task |
| 393 // queue and the upper layer decided that it's not interested in the script | 347 // queue and the upper layer decided that it's not interested in the script |
| 394 // and called removeClient. | 348 // and called removeClient. |
| 395 MutexLocker locker(m_mutex); | |
| 396 if (isFinished() && m_client) | 349 if (isFinished() && m_client) |
| 397 m_client->notifyFinished(m_resource); | 350 m_client->notifyFinished(m_resource); |
| 398 } | 351 } |
| 399 | 352 |
| 400 const char* ScriptStreamer::startedStreamingHistogramName(PendingScript::Type sc
riptType) | 353 const char* ScriptStreamer::startedStreamingHistogramName(PendingScript::Type sc
riptType) |
| 401 { | 354 { |
| 402 switch (scriptType) { | 355 switch (scriptType) { |
| 403 case PendingScript::ParsingBlocking: | 356 case PendingScript::ParsingBlocking: |
| 404 return "WebCore.Scripts.ParsingBlocking.StartedStreaming"; | 357 return "WebCore.Scripts.ParsingBlocking.StartedStreaming"; |
| 405 break; | 358 break; |
| 406 case PendingScript::Deferred: | 359 case PendingScript::Deferred: |
| 407 return "WebCore.Scripts.Deferred.StartedStreaming"; | 360 return "WebCore.Scripts.Deferred.StartedStreaming"; |
| 408 break; | 361 break; |
| 409 case PendingScript::Async: | 362 case PendingScript::Async: |
| 410 return "WebCore.Scripts.Async.StartedStreaming"; | 363 return "WebCore.Scripts.Async.StartedStreaming"; |
| 411 break; | 364 break; |
| 412 default: | 365 default: |
| 413 ASSERT_NOT_REACHED(); | 366 ASSERT_NOT_REACHED(); |
| 414 break; | 367 break; |
| 415 } | 368 } |
| 416 return 0; | 369 return 0; |
| 417 } | 370 } |
| 418 | 371 |
| 419 bool ScriptStreamer::startStreamingInternal(PendingScript& script, Settings* set
tings, ScriptState* scriptState, PendingScript::Type scriptType) | 372 bool ScriptStreamer::startStreamingInternal(PendingScript& script, Settings* set
tings, ScriptState* scriptState, PendingScript::Type scriptType) |
| 420 { | 373 { |
| 421 ASSERT(isMainThread()); | 374 ASSERT(isMainThread()); |
| 422 if (!settings || !settings->v8ScriptStreamingEnabled()) | 375 if (!settings || !settings->v8ScriptStreamingEnabled()) |
| 423 return false; | 376 return false; |
| 424 if (settings->v8ScriptStreamingMode() == ScriptStreamingModeOnlyAsyncAndDefe
r | |
| 425 && scriptType == PendingScript::ParsingBlocking) | |
| 426 return false; | |
| 427 | |
| 428 ScriptResource* resource = script.resource(); | 377 ScriptResource* resource = script.resource(); |
| 429 ASSERT(!resource->isLoaded()); | 378 ASSERT(!resource->isLoaded()); |
| 430 if (!resource->url().protocolIsInHTTPFamily()) | 379 if (!resource->url().protocolIsInHTTPFamily()) |
| 431 return false; | 380 return false; |
| 432 if (resource->resourceToRevalidate()) { | 381 if (resource->resourceToRevalidate()) { |
| 433 // This happens e.g., during reloads. We're actually not going to load | 382 // This happens e.g., during reloads. We're actually not going to load |
| 434 // the current Resource of the PendingScript but switch to another | 383 // the current Resource of the PendingScript but switch to another |
| 435 // Resource -> don't stream. | 384 // Resource -> don't stream. |
| 436 return false; | 385 return false; |
| 437 } | 386 } |
| (...skipping 21 matching lines...) Expand all Loading... |
| 459 return false; | 408 return false; |
| 460 } | 409 } |
| 461 | 410 |
| 462 if (!scriptState->contextIsValid()) | 411 if (!scriptState->contextIsValid()) |
| 463 return false; | 412 return false; |
| 464 ScriptState::Scope scope(scriptState); | 413 ScriptState::Scope scope(scriptState); |
| 465 | 414 |
| 466 // The Resource might go out of scope if the script is no longer needed. We | 415 // The Resource might go out of scope if the script is no longer needed. We |
| 467 // will soon call PendingScript::setStreamer, which makes the PendingScript | 416 // will soon call PendingScript::setStreamer, which makes the PendingScript |
| 468 // notify the ScriptStreamer when it is destroyed. | 417 // notify the ScriptStreamer when it is destroyed. |
| 469 RefPtr<ScriptStreamer> streamer = adoptRef(new ScriptStreamer(resource, enco
ding, scriptType, settings->v8ScriptStreamingMode())); | 418 RefPtr<ScriptStreamer> streamer = adoptRef(new ScriptStreamer(resource, enco
ding, scriptType)); |
| 470 | 419 |
| 471 // Decide what kind of cached data we should produce while streaming. By | 420 // Decide what kind of cached data we should produce while streaming. By |
| 472 // default, we generate the parser cache for streamed scripts, to emulate | 421 // default, we generate the parser cache for streamed scripts, to emulate |
| 473 // the non-streaming behavior (see V8ScriptRunner::compileScript). | 422 // the non-streaming behavior (see V8ScriptRunner::compileScript). |
| 474 v8::ScriptCompiler::CompileOptions compileOption = v8::ScriptCompiler::kProd
uceParserCache; | 423 v8::ScriptCompiler::CompileOptions compileOption = v8::ScriptCompiler::kProd
uceParserCache; |
| 475 if (settings->v8CacheOptions() == V8CacheOptionsCode) | 424 if (settings->v8CacheOptions() == V8CacheOptionsCode) |
| 476 compileOption = v8::ScriptCompiler::kProduceCodeCache; | 425 compileOption = v8::ScriptCompiler::kProduceCodeCache; |
| 477 v8::ScriptCompiler::ScriptStreamingTask* scriptStreamingTask = v8::ScriptCom
piler::StartStreamingScript(scriptState->isolate(), &(streamer->m_source), compi
leOption); | 426 v8::ScriptCompiler::ScriptStreamingTask* scriptStreamingTask = v8::ScriptCom
piler::StartStreamingScript(scriptState->isolate(), &(streamer->m_source), compi
leOption); |
| 478 if (scriptStreamingTask) { | 427 if (scriptStreamingTask) { |
| 479 streamer->m_task = adoptPtr(scriptStreamingTask); | 428 streamer->m_task = adoptPtr(scriptStreamingTask); |
| 480 script.setStreamer(streamer.release()); | 429 script.setStreamer(streamer.release()); |
| 481 return true; | 430 return true; |
| 482 } | 431 } |
| 483 // Otherwise, V8 cannot stream the script. | 432 // Otherwise, V8 cannot stream the script. |
| 484 return false; | 433 return false; |
| 485 } | 434 } |
| 486 | 435 |
| 487 } // namespace blink | 436 } // namespace blink |
| OLD | NEW |