Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 import 'dart:async'; | 5 import 'dart:async'; |
| 6 import 'dart:collection'; | 6 import 'dart:collection'; |
| 7 import 'dart:typed_data'; | 7 import 'dart:typed_data'; |
| 8 | 8 |
| 9 import 'package:analyzer/dart/ast/ast.dart'; | 9 import 'package:analyzer/dart/ast/ast.dart'; |
| 10 import 'package:analyzer/error/error.dart'; | 10 import 'package:analyzer/error/error.dart'; |
| 11 import 'package:analyzer/error/listener.dart'; | 11 import 'package:analyzer/error/listener.dart'; |
| 12 import 'package:analyzer/file_system/file_system.dart'; | 12 import 'package:analyzer/file_system/file_system.dart'; |
| 13 import 'package:analyzer/src/context/context.dart'; | 13 import 'package:analyzer/src/context/context.dart'; |
| 14 import 'package:analyzer/src/dart/analysis/byte_store.dart'; | 14 import 'package:analyzer/src/dart/analysis/byte_store.dart'; |
| 15 import 'package:analyzer/src/dart/analysis/file_state.dart'; | 15 import 'package:analyzer/src/dart/analysis/file_state.dart'; |
| 16 import 'package:analyzer/src/generated/engine.dart' | 16 import 'package:analyzer/src/generated/engine.dart' |
| 17 show AnalysisContext, AnalysisEngine, AnalysisOptions, ChangeSet; | 17 show AnalysisContext, AnalysisEngine, AnalysisOptions, ChangeSet; |
| 18 import 'package:analyzer/src/generated/source.dart'; | 18 import 'package:analyzer/src/generated/source.dart'; |
| 19 import 'package:analyzer/src/summary/api_signature.dart'; | 19 import 'package:analyzer/src/summary/api_signature.dart'; |
| 20 import 'package:analyzer/src/summary/format.dart'; | 20 import 'package:analyzer/src/summary/format.dart'; |
| 21 import 'package:analyzer/src/summary/idl.dart'; | 21 import 'package:analyzer/src/summary/idl.dart'; |
| 22 import 'package:analyzer/src/summary/link.dart'; | 22 import 'package:analyzer/src/summary/link.dart'; |
| 23 import 'package:analyzer/src/summary/package_bundle_reader.dart'; | 23 import 'package:analyzer/src/summary/package_bundle_reader.dart'; |
| 24 import 'package:analyzer/src/summary/summarize_elements.dart'; | 24 import 'package:analyzer/src/summary/summarize_elements.dart'; |
| 25 import 'package:meta/meta.dart'; | |
| 25 | 26 |
| 26 /** | 27 /** |
| 27 * This class computes [AnalysisResult]s for Dart files. | 28 * This class computes [AnalysisResult]s for Dart files. |
| 28 * | 29 * |
| 29 * Let the set of "explicitly analyzed files" denote the set of paths that have | 30 * Let the set of "explicitly analyzed files" denote the set of paths that have |
| 30 * been passed to [addFile] but not subsequently passed to [removeFile]. Let | 31 * been passed to [addFile] but not subsequently passed to [removeFile]. Let |
| 31 * the "current analysis results" denote the map from the set of explicitly | 32 * the "current analysis results" denote the map from the set of explicitly |
| 32 * analyzed files to the most recent [AnalysisResult] delivered to [results] | 33 * analyzed files to the most recent [AnalysisResult] delivered to [results] |
| 33 * for each file. Let the "current file state" represent a map from file path | 34 * for each file. Let the "current file state" represent a map from file path |
| 34 * to the file contents most recently read from that file, or fetched from the | 35 * to the file contents most recently read from that file, or fetched from the |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 61 * TODO(scheglov) Clean up the list of implicitly analyzed files. | 62 * TODO(scheglov) Clean up the list of implicitly analyzed files. |
| 62 * | 63 * |
| 63 * TODO(scheglov) Handle not existing 'dart:x' URIs (while user is typing). | 64 * TODO(scheglov) Handle not existing 'dart:x' URIs (while user is typing). |
| 64 */ | 65 */ |
| 65 class AnalysisDriver { | 66 class AnalysisDriver { |
| 66 /** | 67 /** |
| 67 * The version of data format, should be incremented on every format change. | 68 * The version of data format, should be incremented on every format change. |
| 68 */ | 69 */ |
| 69 static const int DATA_VERSION = 1; | 70 static const int DATA_VERSION = 1; |
| 70 | 71 |
| 72 /** | |
| 73 * The name of the driver, e.g. the name of the folder. | |
| 74 */ | |
| 71 String name; | 75 String name; |
| 76 | |
| 77 /** | |
| 78 * The scheduler that schedules analysis work in this, and possible other | |
|
Paul Berry
2016/11/08 22:43:26
s/possible/possibly/
scheglov
2016/11/09 01:54:50
Done.
| |
| 79 * analysis drivers. | |
| 80 */ | |
| 81 final AnalysisDriverScheduler _scheduler; | |
| 82 | |
| 83 /** | |
| 84 * The logger to write performed operations and performance to. | |
| 85 */ | |
| 72 final PerformanceLog _logger; | 86 final PerformanceLog _logger; |
| 73 | 87 |
| 74 /** | 88 /** |
| 75 * The resource provider for working with files. | 89 * The resource provider for working with files. |
| 76 */ | 90 */ |
| 77 final ResourceProvider _resourceProvider; | 91 final ResourceProvider _resourceProvider; |
| 78 | 92 |
| 79 /** | 93 /** |
| 80 * The byte storage to get and put serialized data. | 94 * The byte storage to get and put serialized data. |
| 81 * | 95 * |
| (...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 142 * The set of files that are currently scheduled for analysis. | 156 * The set of files that are currently scheduled for analysis. |
| 143 */ | 157 */ |
| 144 final _filesToAnalyze = new LinkedHashSet<String>(); | 158 final _filesToAnalyze = new LinkedHashSet<String>(); |
| 145 | 159 |
| 146 /** | 160 /** |
| 147 * Mapping from library URIs to the dependency signature of the library. | 161 * Mapping from library URIs to the dependency signature of the library. |
| 148 */ | 162 */ |
| 149 final _dependencySignatureMap = <Uri, String>{}; | 163 final _dependencySignatureMap = <Uri, String>{}; |
| 150 | 164 |
| 151 /** | 165 /** |
| 152 * The monitor that is signalled when there is work to do. | 166 * The controller for the [results] stream. |
| 153 */ | 167 */ |
| 154 final _Monitor _hasWork = new _Monitor(); | 168 final _resultController = new StreamController<AnalysisResult>(); |
| 155 | 169 |
| 156 /** | 170 /** |
| 157 * The controller for the [status] stream. | 171 * The controller for the [status] stream. |
| 158 */ | 172 */ |
| 159 final _statusController = new StreamController<AnalysisStatus>(); | 173 final _statusController = new StreamController<AnalysisStatus>(); |
| 160 | 174 |
| 161 /** | 175 /** |
| 162 * The last status sent to the [status] stream. | 176 * The last status sent to the [status] stream. |
| 163 */ | 177 */ |
| 164 AnalysisStatus _currentStatus = AnalysisStatus.IDLE; | 178 AnalysisStatus _currentStatus = AnalysisStatus.IDLE; |
| 165 | 179 |
| 166 /** | 180 /** |
| 167 * Create a new instance of [AnalysisDriver]. | 181 * Create a new instance of [AnalysisDriver]. |
| 168 * | 182 * |
| 169 * The given [SourceFactory] is cloned to ensure that it does not contain a | 183 * The given [SourceFactory] is cloned to ensure that it does not contain a |
| 170 * reference to a [AnalysisContext] in which it could have been used. | 184 * reference to a [AnalysisContext] in which it could have been used. |
| 171 */ | 185 */ |
| 172 AnalysisDriver(this._logger, this._resourceProvider, this._byteStore, | 186 AnalysisDriver( |
| 173 this._contentOverlay, SourceFactory sourceFactory, this._analysisOptions) | 187 this._scheduler, |
| 188 this._logger, | |
| 189 this._resourceProvider, | |
| 190 this._byteStore, | |
| 191 this._contentOverlay, | |
| 192 SourceFactory sourceFactory, | |
| 193 this._analysisOptions) | |
| 174 : _sourceFactory = sourceFactory.clone() { | 194 : _sourceFactory = sourceFactory.clone() { |
| 175 _fillSalt(); | 195 _fillSalt(); |
| 176 _sdkBundle = sourceFactory.dartSdk.getLinkedBundle(); | 196 _sdkBundle = sourceFactory.dartSdk.getLinkedBundle(); |
| 177 _fsState = new FileSystemState(_logger, _byteStore, _contentOverlay, | 197 _fsState = new FileSystemState(_logger, _byteStore, _contentOverlay, |
| 178 _resourceProvider, _sourceFactory, _analysisOptions, _salt); | 198 _resourceProvider, _sourceFactory, _analysisOptions, _salt); |
| 199 _scheduler._add(this); | |
| 179 } | 200 } |
| 180 | 201 |
| 181 /** | 202 /** |
| 182 * Set the list of files that the driver should try to analyze sooner. | 203 * Set the list of files that the driver should try to analyze sooner. |
| 183 * | 204 * |
| 184 * Every path in the list must be absolute and normalized. | 205 * Every path in the list must be absolute and normalized. |
| 185 * | 206 * |
| 186 * The driver will produce the results through the [results] stream. The | 207 * The driver will produce the results through the [results] stream. The |
| 187 * exact order in which results are produced is not defined, neither | 208 * exact order in which results are produced is not defined, neither |
| 188 * between priority files, nor between priority and non-priority files. | 209 * between priority files, nor between priority and non-priority files. |
| 189 */ | 210 */ |
| 190 void set priorityFiles(List<String> priorityPaths) { | 211 void set priorityFiles(List<String> priorityPaths) { |
| 191 _priorityFiles.clear(); | 212 _priorityFiles.clear(); |
| 192 _priorityFiles.addAll(priorityPaths); | 213 _priorityFiles.addAll(priorityPaths); |
| 193 _transitionToAnalyzing(); | 214 _transitionToAnalyzing(); |
| 194 _hasWork.notify(); | 215 _scheduler._notify(this); |
| 195 } | 216 } |
| 196 | 217 |
| 197 /** | 218 /** |
| 198 * Return the [Stream] that produces [AnalysisResult]s for added files. | 219 * Return the [Stream] that produces [AnalysisResult]s for added files. |
| 199 * | 220 * |
| 200 * Analysis starts when the client starts listening to the stream, and stops | 221 * Note that the stream supports only one single subscriber. |
| 201 * when the client cancels the subscription. Note that the stream supports | |
| 202 * only one single subscriber. | |
| 203 * | 222 * |
| 204 * When the client starts listening, the analysis state transitions to | 223 * Analysis starts when the [AnalysisDriverScheduler] is started and the |
| 205 * "analyzing" and an analysis result is produced for every added file prior | 224 * driver is added to it. The analysis state transitions to "analyzing" and |
| 206 * to the next time the analysis state transitions to "idle". | 225 * an analysis result is produced for every added file prior to the next time |
| 226 * the analysis state transitions to "idle". | |
| 207 * | 227 * |
| 208 * At least one analysis result is produced for every file passed to | 228 * At least one analysis result is produced for every file passed to |
| 209 * [addFile] or [changeFile] prior to the next time the analysis state | 229 * [addFile] or [changeFile] prior to the next time the analysis state |
| 210 * transitions to "idle", unless the file is later removed from analysis | 230 * transitions to "idle", unless the file is later removed from analysis |
| 211 * using [removeFile]. Analysis results for other files are produced only if | 231 * using [removeFile]. Analysis results for other files are produced only if |
| 212 * the changes affect analysis results of other files. | 232 * the changes affect analysis results of other files. |
| 213 * | 233 * |
| 214 * More than one result might be produced for the same file, even if the | 234 * More than one result might be produced for the same file, even if the |
| 215 * client does not change the state of the files. | 235 * client does not change the state of the files. |
| 216 * | 236 * |
| 217 * Results might be produced even for files that have never been added | 237 * Results might be produced even for files that have never been added |
| 218 * using [addFile], for example when [getResult] was called for a file. | 238 * using [addFile], for example when [getResult] was called for a file. |
| 219 */ | 239 */ |
| 220 Stream<AnalysisResult> get results async* { | 240 Stream<AnalysisResult> get results => _resultController.stream; |
| 221 try { | |
| 222 PerformanceLogSection analysisSection = null; | |
| 223 while (true) { | |
| 224 // Pump the event queue to allow IO and other asynchronous data | |
| 225 // processing while analysis is active. For example Analysis Server | |
| 226 // needs to be able to process `updateContent` or `setPriorityFiles` | |
| 227 // requests while background analysis is in progress. | |
| 228 // | |
| 229 // The number of pumpings is arbitrary, might be changed if we see that | |
| 230 // analysis or other data processing tasks are starving. Ideally we | |
| 231 // would need to be able to set priority of (continuous) asynchronous | |
| 232 // tasks. | |
| 233 await _pumpEventQueue(128); | |
| 234 | |
| 235 await _hasWork.signal; | |
| 236 | |
| 237 if (analysisSection == null) { | |
| 238 analysisSection = _logger.enter('Analyzing'); | |
| 239 } | |
| 240 | |
| 241 // Verify all changed files one at a time. | |
| 242 if (_changedFiles.isNotEmpty) { | |
| 243 String path = _removeFirst(_changedFiles); | |
| 244 _verifyApiSignature(path); | |
| 245 // Repeat the processing loop. | |
| 246 _hasWork.notify(); | |
| 247 continue; | |
| 248 } | |
| 249 | |
| 250 // Analyze a requested file. | |
| 251 if (_requestedFiles.isNotEmpty) { | |
| 252 String path = _requestedFiles.keys.first; | |
| 253 AnalysisResult result = _computeAnalysisResult(path, withUnit: true); | |
| 254 // Notify the completers. | |
| 255 _requestedFiles.remove(path).forEach((completer) { | |
| 256 completer.complete(result); | |
| 257 }); | |
| 258 // Remove from to be analyzed and produce it now. | |
| 259 _filesToAnalyze.remove(path); | |
| 260 yield result; | |
| 261 // Repeat the processing loop. | |
| 262 _hasWork.notify(); | |
| 263 continue; | |
| 264 } | |
| 265 | |
| 266 // Analyze a priority file. | |
| 267 if (_priorityFiles.isNotEmpty) { | |
| 268 bool analyzed = false; | |
| 269 for (String path in _priorityFiles) { | |
| 270 if (_filesToAnalyze.remove(path)) { | |
| 271 analyzed = true; | |
| 272 AnalysisResult result = | |
| 273 _computeAnalysisResult(path, withUnit: true); | |
| 274 yield result; | |
| 275 break; | |
| 276 } | |
| 277 } | |
| 278 // Repeat the processing loop. | |
| 279 if (analyzed) { | |
| 280 _hasWork.notify(); | |
| 281 continue; | |
| 282 } | |
| 283 } | |
| 284 | |
| 285 // Analyze a general file. | |
| 286 if (_filesToAnalyze.isNotEmpty) { | |
| 287 String path = _removeFirst(_filesToAnalyze); | |
| 288 AnalysisResult result = _computeAnalysisResult(path, withUnit: false); | |
| 289 yield result; | |
| 290 // Repeat the processing loop. | |
| 291 _hasWork.notify(); | |
| 292 continue; | |
| 293 } | |
| 294 | |
| 295 // There is nothing to do. | |
| 296 analysisSection.exit(); | |
| 297 analysisSection = null; | |
| 298 _transitionToIdle(); | |
| 299 } | |
| 300 } finally { | |
| 301 print('The stream was cancelled.'); | |
| 302 } | |
| 303 } | |
| 304 | 241 |
| 305 /** | 242 /** |
| 306 * Return the stream that produces [AnalysisStatus] events. | 243 * Return the stream that produces [AnalysisStatus] events. |
| 307 */ | 244 */ |
| 308 Stream<AnalysisStatus> get status => _statusController.stream; | 245 Stream<AnalysisStatus> get status => _statusController.stream; |
| 309 | 246 |
| 310 /** | 247 /** |
| 248 * Return the priority of work that the driver needs to perform. | |
| 249 */ | |
| 250 AnalysisDriverPriority get _workPriority { | |
| 251 if (_requestedFiles.isNotEmpty) { | |
| 252 return AnalysisDriverPriority.interactive; | |
| 253 } | |
| 254 if (_priorityFiles.isNotEmpty) { | |
| 255 for (String path in _priorityFiles) { | |
| 256 if (_filesToAnalyze.contains(path)) { | |
| 257 return AnalysisDriverPriority.priority; | |
| 258 } | |
| 259 } | |
| 260 } | |
| 261 if (_filesToAnalyze.isNotEmpty) { | |
| 262 return AnalysisDriverPriority.general; | |
| 263 } | |
| 264 if (_changedFiles.isNotEmpty) { | |
| 265 return AnalysisDriverPriority.general; | |
| 266 } | |
| 267 _transitionToIdle(); | |
| 268 return AnalysisDriverPriority.nothing; | |
| 269 } | |
| 270 | |
| 271 /** | |
| 311 * Add the file with the given [path] to the set of files to analyze. | 272 * Add the file with the given [path] to the set of files to analyze. |
| 312 * | 273 * |
| 313 * The [path] must be absolute and normalized. | 274 * The [path] must be absolute and normalized. |
| 314 * | 275 * |
| 315 * The results of analysis are eventually produced by the [results] stream. | 276 * The results of analysis are eventually produced by the [results] stream. |
| 316 */ | 277 */ |
| 317 void addFile(String path) { | 278 void addFile(String path) { |
| 318 if (AnalysisEngine.isDartFileName(path)) { | 279 if (AnalysisEngine.isDartFileName(path)) { |
| 319 _explicitFiles.add(path); | 280 _explicitFiles.add(path); |
| 320 _filesToAnalyze.add(path); | 281 _filesToAnalyze.add(path); |
| 321 } | 282 } |
| 322 _transitionToAnalyzing(); | 283 _transitionToAnalyzing(); |
| 323 _hasWork.notify(); | 284 _scheduler._notify(this); |
| 324 } | 285 } |
| 325 | 286 |
| 326 /** | 287 /** |
| 327 * The file with the given [path] might have changed - updated, added or | 288 * The file with the given [path] might have changed - updated, added or |
| 328 * removed. Or not, we don't know. Or it might have, but then changed back. | 289 * removed. Or not, we don't know. Or it might have, but then changed back. |
| 329 * | 290 * |
| 330 * The [path] must be absolute and normalized. | 291 * The [path] must be absolute and normalized. |
| 331 * | 292 * |
| 332 * The [path] can be any file - explicitly or implicitly analyzed, or neither. | 293 * The [path] can be any file - explicitly or implicitly analyzed, or neither. |
| 333 * | 294 * |
| 334 * Causes the analysis state to transition to "analyzing" (if it is not in | 295 * Causes the analysis state to transition to "analyzing" (if it is not in |
| 335 * that state already). Schedules the file contents for [path] to be read | 296 * that state already). Schedules the file contents for [path] to be read |
| 336 * into the current file state prior to the next time the analysis state | 297 * into the current file state prior to the next time the analysis state |
| 337 * transitions to "idle". | 298 * transitions to "idle". |
| 338 * | 299 * |
| 339 * Invocation of this method will not prevent a [Future] returned from | 300 * Invocation of this method will not prevent a [Future] returned from |
| 340 * [getResult] from completing with a result, but the result is not | 301 * [getResult] from completing with a result, but the result is not |
| 341 * guaranteed to be consistent with the new current file state after this | 302 * guaranteed to be consistent with the new current file state after this |
| 342 * [changeFile] invocation. | 303 * [changeFile] invocation. |
| 343 */ | 304 */ |
| 344 void changeFile(String path) { | 305 void changeFile(String path) { |
| 345 if (AnalysisEngine.isDartFileName(path)) { | 306 if (AnalysisEngine.isDartFileName(path)) { |
| 346 _changedFiles.add(path); | 307 _changedFiles.add(path); |
| 347 if (_explicitFiles.contains(path)) { | 308 if (_explicitFiles.contains(path)) { |
| 348 _filesToAnalyze.add(path); | 309 _filesToAnalyze.add(path); |
| 349 } | 310 } |
| 350 } | 311 } |
| 351 _transitionToAnalyzing(); | 312 _transitionToAnalyzing(); |
| 352 _hasWork.notify(); | 313 _scheduler._notify(this); |
| 353 } | 314 } |
| 354 | 315 |
| 355 /** | 316 /** |
| 317 * Notify the driver that the client is going to stop using it. | |
| 318 */ | |
| 319 void dispose() { | |
| 320 _scheduler._remove(this); | |
| 321 } | |
| 322 | |
| 323 /** | |
| 356 * Return the [Future] that completes with a [AnalysisResult] for the file | 324 * Return the [Future] that completes with a [AnalysisResult] for the file |
| 357 * with the given [path]. | 325 * with the given [path]. |
| 358 * | 326 * |
| 359 * The [path] must be absolute and normalized. | 327 * The [path] must be absolute and normalized. |
| 360 * | 328 * |
| 361 * The [path] can be any file - explicitly or implicitly analyzed, or neither. | 329 * The [path] can be any file - explicitly or implicitly analyzed, or neither. |
| 362 * | 330 * |
| 363 * Causes the analysis state to transition to "analyzing" (if it is not in | 331 * Causes the analysis state to transition to "analyzing" (if it is not in |
| 364 * that state already), the driver will read the file and produce the analysis | 332 * that state already), the driver will read the file and produce the analysis |
| 365 * result for it, which is consistent with the current file state (including | 333 * result for it, which is consistent with the current file state (including |
| 366 * the new state of the file), prior to the next time the analysis state | 334 * the new state of the file), prior to the next time the analysis state |
| 367 * transitions to "idle". | 335 * transitions to "idle". |
| 368 */ | 336 */ |
| 369 Future<AnalysisResult> getResult(String path) { | 337 Future<AnalysisResult> getResult(String path) { |
| 370 var completer = new Completer<AnalysisResult>(); | 338 var completer = new Completer<AnalysisResult>(); |
| 371 _requestedFiles | 339 _requestedFiles |
| 372 .putIfAbsent(path, () => <Completer<AnalysisResult>>[]) | 340 .putIfAbsent(path, () => <Completer<AnalysisResult>>[]) |
| 373 .add(completer); | 341 .add(completer); |
| 374 _transitionToAnalyzing(); | 342 _transitionToAnalyzing(); |
| 375 _hasWork.notify(); | 343 _scheduler._notify(this); |
| 376 return completer.future; | 344 return completer.future; |
| 377 } | 345 } |
| 378 | 346 |
| 379 /** | 347 /** |
| 380 * Return `true` if the file with the given [path] was explicitly added | 348 * Return `true` if the file with the given [path] was explicitly added |
| 381 * to analysis using [addFile]. | 349 * to analysis using [addFile]. |
| 382 */ | 350 */ |
| 383 bool isAddedFile(String path) { | 351 bool isAddedFile(String path) { |
| 384 return _explicitFiles.contains(path); | 352 return _explicitFiles.contains(path); |
| 385 } | 353 } |
| (...skipping 282 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 668 ApiSignature signature = new ApiSignature(); | 636 ApiSignature signature = new ApiSignature(); |
| 669 signature.addUint32List(_salt); | 637 signature.addUint32List(_salt); |
| 670 signature.addString(dependencyHash); | 638 signature.addString(dependencyHash); |
| 671 signature.addString(file.contentHash); | 639 signature.addString(file.contentHash); |
| 672 return '${signature.toHex()}.resolved'; | 640 return '${signature.toHex()}.resolved'; |
| 673 } | 641 } |
| 674 return null; | 642 return null; |
| 675 } | 643 } |
| 676 | 644 |
| 677 /** | 645 /** |
| 646 * Perform a single chunk of work and produce [results]. | |
| 647 */ | |
| 648 Future<Null> _performWork() async { | |
| 649 // Verify all changed files one at a time. | |
| 650 if (_changedFiles.isNotEmpty) { | |
| 651 String path = _removeFirst(_changedFiles); | |
| 652 _verifyApiSignature(path); | |
| 653 return; | |
| 654 } | |
| 655 | |
| 656 // Analyze a requested file. | |
| 657 if (_requestedFiles.isNotEmpty) { | |
| 658 String path = _requestedFiles.keys.first; | |
| 659 AnalysisResult result = _computeAnalysisResult(path, withUnit: true); | |
| 660 // Notify the completers. | |
| 661 _requestedFiles.remove(path).forEach((completer) { | |
| 662 completer.complete(result); | |
| 663 }); | |
| 664 // Remove from to be analyzed and produce it now. | |
| 665 _filesToAnalyze.remove(path); | |
| 666 _resultController.add(result); | |
| 667 return; | |
| 668 } | |
| 669 | |
| 670 // Analyze a priority file. | |
| 671 if (_priorityFiles.isNotEmpty) { | |
| 672 for (String path in _priorityFiles) { | |
| 673 if (_filesToAnalyze.remove(path)) { | |
| 674 AnalysisResult result = _computeAnalysisResult(path, withUnit: true); | |
| 675 _resultController.add(result); | |
| 676 return; | |
| 677 } | |
| 678 } | |
| 679 } | |
| 680 | |
| 681 // Analyze a general file. | |
| 682 if (_filesToAnalyze.isNotEmpty) { | |
| 683 String path = _removeFirst(_filesToAnalyze); | |
| 684 AnalysisResult result = _computeAnalysisResult(path, withUnit: false); | |
| 685 _resultController.add(result); | |
| 686 return; | |
| 687 } | |
| 688 } | |
| 689 | |
| 690 /** | |
| 678 * Send a notifications to the [status] stream that the driver started | 691 * Send a notifications to the [status] stream that the driver started |
| 679 * analyzing. | 692 * analyzing. |
| 680 */ | 693 */ |
| 681 void _transitionToAnalyzing() { | 694 void _transitionToAnalyzing() { |
| 682 if (_currentStatus != AnalysisStatus.ANALYZING) { | 695 if (_currentStatus != AnalysisStatus.ANALYZING) { |
| 683 _currentStatus = AnalysisStatus.ANALYZING; | 696 _currentStatus = AnalysisStatus.ANALYZING; |
| 684 _statusController.add(AnalysisStatus.ANALYZING); | 697 _statusController.add(AnalysisStatus.ANALYZING); |
| 685 } | 698 } |
| 686 } | 699 } |
| 687 | 700 |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 714 if (anyApiChanged) { | 727 if (anyApiChanged) { |
| 715 _logger.writeln('API signatures mismatch found for $path'); | 728 _logger.writeln('API signatures mismatch found for $path'); |
| 716 _dependencySignatureMap.clear(); | 729 _dependencySignatureMap.clear(); |
| 717 _filesToAnalyze.addAll(_explicitFiles); | 730 _filesToAnalyze.addAll(_explicitFiles); |
| 718 } | 731 } |
| 719 return files[0]; | 732 return files[0]; |
| 720 }); | 733 }); |
| 721 } | 734 } |
| 722 | 735 |
| 723 /** | 736 /** |
| 737 * Remove and return the first item in the given [set]. | |
| 738 */ | |
| 739 static Object/*=T*/ _removeFirst/*<T>*/(LinkedHashSet<Object/*=T*/ > set) { | |
| 740 Object/*=T*/ element = set.first; | |
| 741 set.remove(element); | |
| 742 return element; | |
| 743 } | |
| 744 } | |
| 745 | |
| 746 /** | |
| 747 * Priorities of [AnalysisDriver] work. The closer a priority to the beginning | |
| 748 * of the list, the earlier the corresponding [AnalysisDriver] should be asked | |
| 749 * to perform work. | |
| 750 */ | |
| 751 @visibleForTesting | |
| 752 enum AnalysisDriverPriority { interactive, priority, general, nothing } | |
| 753 | |
| 754 /** | |
| 755 * Instances of this class schedule work in multiple [AnalysisDriver]s so that | |
| 756 * work with the lowest priority is performed first. | |
| 757 */ | |
| 758 class AnalysisDriverScheduler { | |
| 759 final PerformanceLog _logger; | |
| 760 final List<AnalysisDriver> _drivers = []; | |
| 761 final _Monitor _hasWork = new _Monitor(); | |
| 762 | |
| 763 bool _started = false; | |
| 764 | |
| 765 AnalysisDriverScheduler(this._logger); | |
| 766 | |
| 767 /** | |
| 768 * Start the scheduler, so that any [AnalysisDriver] created before or | |
| 769 * after will be asked to perform work. | |
| 770 */ | |
| 771 void start() { | |
| 772 if (_started) { | |
| 773 throw new StateError('The scheduler has already been started.'); | |
| 774 } | |
| 775 _started = true; | |
| 776 _run(); | |
| 777 } | |
| 778 | |
| 779 /** | |
| 780 * Add the given [driver] and schedule it to perform its work. | |
| 781 */ | |
| 782 void _add(AnalysisDriver driver) { | |
| 783 _drivers.add(driver); | |
| 784 _hasWork.notify(); | |
| 785 } | |
| 786 | |
| 787 /** | |
| 788 * Notify that there is a change to the [driver], it it might need to | |
| 789 * perform some work. | |
| 790 */ | |
| 791 void _notify(AnalysisDriver driver) { | |
| 792 _hasWork.notify(); | |
| 793 } | |
| 794 | |
| 795 /** | |
| 796 * Remove the given [driver] from the scheduler, so that it will not be | |
| 797 * asked to perform any new work. | |
| 798 */ | |
| 799 void _remove(AnalysisDriver driver) { | |
| 800 _drivers.remove(driver); | |
| 801 _hasWork.notify(); | |
| 802 } | |
| 803 | |
| 804 /** | |
| 805 * Run infinitely analysis cycle, selecting the drivers with the lowest | |
| 806 * priority first. | |
| 807 */ | |
| 808 Future<Null> _run() async { | |
| 809 PerformanceLogSection analysisSection; | |
| 810 while (true) { | |
| 811 // Pump the event queue to allow IO and other asynchronous data | |
| 812 // processing while analysis is active. For example Analysis Server | |
| 813 // needs to be able to process `updateContent` or `setPriorityFiles` | |
| 814 // requests while background analysis is in progress. | |
| 815 // | |
| 816 // The number of pumpings is arbitrary, might be changed if we see that | |
| 817 // analysis or other data processing tasks are starving. Ideally we | |
| 818 // would need to be able to set priority of (continuous) asynchronous | |
| 819 // tasks. | |
| 820 await _pumpEventQueue(128); | |
| 821 | |
| 822 await _hasWork.signal; | |
| 823 | |
| 824 if (analysisSection == null) { | |
| 825 analysisSection = _logger.enter('Analyzing'); | |
| 826 } | |
| 827 | |
| 828 // Find the driver with the most priority work. | |
|
Paul Berry
2016/11/08 22:43:26
s/most/highest/
scheglov
2016/11/09 01:54:50
Done.
| |
| 829 AnalysisDriver bestDriver; | |
| 830 AnalysisDriverPriority bestDriverPriority; | |
| 831 for (AnalysisDriver driver in _drivers) { | |
| 832 AnalysisDriverPriority priority = driver._workPriority; | |
| 833 if (bestDriverPriority == null || | |
| 834 priority.index < bestDriverPriority.index) { | |
| 835 bestDriver = driver; | |
| 836 bestDriverPriority = priority; | |
| 837 } | |
| 838 } | |
| 839 | |
| 840 // Continue to sleeping if no work to do. | |
| 841 if (bestDriverPriority == null || | |
| 842 bestDriverPriority == AnalysisDriverPriority.nothing) { | |
| 843 analysisSection.exit(); | |
| 844 analysisSection = null; | |
| 845 continue; | |
| 846 } | |
| 847 | |
| 848 // Ask the driver to perform a chunk of work. | |
| 849 await bestDriver._performWork(); | |
| 850 | |
| 851 // Schedule one more cycle. | |
| 852 _hasWork.notify(); | |
| 853 } | |
| 854 } | |
| 855 | |
| 856 /** | |
| 724 * Returns a [Future] that completes after performing [times] pumpings of | 857 * Returns a [Future] that completes after performing [times] pumpings of |
| 725 * the event queue. | 858 * the event queue. |
| 726 */ | 859 */ |
| 727 static Future _pumpEventQueue(int times) { | 860 static Future _pumpEventQueue(int times) { |
| 728 if (times == 0) { | 861 if (times == 0) { |
| 729 return new Future.value(); | 862 return new Future.value(); |
| 730 } | 863 } |
| 731 return new Future.delayed(Duration.ZERO, () => _pumpEventQueue(times - 1)); | 864 return new Future.delayed(Duration.ZERO, () => _pumpEventQueue(times - 1)); |
| 732 } | 865 } |
| 733 | |
| 734 /** | |
| 735 * Remove and return the first item in the given [set]. | |
| 736 */ | |
| 737 static Object/*=T*/ _removeFirst/*<T>*/(LinkedHashSet<Object/*=T*/ > set) { | |
| 738 Object/*=T*/ element = set.first; | |
| 739 set.remove(element); | |
| 740 return element; | |
| 741 } | |
| 742 } | 866 } |
| 743 | 867 |
| 744 /** | 868 /** |
| 745 * The result of analyzing of a single file. | 869 * The result of analyzing of a single file. |
| 746 * | 870 * |
| 747 * These results are self-consistent, i.e. [content], [contentHash], the | 871 * These results are self-consistent, i.e. [content], [contentHash], the |
| 748 * resolved [unit] correspond to each other. All referenced elements, even | 872 * resolved [unit] correspond to each other. All referenced elements, even |
| 749 * external ones, are also self-consistent. But none of the results is | 873 * external ones, are also self-consistent. But none of the results is |
| 750 * guaranteed to be consistent with the state of the files. | 874 * guaranteed to be consistent with the state of the files. |
| 751 * | 875 * |
| (...skipping 270 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1022 /** | 1146 /** |
| 1023 * Complete the [signal] future if it is not completed yet. It is safe to | 1147 * Complete the [signal] future if it is not completed yet. It is safe to |
| 1024 * call this method multiple times, but the [signal] will complete only once. | 1148 * call this method multiple times, but the [signal] will complete only once. |
| 1025 */ | 1149 */ |
| 1026 void notify() { | 1150 void notify() { |
| 1027 if (!_completer.isCompleted) { | 1151 if (!_completer.isCompleted) { |
| 1028 _completer.complete(null); | 1152 _completer.complete(null); |
| 1029 } | 1153 } |
| 1030 } | 1154 } |
| 1031 } | 1155 } |
| OLD | NEW |