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" |
| (...skipping 206 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) | 217 void ScriptStreamer::startStreaming(PendingScript& script, Settings* settings, S criptState* scriptState, PendingScript::Type scriptType) |
| 218 { | 218 { |
| 219 // 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., |
| 220 // suppressing streaming for short scripts is done later. Record only the | 220 // suppressing streaming for short scripts is done later. Record only the |
| 221 // sure negative cases here. | 221 // sure negative cases here. |
| 222 bool startedStreaming = startStreamingInternal(script, settings, scriptState , scriptType); | 222 bool startedStreaming = startStreamingInternal(script, settings, scriptState , scriptType); |
| 223 if (!startedStreaming) | 223 if (!startedStreaming) |
| 224 blink::Platform::current()->histogramEnumeration(startedStreamingHistogr amName(scriptType), 0, 2); | 224 blink::Platform::current()->histogramEnumeration(startedStreamingHistogr amName(scriptType), 0, 2); |
| 225 } | 225 } |
| 226 | 226 |
| 227 void ScriptStreamer::streamingComplete() | 227 void ScriptStreamer::streamingCompleteOnBackgroundThread() |
| 228 { | 228 { |
| 229 ASSERT(isMainThread()); | 229 ASSERT(!isMainThread()); |
| 230 // It's possible that the corresponding Resource was deleted before V8 | 230 MutexLocker locker(m_mutex); |
| 231 // finished streaming. In that case, the data or the notification is not | 231 m_parsingFinished = true; |
| 232 // needed. In addition, if the streaming is suppressed, the non-streaming | 232 if (m_blockMainThreadAfterLoading) { |
| 233 // code path will resume after the resource has loaded, before the | 233 // Normally, the main thread is waiting at this point, but it can also |
| 234 // background task finishes. | 234 // happen that the load is not yet finished (e.g., a parse error). In |
| 235 if (m_detached || m_streamingSuppressed) { | 235 // that case, notifyFinished will be called eventually and it will not |
| 236 deref(); | 236 // wait on m_parsingFinishedCondition. |
| 237 return; | 237 m_parsingFinishedCondition.signal(); |
| 238 } else { | |
| 239 callOnMainThread(WTF::bind(&ScriptStreamer::streamingComplete, this)); | |
| 238 } | 240 } |
| 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 } | 241 } |
| 250 | 242 |
| 251 void ScriptStreamer::cancel() | 243 void ScriptStreamer::cancel() |
| 252 { | 244 { |
| 253 ASSERT(isMainThread()); | 245 ASSERT(isMainThread()); |
| 254 // The upper layer doesn't need the script any more, but streaming might | 246 // 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 | 247 // 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 | 248 // the control the next time. It can also be that V8 has already completed |
| 257 // its operations and streamingComplete will be called soon. | 249 // its operations and streamingComplete will be called soon. |
| 258 m_detached = true; | 250 m_detached = true; |
| 259 m_resource = 0; | 251 m_resource = 0; |
| 260 m_stream->cancel(); | 252 m_stream->cancel(); |
| 261 } | 253 } |
| 262 | 254 |
| 263 void ScriptStreamer::suppressStreaming() | 255 void ScriptStreamer::suppressStreaming() |
| 264 { | 256 { |
| 257 MutexLocker locker(m_mutex); | |
| 265 ASSERT(!m_parsingFinished); | 258 ASSERT(!m_parsingFinished); |
| 266 ASSERT(!m_loadingFinished); | 259 ASSERT(!m_loadingFinished); |
| 267 m_streamingSuppressed = true; | 260 m_streamingSuppressed = true; |
| 268 } | 261 } |
| 269 | 262 |
| 270 void ScriptStreamer::notifyAppendData(ScriptResource* resource) | 263 void ScriptStreamer::notifyAppendData(ScriptResource* resource) |
| 271 { | 264 { |
| 272 ASSERT(isMainThread()); | 265 ASSERT(isMainThread()); |
| 273 ASSERT(m_resource == resource); | 266 ASSERT(m_resource == resource); |
| 274 if (m_streamingSuppressed) | 267 if (m_streamingSuppressed) |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 291 // network. At the moment we are only streaming parser blocking | 284 // network. At the moment we are only streaming parser blocking |
| 292 // scripts, but this code can still be hit when multiple frames are | 285 // scripts, but this code can still be hit when multiple frames are |
| 293 // loading simultaneously. | 286 // loading simultaneously. |
| 294 suppressStreaming(); | 287 suppressStreaming(); |
| 295 blink::Platform::current()->histogramEnumeration(histogramName, 0, 2 ); | 288 blink::Platform::current()->histogramEnumeration(histogramName, 0, 2 ); |
| 296 return; | 289 return; |
| 297 } | 290 } |
| 298 ASSERT(m_task); | 291 ASSERT(m_task); |
| 299 // ScriptStreamer needs to stay alive as long as the background task is | 292 // ScriptStreamer needs to stay alive as long as the background task is |
| 300 // running. This is taken care of with a manual ref() & deref() pair; | 293 // running. This is taken care of with a manual ref() & deref() pair; |
| 301 // the corresponding deref() is in streamingComplete. | 294 // the corresponding deref() is in streamingComplete or in |
| 295 // notifyFinished. | |
| 302 ref(); | 296 ref(); |
| 303 ScriptStreamingTask* task = new ScriptStreamingTask(m_task.release(), th is); | 297 ScriptStreamingTask* task = new ScriptStreamingTask(m_task.release(), th is); |
| 304 ScriptStreamerThread::shared()->postTask(task); | 298 ScriptStreamerThread::shared()->postTask(task); |
| 305 blink::Platform::current()->histogramEnumeration(histogramName, 1, 2); | 299 blink::Platform::current()->histogramEnumeration(histogramName, 1, 2); |
| 306 } | 300 } |
| 307 m_stream->didReceiveData(); | 301 m_stream->didReceiveData(); |
| 308 } | 302 } |
| 309 | 303 |
| 310 void ScriptStreamer::notifyFinished(Resource* resource) | 304 void ScriptStreamer::notifyFinished(Resource* resource) |
| 311 { | 305 { |
| 312 ASSERT(isMainThread()); | 306 ASSERT(isMainThread()); |
| 313 ASSERT(m_resource == resource); | 307 ASSERT(m_resource == resource); |
| 314 // A special case: empty scripts. We didn't receive any data before this | 308 // A special case: empty scripts. We didn't receive any data before this |
| 315 // notification. In that case, there won't be a "parsing complete" | 309 // notification. In that case, there won't be a "parsing complete" |
| 316 // notification either, and we should not wait for it. | 310 // notification either, and we should not wait for it. |
| 317 if (!m_firstDataChunkReceived) | 311 if (!m_firstDataChunkReceived) |
| 318 suppressStreaming(); | 312 suppressStreaming(); |
| 319 m_stream->didFinishLoading(); | 313 m_stream->didFinishLoading(); |
| 320 m_loadingFinished = true; | 314 m_loadingFinished = true; |
| 315 | |
| 316 if (m_blockMainThreadAfterLoading) { | |
| 317 // Make the main thead wait until the streaming is complete, to make | |
| 318 // sure that the script gets the main thread's attention as early as | |
| 319 // possible (for possible compiling, if the client wants to do it | |
| 320 // right away). Note that blocking here is not any worse than the | |
| 321 // non-streaming code path where the main thread eventually blocks | |
| 322 // to parse the script. | |
| 323 MutexLocker locker(m_mutex); | |
| 324 if (!isFinished()) { | |
|
Sami
2014/10/15 13:11:51
Should this be a while loop to guard against spuri
marja
2014/10/24 13:04:40
Hmm, dunno, it's a bit of overly defensive program
Sami
2014/10/24 13:15:35
I think we're using pthread_cond_wait under the ho
marja
2014/10/24 13:19:29
Oh wow. I wasn't aware of that. TIL. Fixed in the
| |
| 325 m_parsingFinishedCondition.wait(m_mutex); | |
| 326 } | |
| 327 ASSERT(isFinished()); | |
| 328 } | |
| 329 | |
| 321 notifyFinishedToClient(); | 330 notifyFinishedToClient(); |
| 331 | |
| 332 if (m_blockMainThreadAfterLoading && m_parsingFinished) { | |
| 333 // streamingComplete won't be called, so do the ramp-down work | |
| 334 // here. Since m_parsingFinished is true, we know that there was a | |
| 335 // background task and we need to deref(). | |
| 336 deref(); | |
| 337 } | |
| 322 } | 338 } |
| 323 | 339 |
| 324 ScriptStreamer::ScriptStreamer(ScriptResource* resource, v8::ScriptCompiler::Str eamedSource::Encoding encoding, PendingScript::Type scriptType) | 340 ScriptStreamer::ScriptStreamer(ScriptResource* resource, v8::ScriptCompiler::Str eamedSource::Encoding encoding, PendingScript::Type scriptType, bool blockMainTh readAfterLoading) |
| 325 : m_resource(resource) | 341 : m_resource(resource) |
| 326 , m_detached(false) | 342 , m_detached(false) |
| 327 , m_stream(new SourceStream(this)) | 343 , m_stream(new SourceStream(this)) |
| 328 , m_source(m_stream, encoding) // m_source takes ownership of m_stream. | 344 , m_source(m_stream, encoding) // m_source takes ownership of m_stream. |
| 329 , m_client(0) | 345 , m_client(0) |
| 330 , m_loadingFinished(false) | 346 , m_loadingFinished(false) |
| 331 , m_parsingFinished(false) | 347 , m_parsingFinished(false) |
| 332 , m_firstDataChunkReceived(false) | 348 , m_firstDataChunkReceived(false) |
| 333 , m_streamingSuppressed(false) | 349 , m_streamingSuppressed(false) |
| 334 , m_scriptType(scriptType) | 350 , m_scriptType(scriptType) |
| 351 , m_blockMainThreadAfterLoading(blockMainThreadAfterLoading) | |
| 335 { | 352 { |
| 336 } | 353 } |
| 337 | 354 |
| 355 void ScriptStreamer::streamingComplete() | |
| 356 { | |
| 357 // The background task is completed; do the necessary ramp-down in the main | |
| 358 // thread. | |
| 359 ASSERT(isMainThread()); | |
| 360 // In the blocking mode, the ramp-down is done in notifyFinished. | |
| 361 ASSERT(!m_blockMainThreadAfterLoading); | |
| 362 | |
| 363 // It's possible that the corresponding Resource was deleted before V8 | |
| 364 // finished streaming. In that case, the data or the notification is not | |
| 365 // needed. In addition, if the streaming is suppressed, the non-streaming | |
| 366 // code path will resume after the resource has loaded, before the | |
| 367 // background task finishes. | |
| 368 if (m_detached || m_streamingSuppressed) { | |
| 369 deref(); | |
| 370 return; | |
| 371 } | |
| 372 | |
| 373 // We have now streamed the whole script to V8 and it has parsed the | |
| 374 // script. We're ready for the next step: compiling and executing the | |
| 375 // script. | |
| 376 notifyFinishedToClient(); | |
| 377 | |
| 378 // The background thread no longer holds an implicit reference. | |
| 379 deref(); | |
| 380 } | |
| 381 | |
| 338 void ScriptStreamer::notifyFinishedToClient() | 382 void ScriptStreamer::notifyFinishedToClient() |
| 339 { | 383 { |
| 340 ASSERT(isMainThread()); | 384 ASSERT(isMainThread()); |
| 341 // Usually, the loading will be finished first, and V8 will still need some | 385 // 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 | 386 // 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 | 387 // parse error, the V8 side can complete before loading has finished. Send |
| 344 // the notification after both loading and V8 side operations have | 388 // 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 | 389 // 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 | 390 // function calling notifyFinishedToClient was already scheduled in the task |
| 347 // queue and the upper layer decided that it's not interested in the script | 391 // queue and the upper layer decided that it's not interested in the script |
| 348 // and called removeClient. | 392 // and called removeClient. |
| 393 MutexLocker locker(m_mutex); | |
| 349 if (isFinished() && m_client) | 394 if (isFinished() && m_client) |
| 350 m_client->notifyFinished(m_resource); | 395 m_client->notifyFinished(m_resource); |
| 351 } | 396 } |
| 352 | 397 |
| 353 const char* ScriptStreamer::startedStreamingHistogramName(PendingScript::Type sc riptType) | 398 const char* ScriptStreamer::startedStreamingHistogramName(PendingScript::Type sc riptType) |
| 354 { | 399 { |
| 355 switch (scriptType) { | 400 switch (scriptType) { |
| 356 case PendingScript::ParsingBlocking: | 401 case PendingScript::ParsingBlocking: |
| 357 return "WebCore.Scripts.ParsingBlocking.StartedStreaming"; | 402 return "WebCore.Scripts.ParsingBlocking.StartedStreaming"; |
| 358 break; | 403 break; |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 408 return false; | 453 return false; |
| 409 } | 454 } |
| 410 | 455 |
| 411 if (!scriptState->contextIsValid()) | 456 if (!scriptState->contextIsValid()) |
| 412 return false; | 457 return false; |
| 413 ScriptState::Scope scope(scriptState); | 458 ScriptState::Scope scope(scriptState); |
| 414 | 459 |
| 415 // The Resource might go out of scope if the script is no longer needed. We | 460 // 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 | 461 // will soon call PendingScript::setStreamer, which makes the PendingScript |
| 417 // notify the ScriptStreamer when it is destroyed. | 462 // notify the ScriptStreamer when it is destroyed. |
| 418 RefPtr<ScriptStreamer> streamer = adoptRef(new ScriptStreamer(resource, enco ding, scriptType)); | 463 RefPtr<ScriptStreamer> streamer = adoptRef(new ScriptStreamer(resource, enco ding, scriptType, settings->v8ScriptStreamingBlocking())); |
| 419 | 464 |
| 420 // Decide what kind of cached data we should produce while streaming. By | 465 // Decide what kind of cached data we should produce while streaming. By |
| 421 // default, we generate the parser cache for streamed scripts, to emulate | 466 // default, we generate the parser cache for streamed scripts, to emulate |
| 422 // the non-streaming behavior (see V8ScriptRunner::compileScript). | 467 // the non-streaming behavior (see V8ScriptRunner::compileScript). |
| 423 v8::ScriptCompiler::CompileOptions compileOption = v8::ScriptCompiler::kProd uceParserCache; | 468 v8::ScriptCompiler::CompileOptions compileOption = v8::ScriptCompiler::kProd uceParserCache; |
| 424 if (settings->v8CacheOptions() == V8CacheOptionsCode) | 469 if (settings->v8CacheOptions() == V8CacheOptionsCode) |
| 425 compileOption = v8::ScriptCompiler::kProduceCodeCache; | 470 compileOption = v8::ScriptCompiler::kProduceCodeCache; |
| 426 v8::ScriptCompiler::ScriptStreamingTask* scriptStreamingTask = v8::ScriptCom piler::StartStreamingScript(scriptState->isolate(), &(streamer->m_source), compi leOption); | 471 v8::ScriptCompiler::ScriptStreamingTask* scriptStreamingTask = v8::ScriptCom piler::StartStreamingScript(scriptState->isolate(), &(streamer->m_source), compi leOption); |
| 427 if (scriptStreamingTask) { | 472 if (scriptStreamingTask) { |
| 428 streamer->m_task = adoptPtr(scriptStreamingTask); | 473 streamer->m_task = adoptPtr(scriptStreamingTask); |
| 429 script.setStreamer(streamer.release()); | 474 script.setStreamer(streamer.release()); |
| 430 return true; | 475 return true; |
| 431 } | 476 } |
| 432 // Otherwise, V8 cannot stream the script. | 477 // Otherwise, V8 cannot stream the script. |
| 433 return false; | 478 return false; |
| 434 } | 479 } |
| 435 | 480 |
| 436 } // namespace blink | 481 } // namespace blink |
| OLD | NEW |