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