OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2013 Google, Inc. All Rights Reserved. | 2 * Copyright (C) 2013 Google, Inc. All Rights Reserved. |
3 * | 3 * |
4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
5 * modification, are permitted provided that the following conditions | 5 * modification, are permitted provided that the following conditions |
6 * are met: | 6 * are met: |
7 * 1. Redistributions of source code must retain the above copyright | 7 * 1. Redistributions of source code must retain the above copyright |
8 * notice, this list of conditions and the following disclaimer. | 8 * notice, this list of conditions and the following disclaimer. |
9 * 2. Redistributions in binary form must reproduce the above copyright | 9 * 2. Redistributions in binary form must reproduce the above copyright |
10 * notice, this list of conditions and the following disclaimer in the | 10 * notice, this list of conditions and the following disclaimer in the |
(...skipping 24 matching lines...) Expand all Loading... |
35 #include "public/platform/Platform.h" | 35 #include "public/platform/Platform.h" |
36 #include "public/platform/WebTaskRunner.h" | 36 #include "public/platform/WebTaskRunner.h" |
37 #include "wtf/CurrentTime.h" | 37 #include "wtf/CurrentTime.h" |
38 #include "wtf/Functional.h" | 38 #include "wtf/Functional.h" |
39 #include "wtf/PtrUtil.h" | 39 #include "wtf/PtrUtil.h" |
40 #include "wtf/text/TextPosition.h" | 40 #include "wtf/text/TextPosition.h" |
41 #include <memory> | 41 #include <memory> |
42 | 42 |
43 namespace blink { | 43 namespace blink { |
44 | 44 |
45 // On a network with high latency and high bandwidth, using a device | 45 // On a network with high latency and high bandwidth, using a device with a fast |
46 // with a fast CPU, we could end up speculatively tokenizing | 46 // CPU, we could end up speculatively tokenizing the whole document, well ahead |
47 // the whole document, well ahead of when the main-thread actually needs it. | 47 // of when the main-thread actually needs it. This is a waste of memory (and |
48 // This is a waste of memory (and potentially time if the speculation fails). | 48 // potentially time if the speculation fails). So we limit our outstanding |
49 // So we limit our outstanding tokens arbitrarily to 10,000. | 49 // tokens arbitrarily to 10,000. Our maximal memory spent speculating will be |
50 // Our maximal memory spent speculating will be approximately: | 50 // approximately: |
51 // (defaultOutstandingTokenLimit + defaultPendingTokenLimit) * | 51 // (defaultOutstandingTokenLimit + defaultPendingTokenLimit) * |
52 // sizeof(CompactToken) | 52 // sizeof(CompactToken) |
53 // We use a separate low and high water mark to avoid constantly topping | 53 // |
54 // off the main thread's token buffer. | 54 // We use a separate low and high water mark to avoid |
55 // At time of writing, this is (10000 + 1000) * 28 bytes = ~308kb of memory. | 55 // constantly topping off the main thread's token buffer. At time of writing, |
56 // These numbers have not been tuned. | 56 // this is (10000 + 1000) * 28 bytes = ~308kb of memory. These numbers have not |
| 57 // been tuned. |
57 static const size_t defaultOutstandingTokenLimit = 10000; | 58 static const size_t defaultOutstandingTokenLimit = 10000; |
58 | 59 |
59 // We limit our chucks to 1000 tokens, to make sure the main | 60 // We limit our chucks to 1000 tokens, to make sure the main thread is never |
60 // thread is never waiting on the parser thread for tokens. | 61 // waiting on the parser thread for tokens. This was tuned in |
61 // This was tuned in https://bugs.webkit.org/show_bug.cgi?id=110408. | 62 // https://bugs.webkit.org/show_bug.cgi?id=110408. |
62 static const size_t defaultPendingTokenLimit = 1000; | 63 static const size_t defaultPendingTokenLimit = 1000; |
63 | 64 |
64 using namespace HTMLNames; | 65 using namespace HTMLNames; |
65 | 66 |
66 #if ENABLE(ASSERT) | 67 #if ENABLE(ASSERT) |
67 | 68 |
68 static void checkThatTokensAreSafeToSendToAnotherThread( | 69 static void checkThatTokensAreSafeToSendToAnotherThread( |
69 const CompactHTMLTokenStream* tokens) { | 70 const CompactHTMLTokenStream* tokens) { |
70 for (size_t i = 0; i < tokens->size(); ++i) | 71 for (size_t i = 0; i < tokens->size(); ++i) |
71 ASSERT(tokens->at(i).isSafeToSendToAnotherThread()); | 72 ASSERT(tokens->at(i).isSafeToSendToAnotherThread()); |
(...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
188 m_input.rewindTo(checkpoint->inputCheckpoint, checkpoint->unparsedInput); | 189 m_input.rewindTo(checkpoint->inputCheckpoint, checkpoint->unparsedInput); |
189 m_preloadScanner->rewindTo(checkpoint->preloadScannerCheckpoint); | 190 m_preloadScanner->rewindTo(checkpoint->preloadScannerCheckpoint); |
190 m_startingScript = false; | 191 m_startingScript = false; |
191 m_tokenizedChunkQueue->clear(); | 192 m_tokenizedChunkQueue->clear(); |
192 m_lastBytesReceivedTime = monotonicallyIncreasingTimeMS(); | 193 m_lastBytesReceivedTime = monotonicallyIncreasingTimeMS(); |
193 pumpTokenizer(); | 194 pumpTokenizer(); |
194 } | 195 } |
195 | 196 |
196 void BackgroundHTMLParser::startedChunkWithCheckpoint( | 197 void BackgroundHTMLParser::startedChunkWithCheckpoint( |
197 HTMLInputCheckpoint inputCheckpoint) { | 198 HTMLInputCheckpoint inputCheckpoint) { |
198 // Note, we should not have to worry about the index being invalid | 199 // Note, we should not have to worry about the index being invalid as messages |
199 // as messages from the main thread will be processed in FIFO order. | 200 // from the main thread will be processed in FIFO order. |
200 m_input.invalidateCheckpointsBefore(inputCheckpoint); | 201 m_input.invalidateCheckpointsBefore(inputCheckpoint); |
201 pumpTokenizer(); | 202 pumpTokenizer(); |
202 } | 203 } |
203 | 204 |
204 void BackgroundHTMLParser::finish() { | 205 void BackgroundHTMLParser::finish() { |
205 markEndOfFile(); | 206 markEndOfFile(); |
206 pumpTokenizer(); | 207 pumpTokenizer(); |
207 } | 208 } |
208 | 209 |
209 void BackgroundHTMLParser::stop() { | 210 void BackgroundHTMLParser::stop() { |
210 delete this; | 211 delete this; |
211 } | 212 } |
212 | 213 |
213 void BackgroundHTMLParser::forcePlaintextForTextDocument() { | 214 void BackgroundHTMLParser::forcePlaintextForTextDocument() { |
214 // This is only used by the TextDocumentParser (a subclass of HTMLDocumentPars
er) | 215 // This is only used by the TextDocumentParser (a subclass of |
215 // to force us into the PLAINTEXT state w/o using a <plaintext> tag. | 216 // HTMLDocumentParser) to force us into the PLAINTEXT state w/o using a |
216 // The TextDocumentParser uses a <pre> tag for historical/compatibility reason
s. | 217 // <plaintext> tag. The TextDocumentParser uses a <pre> tag for historical / |
| 218 // compatibility reasons. |
217 m_tokenizer->setState(HTMLTokenizer::PLAINTEXTState); | 219 m_tokenizer->setState(HTMLTokenizer::PLAINTEXTState); |
218 } | 220 } |
219 | 221 |
220 void BackgroundHTMLParser::markEndOfFile() { | 222 void BackgroundHTMLParser::markEndOfFile() { |
221 ASSERT(!m_input.current().isClosed()); | 223 ASSERT(!m_input.current().isClosed()); |
222 m_input.append(String(&kEndOfFileMarker, 1)); | 224 m_input.append(String(&kEndOfFileMarker, 1)); |
223 m_input.close(); | 225 m_input.close(); |
224 } | 226 } |
225 | 227 |
226 void BackgroundHTMLParser::pumpTokenizer() { | 228 void BackgroundHTMLParser::pumpTokenizer() { |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
261 | 263 |
262 bool shouldEvaluateForDocumentWrite = false; | 264 bool shouldEvaluateForDocumentWrite = false; |
263 bool isCSPMetaTag = false; | 265 bool isCSPMetaTag = false; |
264 m_preloadScanner->scan(token, m_input.current(), m_pendingPreloads, | 266 m_preloadScanner->scan(token, m_input.current(), m_pendingPreloads, |
265 &m_viewportDescription, &isCSPMetaTag, | 267 &m_viewportDescription, &isCSPMetaTag, |
266 &shouldEvaluateForDocumentWrite); | 268 &shouldEvaluateForDocumentWrite); |
267 | 269 |
268 simulatedToken = | 270 simulatedToken = |
269 m_treeBuilderSimulator.simulate(token, m_tokenizer.get()); | 271 m_treeBuilderSimulator.simulate(token, m_tokenizer.get()); |
270 | 272 |
271 // Break chunks before a script tag is inserted and flag the chunk as star
ting a script | 273 // Break chunks before a script tag is inserted and flag the chunk as |
272 // so the main parser can decide if it should yield before processing the
chunk. | 274 // starting a script so the main parser can decide if it should yield |
| 275 // before processing the chunk. |
273 if (simulatedToken == HTMLTreeBuilderSimulator::ScriptStart) { | 276 if (simulatedToken == HTMLTreeBuilderSimulator::ScriptStart) { |
274 shouldNotifyMainThread |= queueChunkForMainThread(); | 277 shouldNotifyMainThread |= queueChunkForMainThread(); |
275 m_startingScript = true; | 278 m_startingScript = true; |
276 } | 279 } |
277 | 280 |
278 m_pendingTokens->append(token); | 281 m_pendingTokens->append(token); |
279 if (isCSPMetaTag) { | 282 if (isCSPMetaTag) { |
280 m_pendingCSPMetaTokenIndex = m_pendingTokens->size() - 1; | 283 m_pendingCSPMetaTokenIndex = m_pendingTokens->size() - 1; |
281 } | 284 } |
282 if (shouldEvaluateForDocumentWrite) { | 285 if (shouldEvaluateForDocumentWrite) { |
283 m_likelyDocumentWriteScriptIndices.append(m_pendingTokens->size() - 1); | 286 m_likelyDocumentWriteScriptIndices.append(m_pendingTokens->size() - 1); |
284 } | 287 } |
285 } | 288 } |
286 | 289 |
287 m_token->clear(); | 290 m_token->clear(); |
288 | 291 |
289 if (simulatedToken == HTMLTreeBuilderSimulator::ScriptEnd || | 292 if (simulatedToken == HTMLTreeBuilderSimulator::ScriptEnd || |
290 m_pendingTokens->size() >= m_pendingTokenLimit) { | 293 m_pendingTokens->size() >= m_pendingTokenLimit) { |
291 shouldNotifyMainThread |= queueChunkForMainThread(); | 294 shouldNotifyMainThread |= queueChunkForMainThread(); |
292 // If we're far ahead of the main thread, yield for a bit to avoid consumi
ng too much memory. | 295 // If we're far ahead of the main thread, yield for a bit to avoid |
| 296 // consuming too much memory. |
293 if (m_input.totalCheckpointTokenCount() > m_outstandingTokenLimit) | 297 if (m_input.totalCheckpointTokenCount() > m_outstandingTokenLimit) |
294 break; | 298 break; |
295 } | 299 } |
296 | 300 |
297 if (!m_shouldCoalesceChunks && shouldNotifyMainThread) { | 301 if (!m_shouldCoalesceChunks && shouldNotifyMainThread) { |
298 runOnMainThread(&HTMLDocumentParser::notifyPendingTokenizedChunks, | 302 runOnMainThread(&HTMLDocumentParser::notifyPendingTokenizedChunks, |
299 m_parser); | 303 m_parser); |
300 shouldNotifyMainThread = false; | 304 shouldNotifyMainThread = false; |
301 } | 305 } |
302 } | 306 } |
303 // Wait to notify the main thread about the chunks until we're at the | 307 // Wait to notify the main thread about the chunks until we're at the limit. |
304 // limit. This lets the background parser generate lots of valuable | 308 // This lets the background parser generate lots of valuable preloads before |
305 // preloads before anything expensive (extensions, scripts) take up time | 309 // anything expensive (extensions, scripts) take up time on the main thread. A |
306 // on the main thread. A busy main thread can cause preload delays. | 310 // busy main thread can cause preload delays. |
307 if (shouldNotifyMainThread) { | 311 if (shouldNotifyMainThread) { |
308 runOnMainThread(&HTMLDocumentParser::notifyPendingTokenizedChunks, | 312 runOnMainThread(&HTMLDocumentParser::notifyPendingTokenizedChunks, |
309 m_parser); | 313 m_parser); |
310 } | 314 } |
311 } | 315 } |
312 | 316 |
313 bool BackgroundHTMLParser::queueChunkForMainThread() { | 317 bool BackgroundHTMLParser::queueChunkForMainThread() { |
314 if (m_pendingTokens->isEmpty()) | 318 if (m_pendingTokens->isEmpty()) |
315 return false; | 319 return false; |
316 | 320 |
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
372 if (isMainThread()) { | 376 if (isMainThread()) { |
373 (*WTF::bind(function, std::forward<Ps>(parameters)...))(); | 377 (*WTF::bind(function, std::forward<Ps>(parameters)...))(); |
374 } else { | 378 } else { |
375 m_loadingTaskRunner->postTask( | 379 m_loadingTaskRunner->postTask( |
376 BLINK_FROM_HERE, | 380 BLINK_FROM_HERE, |
377 crossThreadBind(function, std::forward<Ps>(parameters)...)); | 381 crossThreadBind(function, std::forward<Ps>(parameters)...)); |
378 } | 382 } |
379 } | 383 } |
380 | 384 |
381 } // namespace blink | 385 } // namespace blink |
OLD | NEW |