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

Side by Side Diff: pkg/analyzer/lib/src/dart/analysis/driver.dart

Issue 2487003002: Use AnalysisDriverScheduler to schedule work across multiple AnalysisDriver(s). (Closed)
Patch Set: Created 4 years, 1 month 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 (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
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
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
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
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
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 }
OLDNEW
« no previous file with comments | « pkg/analysis_server/lib/src/analysis_server.dart ('k') | pkg/analyzer/test/src/dart/analysis/driver_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698