Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(72)

Side by Side Diff: Source/bindings/core/v8/ScriptStreamer.cpp

Issue 674133002: Reland: Script streaming: Add an option to make the main thread block (wait for parsing) (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@master
Patch Set: code review (skyostil@) Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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);
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 // In the blocking case, the main thread is normally waiting at this
233 // code path will resume after the resource has loaded, before the 234 // point, but it can also happen that the load is not yet finished
234 // background task finishes. 235 // (e.g., a parse error). In that case, notifyFinished will be called
235 if (m_detached || m_streamingSuppressed) { 236 // eventually and it will not wait on m_parsingFinishedCondition.
236 deref(); 237
237 return; 238 // In the non-blocking case, notifyFinished might already be called, or it
239 // might be called in the future. In any case, do the cleanup here.
240 if (m_mainThreadWaitingForParserThread) {
241 m_parsingFinishedCondition.signal();
242 } else {
243 callOnMainThread(WTF::bind(&ScriptStreamer::streamingComplete, this));
238 } 244 }
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 } 245 }
250 246
251 void ScriptStreamer::cancel() 247 void ScriptStreamer::cancel()
252 { 248 {
253 ASSERT(isMainThread()); 249 ASSERT(isMainThread());
254 // The upper layer doesn't need the script any more, but streaming might 250 // 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 251 // 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 252 // the control the next time. It can also be that V8 has already completed
257 // its operations and streamingComplete will be called soon. 253 // its operations and streamingComplete will be called soon.
258 m_detached = true; 254 m_detached = true;
259 m_resource = 0; 255 m_resource = 0;
260 m_stream->cancel(); 256 m_stream->cancel();
261 } 257 }
262 258
263 void ScriptStreamer::suppressStreaming() 259 void ScriptStreamer::suppressStreaming()
264 { 260 {
265 ASSERT(!m_parsingFinished); 261 MutexLocker locker(m_mutex);
266 ASSERT(!m_loadingFinished); 262 ASSERT(!m_loadingFinished);
263 // It can be that the parsing task has already finished (e.g., if there was
264 // a parse error).
267 m_streamingSuppressed = true; 265 m_streamingSuppressed = true;
268 } 266 }
269 267
270 void ScriptStreamer::notifyAppendData(ScriptResource* resource) 268 void ScriptStreamer::notifyAppendData(ScriptResource* resource)
271 { 269 {
272 ASSERT(isMainThread()); 270 ASSERT(isMainThread());
273 ASSERT(m_resource == resource); 271 ASSERT(m_resource == resource);
274 if (m_streamingSuppressed) 272 {
275 return; 273 MutexLocker locker(m_mutex);
274 if (m_streamingSuppressed)
275 return;
276 }
276 if (!m_haveEnoughDataForStreaming) { 277 if (!m_haveEnoughDataForStreaming) {
277 // Even if the first data chunk is small, the script can still be big 278 // Even if the first data chunk is small, the script can still be big
278 // enough - wait until the next data chunk comes before deciding whether 279 // enough - wait until the next data chunk comes before deciding whether
279 // to start the streaming. 280 // to start the streaming.
280 if (resource->resourceBuffer()->size() < kSmallScriptThreshold) { 281 if (resource->resourceBuffer()->size() < kSmallScriptThreshold) {
281 return; 282 return;
282 } 283 }
283 m_haveEnoughDataForStreaming = true; 284 m_haveEnoughDataForStreaming = true;
284 const char* histogramName = startedStreamingHistogramName(m_scriptType); 285 const char* histogramName = startedStreamingHistogramName(m_scriptType);
285 if (ScriptStreamerThread::shared()->isRunningTask()) { 286 if (ScriptStreamerThread::shared()->isRunningTask()) {
286 // At the moment we only have one thread for running the tasks. A 287 // At the moment we only have one thread for running the tasks. A
287 // new task shouldn't be queued before the running task completes, 288 // new task shouldn't be queued before the running task completes,
288 // because the running task can block and wait for data from the 289 // because the running task can block and wait for data from the
289 // network. 290 // network.
290 suppressStreaming(); 291 suppressStreaming();
291 blink::Platform::current()->histogramEnumeration(histogramName, 0, 2 ); 292 blink::Platform::current()->histogramEnumeration(histogramName, 0, 2 );
292 return; 293 return;
293 } 294 }
294 ASSERT(m_task); 295 ASSERT(m_task);
295 // ScriptStreamer needs to stay alive as long as the background task is 296 // 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; 297 // running. This is taken care of with a manual ref() & deref() pair;
297 // the corresponding deref() is in streamingComplete. 298 // the corresponding deref() is in streamingComplete or in
299 // notifyFinished.
298 ref(); 300 ref();
299 ScriptStreamingTask* task = new ScriptStreamingTask(m_task.release(), th is); 301 ScriptStreamingTask* task = new ScriptStreamingTask(m_task.release(), th is);
300 ScriptStreamerThread::shared()->postTask(task); 302 ScriptStreamerThread::shared()->postTask(task);
301 blink::Platform::current()->histogramEnumeration(histogramName, 1, 2); 303 blink::Platform::current()->histogramEnumeration(histogramName, 1, 2);
302 } 304 }
303 m_stream->didReceiveData(); 305 m_stream->didReceiveData();
304 } 306 }
305 307
306 void ScriptStreamer::notifyFinished(Resource* resource) 308 void ScriptStreamer::notifyFinished(Resource* resource)
307 { 309 {
308 ASSERT(isMainThread()); 310 ASSERT(isMainThread());
309 ASSERT(m_resource == resource); 311 ASSERT(m_resource == resource);
310 // A special case: empty and small scripts. We didn't receive enough data to 312 // 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 313 // 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 314 // be a "parsing complete" notification either, and we should not wait for
313 // it. 315 // it.
314 if (!m_haveEnoughDataForStreaming) { 316 if (!m_haveEnoughDataForStreaming) {
315 const char* histogramName = startedStreamingHistogramName(m_scriptType); 317 const char* histogramName = startedStreamingHistogramName(m_scriptType);
316 blink::Platform::current()->histogramEnumeration(histogramName, 0, 2); 318 blink::Platform::current()->histogramEnumeration(histogramName, 0, 2);
317 suppressStreaming(); 319 suppressStreaming();
318 } 320 }
319 m_stream->didFinishLoading(); 321 m_stream->didFinishLoading();
320 m_loadingFinished = true; 322 m_loadingFinished = true;
323
324 if (shouldBlockMainThread()) {
325 // Make the main thead wait until the streaming is complete, to make
326 // sure that the script gets the main thread's attention as early as
327 // possible (for possible compiling, if the client wants to do it
328 // right away). Note that blocking here is not any worse than the
329 // non-streaming code path where the main thread eventually blocks
330 // to parse the script.
331 TRACE_EVENT0("v8", "v8.mainThreadWaitingForParserThread");
332 MutexLocker locker(m_mutex);
333 while (!isFinished()) {
334 ASSERT(!m_parsingFinished);
335 ASSERT(!m_streamingSuppressed);
336 m_mainThreadWaitingForParserThread = true;
337 m_parsingFinishedCondition.wait(m_mutex);
338 }
339 ASSERT(isFinished());
haraken 2014/10/24 13:43:04 This ASSERT won't make sense.
marja 2014/10/24 13:48:23 Done.
340 }
341
342 // Calling notifyFinishedToClient can result into the upper layers dropping
343 // references to ScriptStreamer. Keep it alive until this function ends.
344 RefPtr<ScriptStreamer> keepAlive(this);
haraken 2014/10/24 13:43:04 Nit: We normally use 'protect'.
marja 2014/10/24 13:48:23 Done.
345
321 notifyFinishedToClient(); 346 notifyFinishedToClient();
347
348 if (m_mainThreadWaitingForParserThread) {
349 ASSERT(m_parsingFinished);
350 ASSERT(!m_streamingSuppressed);
351 // streamingComplete won't be called, so do the ramp-down work
352 // here.
353 deref();
354 }
322 } 355 }
323 356
324 ScriptStreamer::ScriptStreamer(ScriptResource* resource, v8::ScriptCompiler::Str eamedSource::Encoding encoding, PendingScript::Type scriptType) 357 ScriptStreamer::ScriptStreamer(ScriptResource* resource, v8::ScriptCompiler::Str eamedSource::Encoding encoding, PendingScript::Type scriptType, ScriptStreamingM ode mode)
325 : m_resource(resource) 358 : m_resource(resource)
326 , m_detached(false) 359 , m_detached(false)
327 , m_stream(new SourceStream(this)) 360 , m_stream(new SourceStream(this))
328 , m_source(m_stream, encoding) // m_source takes ownership of m_stream. 361 , m_source(m_stream, encoding) // m_source takes ownership of m_stream.
329 , m_client(0) 362 , m_client(0)
330 , m_loadingFinished(false) 363 , m_loadingFinished(false)
331 , m_parsingFinished(false) 364 , m_parsingFinished(false)
332 , m_haveEnoughDataForStreaming(false) 365 , m_haveEnoughDataForStreaming(false)
333 , m_streamingSuppressed(false) 366 , m_streamingSuppressed(false)
334 , m_scriptType(scriptType) 367 , m_scriptType(scriptType)
368 , m_scriptStreamingMode(mode)
369 , m_mainThreadWaitingForParserThread(false)
335 { 370 {
336 } 371 }
337 372
373 void ScriptStreamer::streamingComplete()
374 {
375 // The background task is completed; do the necessary ramp-down in the main
376 // thread.
377 ASSERT(isMainThread());
378
379 // It's possible that the corresponding Resource was deleted before V8
380 // finished streaming. In that case, the data or the notification is not
381 // needed. In addition, if the streaming is suppressed, the non-streaming
382 // code path will resume after the resource has loaded, before the
383 // background task finishes.
384 if (m_detached || m_streamingSuppressed) {
385 deref();
386 return;
387 }
388
389 // We have now streamed the whole script to V8 and it has parsed the
390 // script. We're ready for the next step: compiling and executing the
391 // script.
392 notifyFinishedToClient();
393
394 // The background thread no longer holds an implicit reference.
395 deref();
396 }
397
338 void ScriptStreamer::notifyFinishedToClient() 398 void ScriptStreamer::notifyFinishedToClient()
339 { 399 {
340 ASSERT(isMainThread()); 400 ASSERT(isMainThread());
341 // Usually, the loading will be finished first, and V8 will still need some 401 // 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 402 // 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 403 // parse error, the V8 side can complete before loading has finished. Send
344 // the notification after both loading and V8 side operations have 404 // 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 405 // 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 406 // function calling notifyFinishedToClient was already scheduled in the task
347 // queue and the upper layer decided that it's not interested in the script 407 // queue and the upper layer decided that it's not interested in the script
348 // and called removeClient. 408 // and called removeClient.
349 if (isFinished() && m_client) 409 {
410 MutexLocker locker(m_mutex);
411 if (!isFinished())
412 return;
413 }
414 if (m_client)
350 m_client->notifyFinished(m_resource); 415 m_client->notifyFinished(m_resource);
351 } 416 }
352 417
353 const char* ScriptStreamer::startedStreamingHistogramName(PendingScript::Type sc riptType) 418 const char* ScriptStreamer::startedStreamingHistogramName(PendingScript::Type sc riptType)
354 { 419 {
355 switch (scriptType) { 420 switch (scriptType) {
356 case PendingScript::ParsingBlocking: 421 case PendingScript::ParsingBlocking:
357 return "WebCore.Scripts.ParsingBlocking.StartedStreaming"; 422 return "WebCore.Scripts.ParsingBlocking.StartedStreaming";
358 break; 423 break;
359 case PendingScript::Deferred: 424 case PendingScript::Deferred:
360 return "WebCore.Scripts.Deferred.StartedStreaming"; 425 return "WebCore.Scripts.Deferred.StartedStreaming";
361 break; 426 break;
362 case PendingScript::Async: 427 case PendingScript::Async:
363 return "WebCore.Scripts.Async.StartedStreaming"; 428 return "WebCore.Scripts.Async.StartedStreaming";
364 break; 429 break;
365 default: 430 default:
366 ASSERT_NOT_REACHED(); 431 ASSERT_NOT_REACHED();
367 break; 432 break;
368 } 433 }
369 return 0; 434 return 0;
370 } 435 }
371 436
372 bool ScriptStreamer::startStreamingInternal(PendingScript& script, Settings* set tings, ScriptState* scriptState, PendingScript::Type scriptType) 437 bool ScriptStreamer::startStreamingInternal(PendingScript& script, Settings* set tings, ScriptState* scriptState, PendingScript::Type scriptType)
373 { 438 {
374 ASSERT(isMainThread()); 439 ASSERT(isMainThread());
375 if (!settings || !settings->v8ScriptStreamingEnabled()) 440 if (!settings || !settings->v8ScriptStreamingEnabled())
376 return false; 441 return false;
442 if (settings->v8ScriptStreamingMode() == ScriptStreamingModeOnlyAsyncAndDefe r
443 && scriptType == PendingScript::ParsingBlocking)
444 return false;
445
377 ScriptResource* resource = script.resource(); 446 ScriptResource* resource = script.resource();
378 ASSERT(!resource->isLoaded()); 447 ASSERT(!resource->isLoaded());
379 if (!resource->url().protocolIsInHTTPFamily()) 448 if (!resource->url().protocolIsInHTTPFamily())
380 return false; 449 return false;
381 if (resource->resourceToRevalidate()) { 450 if (resource->resourceToRevalidate()) {
382 // This happens e.g., during reloads. We're actually not going to load 451 // This happens e.g., during reloads. We're actually not going to load
383 // the current Resource of the PendingScript but switch to another 452 // the current Resource of the PendingScript but switch to another
384 // Resource -> don't stream. 453 // Resource -> don't stream.
385 return false; 454 return false;
386 } 455 }
(...skipping 21 matching lines...) Expand all
408 return false; 477 return false;
409 } 478 }
410 479
411 if (!scriptState->contextIsValid()) 480 if (!scriptState->contextIsValid())
412 return false; 481 return false;
413 ScriptState::Scope scope(scriptState); 482 ScriptState::Scope scope(scriptState);
414 483
415 // The Resource might go out of scope if the script is no longer needed. We 484 // 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 485 // will soon call PendingScript::setStreamer, which makes the PendingScript
417 // notify the ScriptStreamer when it is destroyed. 486 // notify the ScriptStreamer when it is destroyed.
418 RefPtr<ScriptStreamer> streamer = adoptRef(new ScriptStreamer(resource, enco ding, scriptType)); 487 RefPtr<ScriptStreamer> streamer = adoptRef(new ScriptStreamer(resource, enco ding, scriptType, settings->v8ScriptStreamingMode()));
419 488
420 // Decide what kind of cached data we should produce while streaming. By 489 // Decide what kind of cached data we should produce while streaming. By
421 // default, we generate the parser cache for streamed scripts, to emulate 490 // default, we generate the parser cache for streamed scripts, to emulate
422 // the non-streaming behavior (see V8ScriptRunner::compileScript). 491 // the non-streaming behavior (see V8ScriptRunner::compileScript).
423 v8::ScriptCompiler::CompileOptions compileOption = v8::ScriptCompiler::kProd uceParserCache; 492 v8::ScriptCompiler::CompileOptions compileOption = v8::ScriptCompiler::kProd uceParserCache;
424 if (settings->v8CacheOptions() == V8CacheOptionsCode) 493 if (settings->v8CacheOptions() == V8CacheOptionsCode)
425 compileOption = v8::ScriptCompiler::kProduceCodeCache; 494 compileOption = v8::ScriptCompiler::kProduceCodeCache;
426 v8::ScriptCompiler::ScriptStreamingTask* scriptStreamingTask = v8::ScriptCom piler::StartStreamingScript(scriptState->isolate(), &(streamer->m_source), compi leOption); 495 v8::ScriptCompiler::ScriptStreamingTask* scriptStreamingTask = v8::ScriptCom piler::StartStreamingScript(scriptState->isolate(), &(streamer->m_source), compi leOption);
427 if (scriptStreamingTask) { 496 if (scriptStreamingTask) {
428 streamer->m_task = adoptPtr(scriptStreamingTask); 497 streamer->m_task = adoptPtr(scriptStreamingTask);
429 script.setStreamer(streamer.release()); 498 script.setStreamer(streamer.release());
430 return true; 499 return true;
431 } 500 }
432 // Otherwise, V8 cannot stream the script. 501 // Otherwise, V8 cannot stream the script.
433 return false; 502 return false;
434 } 503 }
435 504
436 } // namespace blink 505 } // namespace blink
OLDNEW
« no previous file with comments | « Source/bindings/core/v8/ScriptStreamer.h ('k') | Source/bindings/core/v8/ScriptStreamerTest.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698