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

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

Issue 651163002: 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 (jochen@, skyostil@) Created 6 years, 2 months 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);
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698