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

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: . 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"
(...skipping 206 matching lines...) Expand 10 before | Expand all | Expand 10 after
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
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
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
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