| 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 15 matching lines...) Expand all Loading... |
| 26 #include "config.h" | 26 #include "config.h" |
| 27 #include "core/html/parser/BackgroundHTMLParser.h" | 27 #include "core/html/parser/BackgroundHTMLParser.h" |
| 28 | 28 |
| 29 #include "core/html/parser/HTMLDocumentParser.h" | 29 #include "core/html/parser/HTMLDocumentParser.h" |
| 30 #include "core/html/parser/TextResourceDecoder.h" | 30 #include "core/html/parser/TextResourceDecoder.h" |
| 31 #include "wtf/MainThread.h" | 31 #include "wtf/MainThread.h" |
| 32 #include "wtf/text/TextPosition.h" | 32 #include "wtf/text/TextPosition.h" |
| 33 | 33 |
| 34 namespace blink { | 34 namespace blink { |
| 35 | 35 |
| 36 // On a network with high latency and high bandwidth, using a device | |
| 37 // with a fast CPU, we could end up speculatively tokenizing | |
| 38 // the whole document, well ahead of when the main-thread actually needs it. | |
| 39 // This is a waste of memory (and potentially time if the speculation fails). | |
| 40 // So we limit our outstanding tokens arbitrarily to 10,000. | |
| 41 // Our maximal memory spent speculating will be approximately: | |
| 42 // (outstandingTokenLimit + pendingTokenLimit) * sizeof(CompactToken) | |
| 43 // We use a separate low and high water mark to avoid constantly topping | |
| 44 // off the main thread's token buffer. | |
| 45 // At time of writing, this is (10000 + 1000) * 28 bytes = ~308kb of memory. | |
| 46 // These numbers have not been tuned. | |
| 47 static const size_t outstandingTokenLimit = 10000; | |
| 48 | |
| 49 // We limit our chucks to 1000 tokens, to make sure the main | 36 // We limit our chucks to 1000 tokens, to make sure the main |
| 50 // thread is never waiting on the parser thread for tokens. | 37 // thread is never waiting on the parser thread for tokens. |
| 51 // This was tuned in https://bugs.webkit.org/show_bug.cgi?id=110408. | 38 // This was tuned in https://bugs.webkit.org/show_bug.cgi?id=110408. |
| 52 static const size_t pendingTokenLimit = 1000; | 39 static const size_t pendingTokenLimit = 1000; |
| 53 | 40 |
| 54 #if ENABLE(ASSERT) | 41 #if ENABLE(ASSERT) |
| 55 | 42 |
| 56 static void checkThatTokensAreSafeToSendToAnotherThread(const CompactHTMLTokenSt
ream* tokens) | 43 static void checkThatTokensAreSafeToSendToAnotherThread(const CompactHTMLTokenSt
ream* tokens) |
| 57 { | 44 { |
| 58 for (size_t i = 0; i < tokens->size(); ++i) | 45 for (size_t i = 0; i < tokens->size(); ++i) |
| (...skipping 30 matching lines...) Expand all Loading... |
| 89 updateDocument(m_decoder->decode(data, dataLength)); | 76 updateDocument(m_decoder->decode(data, dataLength)); |
| 90 } | 77 } |
| 91 | 78 |
| 92 void BackgroundHTMLParser::appendRawBytesFromMainThread(PassOwnPtr<Vector<char>
> buffer) | 79 void BackgroundHTMLParser::appendRawBytesFromMainThread(PassOwnPtr<Vector<char>
> buffer) |
| 93 { | 80 { |
| 94 updateDocument(m_decoder->decode(buffer->data(), buffer->size())); | 81 updateDocument(m_decoder->decode(buffer->data(), buffer->size())); |
| 95 } | 82 } |
| 96 | 83 |
| 97 void BackgroundHTMLParser::appendDecodedBytes(const String& input) | 84 void BackgroundHTMLParser::appendDecodedBytes(const String& input) |
| 98 { | 85 { |
| 99 ASSERT(!m_input.current().isClosed()); | 86 ASSERT(!m_input.isClosed()); |
| 100 m_input.append(input); | 87 m_input.append(SegmentedString(input)); |
| 101 pumpTokenizer(); | 88 pumpTokenizer(); |
| 102 } | 89 } |
| 103 | 90 |
| 104 void BackgroundHTMLParser::flush() | 91 void BackgroundHTMLParser::flush() |
| 105 { | 92 { |
| 106 updateDocument(m_decoder->flush()); | 93 updateDocument(m_decoder->flush()); |
| 107 } | 94 } |
| 108 | 95 |
| 109 void BackgroundHTMLParser::updateDocument(const String& decodedData) | 96 void BackgroundHTMLParser::updateDocument(const String& decodedData) |
| 110 { | 97 { |
| 111 if (decodedData.isEmpty()) | 98 if (decodedData.isEmpty()) |
| 112 return; | 99 return; |
| 113 | 100 |
| 114 appendDecodedBytes(decodedData); | 101 appendDecodedBytes(decodedData); |
| 115 } | 102 } |
| 116 | 103 |
| 117 void BackgroundHTMLParser::startedChunkWithCheckpoint(HTMLInputCheckpoint inputC
heckpoint) | |
| 118 { | |
| 119 // Note, we should not have to worry about the index being invalid | |
| 120 // as messages from the main thread will be processed in FIFO order. | |
| 121 m_input.invalidateCheckpointsBefore(inputCheckpoint); | |
| 122 pumpTokenizer(); | |
| 123 } | |
| 124 | |
| 125 void BackgroundHTMLParser::finish() | 104 void BackgroundHTMLParser::finish() |
| 126 { | 105 { |
| 127 markEndOfFile(); | 106 markEndOfFile(); |
| 128 pumpTokenizer(); | 107 pumpTokenizer(); |
| 129 } | 108 } |
| 130 | 109 |
| 131 void BackgroundHTMLParser::stop() | 110 void BackgroundHTMLParser::stop() |
| 132 { | 111 { |
| 133 delete this; | 112 delete this; |
| 134 } | 113 } |
| 135 | 114 |
| 136 void BackgroundHTMLParser::markEndOfFile() | 115 void BackgroundHTMLParser::markEndOfFile() |
| 137 { | 116 { |
| 138 ASSERT(!m_input.current().isClosed()); | 117 ASSERT(!m_input.isClosed()); |
| 139 m_input.append(String(&kEndOfFileMarker, 1)); | 118 m_input.append(SegmentedString(String(&kEndOfFileMarker, 1))); |
| 140 m_input.close(); | 119 m_input.close(); |
| 141 } | 120 } |
| 142 | 121 |
| 143 void BackgroundHTMLParser::pumpTokenizer() | 122 void BackgroundHTMLParser::pumpTokenizer() |
| 144 { | 123 { |
| 145 // No need to start speculating until the main thread has almost caught up. | |
| 146 if (m_input.totalCheckpointTokenCount() > outstandingTokenLimit) | |
| 147 return; | |
| 148 | |
| 149 while (true) { | 124 while (true) { |
| 150 if (!m_tokenizer->nextToken(m_input.current(), *m_token)) { | 125 if (!m_tokenizer->nextToken(m_input, *m_token)) { |
| 151 // We've reached the end of our current input. | 126 // We've reached the end of our current input. |
| 152 sendTokensToMainThread(); | 127 sendTokensToMainThread(); |
| 153 break; | 128 break; |
| 154 } | 129 } |
| 155 | 130 |
| 156 { | 131 { |
| 157 CompactHTMLToken token(m_token.get(), TextPosition(m_input.current()
.currentLine(), m_input.current().currentColumn())); | 132 CompactHTMLToken token(m_token.get(), TextPosition(m_input.currentLi
ne(), m_input.currentColumn())); |
| 158 m_pendingTokens->append(token); | 133 m_pendingTokens->append(token); |
| 159 } | 134 } |
| 160 | 135 |
| 161 m_token->clear(); | 136 m_token->clear(); |
| 162 | 137 |
| 163 if (!m_treeBuilderSimulator.simulate(m_pendingTokens->last(), m_tokenize
r.get()) || m_pendingTokens->size() >= pendingTokenLimit) { | 138 if (!m_treeBuilderSimulator.simulate(m_pendingTokens->last(), m_tokenize
r.get()) || m_pendingTokens->size() >= pendingTokenLimit) |
| 164 sendTokensToMainThread(); | 139 sendTokensToMainThread(); |
| 165 // If we're far ahead of the main thread, yield for a bit to avoid c
onsuming too much memory. | |
| 166 if (m_input.totalCheckpointTokenCount() > outstandingTokenLimit) | |
| 167 break; | |
| 168 } | |
| 169 } | 140 } |
| 170 } | 141 } |
| 171 | 142 |
| 172 void BackgroundHTMLParser::sendTokensToMainThread() | 143 void BackgroundHTMLParser::sendTokensToMainThread() |
| 173 { | 144 { |
| 174 if (m_pendingTokens->isEmpty()) | 145 if (m_pendingTokens->isEmpty()) |
| 175 return; | 146 return; |
| 176 | 147 |
| 177 #if ENABLE(ASSERT) | 148 #if ENABLE(ASSERT) |
| 178 checkThatTokensAreSafeToSendToAnotherThread(m_pendingTokens.get()); | 149 checkThatTokensAreSafeToSendToAnotherThread(m_pendingTokens.get()); |
| 179 #endif | 150 #endif |
| 180 | 151 |
| 181 OwnPtr<HTMLDocumentParser::ParsedChunk> chunk = adoptPtr(new HTMLDocumentPar
ser::ParsedChunk); | 152 OwnPtr<HTMLDocumentParser::ParsedChunk> chunk = adoptPtr(new HTMLDocumentPar
ser::ParsedChunk); |
| 182 chunk->tokenizerState = m_tokenizer->state(); | 153 chunk->tokenizerState = m_tokenizer->state(); |
| 183 chunk->inputCheckpoint = m_input.createCheckpoint(m_pendingTokens->size()); | |
| 184 chunk->tokens = m_pendingTokens.release(); | 154 chunk->tokens = m_pendingTokens.release(); |
| 185 callOnMainThread(bind(&HTMLDocumentParser::didReceiveParsedChunkFromBackgrou
ndParser, m_parser, chunk.release())); | 155 callOnMainThread(bind(&HTMLDocumentParser::didReceiveParsedChunkFromBackgrou
ndParser, m_parser, chunk.release())); |
| 186 | 156 |
| 187 m_pendingTokens = adoptPtr(new CompactHTMLTokenStream); | 157 m_pendingTokens = adoptPtr(new CompactHTMLTokenStream); |
| 188 } | 158 } |
| 189 | 159 |
| 190 } | 160 } |
| OLD | NEW |