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

Side by Side Diff: pkg/analyzer/lib/src/context/cache.dart

Issue 1133513003: Cache flushing implementation for the task model. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Fixes for review comments. Created 5 years, 7 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 | Annotate | Revision Log
« no previous file with comments | « no previous file | pkg/analyzer/lib/src/context/context.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2015, 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 library analyzer.src.context.cache; 5 library analyzer.src.context.cache;
6 6
7 import 'dart:collection'; 7 import 'dart:collection';
8 8
9 import 'package:analyzer/src/generated/ast.dart';
10 import 'package:analyzer/src/generated/engine.dart' 9 import 'package:analyzer/src/generated/engine.dart'
11 show AnalysisEngine, CacheState, InternalAnalysisContext, RetentionPriority; 10 show AnalysisEngine, CacheState, InternalAnalysisContext, RetentionPriority;
12 import 'package:analyzer/src/generated/html.dart';
13 import 'package:analyzer/src/generated/java_engine.dart'; 11 import 'package:analyzer/src/generated/java_engine.dart';
14 import 'package:analyzer/src/generated/source.dart'; 12 import 'package:analyzer/src/generated/source.dart';
15 import 'package:analyzer/src/generated/utilities_collection.dart'; 13 import 'package:analyzer/src/generated/utilities_collection.dart';
16 import 'package:analyzer/src/generated/utilities_general.dart'; 14 import 'package:analyzer/src/generated/utilities_general.dart';
17 import 'package:analyzer/task/model.dart'; 15 import 'package:analyzer/task/model.dart';
18 16
19 /** 17 /**
18 * Return `true` if the given [target] is a priority one.
19 */
20 typedef bool IsPriorityAnalysisTarget(AnalysisTarget target);
21
22 /**
20 * An LRU cache of results produced by analysis. 23 * An LRU cache of results produced by analysis.
21 */ 24 */
22 class AnalysisCache { 25 class AnalysisCache {
23 /** 26 /**
24 * A flag used to control whether trace information should be produced when 27 * A flag used to control whether trace information should be produced when
25 * the content of the cache is modified. 28 * the content of the cache is modified.
26 */ 29 */
27 static bool _TRACE_CHANGES = false; 30 static bool _TRACE_CHANGES = false;
28 31
29 /** 32 /**
30 * An array containing the partitions of which this cache is comprised. 33 * An array containing the partitions of which this cache is comprised.
31 */ 34 */
32 final List<CachePartition> _partitions; 35 final List<CachePartition> _partitions;
33 36
34 /** 37 /**
35 * Initialize a newly created cache to have the given [partitions]. The 38 * Initialize a newly created cache to have the given [partitions]. The
36 * partitions will be searched in the order in which they appear in the array, 39 * partitions will be searched in the order in which they appear in the array,
37 * so the most specific partition (usually an [SdkCachePartition]) should be 40 * so the most specific partition (usually an [SdkCachePartition]) should be
38 * first and the most general (usually a [UniversalCachePartition]) last. 41 * first and the most general (usually a [UniversalCachePartition]) last.
39 */ 42 */
40 AnalysisCache(this._partitions); 43 AnalysisCache(this._partitions) {
41 44 for (CachePartition partition in _partitions) {
42 /** 45 partition._cache = this;
43 * Return the number of entries in this cache that have an AST associated with 46 }
44 * them. 47 }
45 */
46 int get astSize => _partitions[_partitions.length - 1].astSize;
47 48
48 // TODO(brianwilkerson) Implement or delete this. 49 // TODO(brianwilkerson) Implement or delete this.
49 // /** 50 // /**
50 // * Return information about each of the partitions in this cache. 51 // * Return information about each of the partitions in this cache.
51 // */ 52 // */
52 // List<AnalysisContextStatistics_PartitionData> get partitionData { 53 // List<AnalysisContextStatistics_PartitionData> get partitionData {
53 // int count = _partitions.length; 54 // int count = _partitions.length;
54 // List<AnalysisContextStatistics_PartitionData> data = 55 // List<AnalysisContextStatistics_PartitionData> data =
55 // new List<AnalysisContextStatistics_PartitionData>(count); 56 // new List<AnalysisContextStatistics_PartitionData>(count);
56 // for (int i = 0; i < count; i++) { 57 // for (int i = 0; i < count; i++) {
57 // CachePartition partition = _partitions[i]; 58 // CachePartition partition = _partitions[i];
58 // data[i] = new AnalysisContextStatisticsImpl_PartitionDataImpl( 59 // data[i] = new AnalysisContextStatisticsImpl_PartitionDataImpl(
59 // partition.astSize, 60 // partition.astSize,
60 // partition.map.length); 61 // partition.map.length);
61 // } 62 // }
62 // return data; 63 // return data;
63 // } 64 // }
64 65
65 /** 66 /**
66 * Record that the AST associated with the given [target] was just read from
67 * the cache.
68 */
69 void accessedAst(AnalysisTarget target) {
70 // TODO(brianwilkerson) Extract this logic to a helper method (here and
71 // elsewhere)
72 int count = _partitions.length;
73 for (int i = 0; i < count; i++) {
74 if (_partitions[i].contains(target)) {
75 _partitions[i].accessedAst(target);
76 return;
77 }
78 }
79 }
80
81 /**
82 * Return the entry associated with the given [target]. 67 * Return the entry associated with the given [target].
83 */ 68 */
84 CacheEntry get(AnalysisTarget target) { 69 CacheEntry get(AnalysisTarget target) {
85 int count = _partitions.length; 70 int count = _partitions.length;
86 for (int i = 0; i < count; i++) { 71 for (int i = 0; i < count; i++) {
87 if (_partitions[i].contains(target)) { 72 if (_partitions[i].contains(target)) {
88 return _partitions[i].get(target); 73 return _partitions[i].get(target);
89 } 74 }
90 } 75 }
91 // 76 //
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
128 for (int i = 0; i < count; i++) { 113 for (int i = 0; i < count; i++) {
129 maps[i] = _partitions[i].map; 114 maps[i] = _partitions[i].map;
130 } 115 }
131 return new MultipleMapIterator<AnalysisTarget, CacheEntry>(maps); 116 return new MultipleMapIterator<AnalysisTarget, CacheEntry>(maps);
132 } 117 }
133 118
134 /** 119 /**
135 * Associate the given [entry] with the given [target]. 120 * Associate the given [entry] with the given [target].
136 */ 121 */
137 void put(AnalysisTarget target, CacheEntry entry) { 122 void put(AnalysisTarget target, CacheEntry entry) {
138 entry._cache = this;
139 entry._target = target;
140 entry.fixExceptionState(); 123 entry.fixExceptionState();
141 int count = _partitions.length; 124 int count = _partitions.length;
142 for (int i = 0; i < count; i++) { 125 for (int i = 0; i < count; i++) {
143 if (_partitions[i].contains(target)) { 126 if (_partitions[i].contains(target)) {
144 if (_TRACE_CHANGES) { 127 if (_TRACE_CHANGES) {
145 CacheEntry oldEntry = _partitions[i].get(target); 128 CacheEntry oldEntry = _partitions[i].get(target);
146 if (oldEntry == null) { 129 if (oldEntry == null) {
147 AnalysisEngine.instance.logger 130 AnalysisEngine.instance.logger
148 .logInformation('Added a cache entry for $target.'); 131 .logInformation('Added a cache entry for $target.');
149 } else { 132 } else {
(...skipping 21 matching lines...) Expand all
171 AnalysisEngine.instance.logger 154 AnalysisEngine.instance.logger
172 .logInformation('Removed the cache entry for $target.'); 155 .logInformation('Removed the cache entry for $target.');
173 } 156 }
174 _partitions[i].remove(target); 157 _partitions[i].remove(target);
175 return; 158 return;
176 } 159 }
177 } 160 }
178 } 161 }
179 162
180 /** 163 /**
181 * Record that the AST associated with the given [target] was just removed
182 * from the cache.
183 */
184 void removedAst(AnalysisTarget target) {
185 int count = _partitions.length;
186 for (int i = 0; i < count; i++) {
187 if (_partitions[i].contains(target)) {
188 _partitions[i].removedAst(target);
189 return;
190 }
191 }
192 }
193
194 /**
195 * Return the number of targets that are mapped to cache entries. 164 * Return the number of targets that are mapped to cache entries.
196 */ 165 */
197 int size() { 166 int size() {
198 int size = 0; 167 int size = 0;
199 int count = _partitions.length; 168 int count = _partitions.length;
200 for (int i = 0; i < count; i++) { 169 for (int i = 0; i < count; i++) {
201 size += _partitions[i].size(); 170 size += _partitions[i].size();
202 } 171 }
203 return size; 172 return size;
204 } 173 }
205 174
206 /**
207 * Record that the AST associated with the given [target] was just stored to
208 * the cache.
209 */
210 void storedAst(AnalysisTarget target) {
211 int count = _partitions.length;
212 for (int i = 0; i < count; i++) {
213 if (_partitions[i].contains(target)) {
214 _partitions[i].storedAst(target);
215 return;
216 }
217 }
218 }
219
220 ResultData _getDataFor(TargetedResult result) { 175 ResultData _getDataFor(TargetedResult result) {
221 AnalysisTarget target = result.target; 176 AnalysisTarget target = result.target;
222 int count = _partitions.length; 177 int count = _partitions.length;
223 for (int i = 0; i < count; i++) { 178 for (int i = 0; i < count; i++) {
224 if (_partitions[i].contains(target)) { 179 if (_partitions[i].contains(target)) {
225 CacheEntry entry = _partitions[i].get(target); 180 CacheEntry entry = _partitions[i].get(target);
226 return entry._getResultData(result.result); 181 return entry._getResultData(result.result);
227 } 182 }
228 } 183 }
229 return null; 184 return null;
230 } 185 }
231 } 186 }
232 187
233 /** 188 /**
234 * The information cached by an analysis context about an individual target. 189 * The information cached by an analysis context about an individual target.
235 */ 190 */
236 class CacheEntry { 191 class CacheEntry {
237 /** 192 /**
238 * The index of the flag indicating whether the source was explicitly added to 193 * The index of the flag indicating whether the source was explicitly added to
239 * the context or whether the source was implicitly added because it was 194 * the context or whether the source was implicitly added because it was
240 * referenced by another source. 195 * referenced by another source.
241 */ 196 */
242 static int _EXPLICITLY_ADDED_FLAG = 0; 197 static int _EXPLICITLY_ADDED_FLAG = 0;
243 198
244 /** 199 /**
245 * The cache that contains this entry. 200 * The partition that is responsible for this entry.
246 */ 201 */
247 AnalysisCache _cache; 202 CachePartition _partition;
248 203
249 /** 204 /**
250 * The target this entry is about. 205 * The target this entry is about.
251 */ 206 */
252 AnalysisTarget _target; 207 AnalysisTarget _target;
253 208
254 /** 209 /**
255 * The most recent time at which the state of the target matched the state 210 * The most recent time at which the state of the target matched the state
256 * represented by this entry. 211 * represented by this entry.
257 */ 212 */
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
289 244
290 /** 245 /**
291 * Set whether the source was explicitly added to the context to match the 246 * Set whether the source was explicitly added to the context to match the
292 * [explicitlyAdded] flag. 247 * [explicitlyAdded] flag.
293 */ 248 */
294 void set explicitlyAdded(bool explicitlyAdded) { 249 void set explicitlyAdded(bool explicitlyAdded) {
295 _setFlag(_EXPLICITLY_ADDED_FLAG, explicitlyAdded); 250 _setFlag(_EXPLICITLY_ADDED_FLAG, explicitlyAdded);
296 } 251 }
297 252
298 /** 253 /**
299 * Return `true` if this entry contains at least one result whose value is an
300 * AST structure.
301 */
302 bool get hasAstStructure {
303 for (ResultData data in _resultMap.values) {
304 if (data.value is AstNode || data.value is XmlNode) {
305 return true;
306 }
307 }
308 return false;
309 }
310
311 /**
312 * Fix the state of the [exception] to match the current state of the entry. 254 * Fix the state of the [exception] to match the current state of the entry.
313 */ 255 */
314 void fixExceptionState() { 256 void fixExceptionState() {
315 if (!hasErrorState()) { 257 if (!hasErrorState()) {
316 _exception = null; 258 _exception = null;
317 } 259 }
318 } 260 }
319 261
320 /** 262 /**
321 * Mark any AST structures associated with this cache entry as being flushed.
322 */
323 void flushAstStructures() {
324 _resultMap.forEach((ResultDescriptor descriptor, ResultData data) {
325 if (data.value is AstNode || data.value is XmlNode) {
326 _validateStateChange(descriptor, CacheState.FLUSHED);
327 data.state = CacheState.FLUSHED;
328 data.value = descriptor.defaultValue;
329 }
330 });
331 }
332
333 /**
334 * Return the memento of the result represented by the given [descriptor]. 263 * Return the memento of the result represented by the given [descriptor].
335 */ 264 */
336 Object getMemento(ResultDescriptor descriptor) { 265 Object getMemento(ResultDescriptor descriptor) {
337 ResultData data = _resultMap[descriptor]; 266 ResultData data = _resultMap[descriptor];
338 if (data == null) { 267 if (data == null) {
339 return null; 268 return null;
340 } 269 }
341 return data.memento; 270 return data.memento;
342 } 271 }
343 272
(...skipping 10 matching lines...) Expand all
354 283
355 /** 284 /**
356 * Return the value of the result represented by the given [descriptor], or 285 * Return the value of the result represented by the given [descriptor], or
357 * the default value for the result if this entry does not have a valid value. 286 * the default value for the result if this entry does not have a valid value.
358 */ 287 */
359 /*<V>*/ dynamic /*V*/ getValue(ResultDescriptor /*<V>*/ descriptor) { 288 /*<V>*/ dynamic /*V*/ getValue(ResultDescriptor /*<V>*/ descriptor) {
360 ResultData data = _resultMap[descriptor]; 289 ResultData data = _resultMap[descriptor];
361 if (data == null) { 290 if (data == null) {
362 return descriptor.defaultValue; 291 return descriptor.defaultValue;
363 } 292 }
293 if (_partition != null) {
294 _partition.resultAccessed(_target, descriptor);
295 }
364 return data.value; 296 return data.value;
365 } 297 }
366 298
367 /** 299 /**
368 * Return `true` if the state of any data value is [CacheState.ERROR]. 300 * Return `true` if the state of any data value is [CacheState.ERROR].
369 */ 301 */
370 bool hasErrorState() { 302 bool hasErrorState() {
371 for (ResultData data in _resultMap.values) { 303 for (ResultData data in _resultMap.values) {
372 if (data.state == CacheState.ERROR) { 304 if (data.state == CacheState.ERROR) {
373 return true; 305 return true;
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
408 if (descriptors == null || descriptors.isEmpty) { 340 if (descriptors == null || descriptors.isEmpty) {
409 throw new ArgumentError('at least one descriptor is expected'); 341 throw new ArgumentError('at least one descriptor is expected');
410 } 342 }
411 if (exception == null) { 343 if (exception == null) {
412 throw new ArgumentError('an exception is expected'); 344 throw new ArgumentError('an exception is expected');
413 } 345 }
414 this._exception = exception; 346 this._exception = exception;
415 for (ResultDescriptor descriptor in descriptors) { 347 for (ResultDescriptor descriptor in descriptors) {
416 ResultData data = _getResultData(descriptor); 348 ResultData data = _getResultData(descriptor);
417 TargetedResult thisResult = new TargetedResult(_target, descriptor); 349 TargetedResult thisResult = new TargetedResult(_target, descriptor);
418 data.invalidate(_cache, thisResult, CacheState.ERROR); 350 data.invalidate(_partition, thisResult, CacheState.ERROR);
419 } 351 }
420 } 352 }
421 353
422 /** 354 /**
423 * Set the state of the result represented by the given [descriptor] to the 355 * Set the state of the result represented by the given [descriptor] to the
424 * given [state]. 356 * given [state].
425 */ 357 */
426 void setState(ResultDescriptor descriptor, CacheState state) { 358 void setState(ResultDescriptor descriptor, CacheState state) {
427 if (state == CacheState.ERROR) { 359 if (state == CacheState.ERROR) {
428 throw new ArgumentError('use setErrorState() to set the state to ERROR'); 360 throw new ArgumentError('use setErrorState() to set the state to ERROR');
429 } 361 }
430 if (state == CacheState.VALID) { 362 if (state == CacheState.VALID) {
431 throw new ArgumentError('use setValue() to set the state to VALID'); 363 throw new ArgumentError('use setValue() to set the state to VALID');
432 } 364 }
433 _validateStateChange(descriptor, state); 365 _validateStateChange(descriptor, state);
434 if (state == CacheState.INVALID) { 366 if (state == CacheState.INVALID) {
435 ResultData data = _resultMap[descriptor]; 367 ResultData data = _resultMap[descriptor];
436 if (data != null) { 368 if (data != null) {
437 TargetedResult thisResult = new TargetedResult(_target, descriptor); 369 TargetedResult thisResult = new TargetedResult(_target, descriptor);
438 data.invalidate(_cache, thisResult, CacheState.INVALID); 370 data.invalidate(_partition, thisResult, CacheState.INVALID);
439 } 371 }
440 } else { 372 } else {
441 ResultData data = _getResultData(descriptor); 373 ResultData data = _getResultData(descriptor);
442 data.state = state; 374 data.state = state;
443 if (state != CacheState.IN_PROCESS) { 375 if (state != CacheState.IN_PROCESS) {
444 // 376 //
445 // If the state is in-process, we can leave the current value in the 377 // If the state is in-process, we can leave the current value in the
446 // cache for any 'get' methods to access. 378 // cache for any 'get' methods to access.
447 // 379 //
448 data.value = descriptor.defaultValue; 380 data.value = descriptor.defaultValue;
449 } 381 }
450 } 382 }
451 } 383 }
452 384
453 /** 385 /**
454 * Set the value of the result represented by the given [descriptor] to the 386 * Set the value of the result represented by the given [descriptor] to the
455 * given [value]. The optional [memento] may help to recompute [value] more 387 * given [value]. The optional [memento] may help to recompute [value] more
456 * efficiently after invalidation. 388 * efficiently after invalidation.
457 */ 389 */
458 /*<V>*/ void setValue(ResultDescriptor /*<V>*/ descriptor, dynamic /*V*/ 390 /*<V>*/ void setValue(ResultDescriptor /*<V>*/ descriptor, dynamic /*V*/
459 value, List<TargetedResult> dependedOn, Object memento) { 391 value, List<TargetedResult> dependedOn, Object memento) {
460 _validateStateChange(descriptor, CacheState.VALID); 392 _validateStateChange(descriptor, CacheState.VALID);
393 TargetedResult thisResult = new TargetedResult(_target, descriptor);
394 if (_partition != null) {
395 _partition.resultStored(thisResult, value);
396 }
461 ResultData data = _getResultData(descriptor); 397 ResultData data = _getResultData(descriptor);
462 { 398 data.invalidate(_partition, thisResult, CacheState.INVALID);
463 TargetedResult thisResult = new TargetedResult(_target, descriptor); 399 data.setDependedOnResults(_partition, thisResult, dependedOn);
464 data.invalidate(_cache, thisResult, CacheState.INVALID);
465 data.setDependedOnResults(_cache, thisResult, dependedOn);
466 }
467 data.state = CacheState.VALID; 400 data.state = CacheState.VALID;
468 data.value = value == null ? descriptor.defaultValue : value; 401 data.value = value == null ? descriptor.defaultValue : value;
469 data.memento = memento; 402 data.memento = memento;
470 } 403 }
471 404
472 @override 405 @override
473 String toString() { 406 String toString() {
474 StringBuffer buffer = new StringBuffer(); 407 StringBuffer buffer = new StringBuffer();
475 _writeOn(buffer); 408 _writeOn(buffer);
476 return buffer.toString(); 409 return buffer.toString();
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after
533 ResultData data = _resultMap[result]; 466 ResultData data = _resultMap[result];
534 buffer.write('; '); 467 buffer.write('; ');
535 buffer.write(result.toString()); 468 buffer.write(result.toString());
536 buffer.write(' = '); 469 buffer.write(' = ');
537 buffer.write(data.state); 470 buffer.write(data.state);
538 } 471 }
539 } 472 }
540 } 473 }
541 474
542 /** 475 /**
476 * An object that controls flushing of analysis results from the cache.
477 */
478 class CacheFlushManager<T> {
479 final IsPriorityAnalysisTarget isPriorityAnalysisTarget;
480 final ResultCachingPolicy<T> policy;
481 final int maxActiveSize;
482 final int maxIdleSize;
483
484 /**
485 * A map of the stored [TargetedResult] to their sizes.
486 */
487 final HashMap<TargetedResult, int> resultSizeMap =
488 new HashMap<TargetedResult, int>();
489
490 /**
491 * A linked set containing the most recently accessed results with the most
492 * recently used at the end of the list. When more results are added than the
493 * maximum size allowed then the least recently used results will be flushed
494 * from the cache.
495 */
496 final LinkedHashSet<TargetedResult> recentlyUsed =
497 new LinkedHashSet<TargetedResult>();
498
499 /**
500 * The current size of stored results.
501 */
502 int currentSize = 0;
503
504 /**
505 * The current maximum cache size.
506 */
507 int maxSize;
508
509 CacheFlushManager(
510 ResultCachingPolicy<T> policy, this.isPriorityAnalysisTarget)
511 : policy = policy,
512 maxActiveSize = policy.maxActiveSize,
513 maxIdleSize = policy.maxIdleSize,
514 maxSize = policy.maxIdleSize;
515
516 /**
517 * If [currentSize] is already less than [maxSize], returns an empty list.
518 * Otherwise returns [TargetedResult]s to flush from the cache to make
519 * [currentSize] less or equal to [maxSize].
520 *
521 * Results for priority files are never flushed, so this method might leave
522 * [currentSize] greater than [maxSize].
523 */
524 List<TargetedResult> flushToSize() {
525 // If still under the cap, done.
526 if (maxSize == -1 || currentSize <= maxSize) {
527 return TargetedResult.EMPTY_LIST;
528 }
529 // Flush results until we are under the cap.
530 List<TargetedResult> resultsToFlush = <TargetedResult>[];
531 for (TargetedResult result in recentlyUsed) {
532 if (isPriorityAnalysisTarget(result.target)) {
533 continue;
534 }
535 resultsToFlush.add(result);
536 int size = resultSizeMap.remove(result);
537 assert(size != null);
538 currentSize -= size;
539 if (currentSize <= maxSize) {
540 break;
541 }
542 }
543 recentlyUsed.removeAll(resultsToFlush);
544 return resultsToFlush;
545 }
546
547 /**
548 * Notifies this manager that the corresponding analysis context is active.
549 */
550 void madeActive() {
551 maxSize = maxActiveSize;
552 }
553
554 /**
555 * Notifies this manager that the corresponding analysis context is idle.
556 * Returns [TargetedResult]s that should be flushed from the cache.
557 */
558 List<TargetedResult> madeIdle() {
559 maxSize = maxIdleSize;
560 return flushToSize();
561 }
562
563 /**
564 * Records that the given [result] was just read from the cache.
565 */
566 void resultAccessed(TargetedResult result) {
567 if (recentlyUsed.remove(result)) {
568 recentlyUsed.add(result);
569 }
570 }
571
572 /**
573 * Records that the given [newResult] and [newValue] were stored to the cache.
574 * Returns [TargetedResult]s that should be flushed from the cache.
575 */
576 List<TargetedResult> resultStored(TargetedResult newResult, T newValue) {
577 if (!recentlyUsed.remove(newResult)) {
578 int size = policy.measure(newValue);
579 resultSizeMap[newResult] = size;
580 currentSize += size;
581 }
582 recentlyUsed.add(newResult);
583 return flushToSize();
584 }
585
586 /**
587 * Records that the given [target] was just removed from to the cache.
588 */
589 void targetRemoved(AnalysisTarget target) {
590 List<TargetedResult> resultsToRemove = <TargetedResult>[];
591 for (TargetedResult result in recentlyUsed) {
592 if (result.target == target) {
593 resultsToRemove.add(result);
594 int size = resultSizeMap.remove(result);
595 assert(size != null);
596 currentSize -= size;
597 }
598 }
599 recentlyUsed.removeAll(resultsToRemove);
600 }
601 }
602
603 /**
543 * A single partition in an LRU cache of information related to analysis. 604 * A single partition in an LRU cache of information related to analysis.
544 */ 605 */
545 abstract class CachePartition { 606 abstract class CachePartition {
546 /** 607 /**
608 * The [AnalysisCache] that owns this partition.
609 *
610 * TODO(scheglov) It seems wrong. Partitions may be shared between caches.
611 * But we need a way to go from every "enclosing" partition into "enclosed"
612 * ones.
613 */
614 AnalysisCache _cache;
615
616 /**
547 * The context that owns this partition. Multiple contexts can reference a 617 * The context that owns this partition. Multiple contexts can reference a
548 * partition, but only one context can own it. 618 * partition, but only one context can own it.
549 */ 619 */
550 final InternalAnalysisContext context; 620 final InternalAnalysisContext context;
551 621
552 /** 622 /**
553 * The maximum number of sources for which AST structures should be kept in 623 * A table mapping caching policies to the cache flush managers.
554 * the cache.
555 */ 624 */
556 int _maxCacheSize = 0; 625 final HashMap<ResultCachingPolicy, CacheFlushManager> _flushManagerMap =
557 626 new HashMap<ResultCachingPolicy, CacheFlushManager>();
558 /**
559 * The policy used to determine which results to remove from the cache.
560 */
561 final CacheRetentionPolicy _retentionPolicy;
562 627
563 /** 628 /**
564 * A table mapping the targets belonging to this partition to the information 629 * A table mapping the targets belonging to this partition to the information
565 * known about those targets. 630 * known about those targets.
566 */ 631 */
567 HashMap<AnalysisTarget, CacheEntry> _targetMap = 632 HashMap<AnalysisTarget, CacheEntry> _targetMap =
568 new HashMap<AnalysisTarget, CacheEntry>(); 633 new HashMap<AnalysisTarget, CacheEntry>();
569 634
570 /** 635 /**
571 * A list containing the most recently accessed targets with the most recently 636 * Initialize a newly created cache partition, belonging to the given
572 * used at the end of the list. When more targets are added than the maximum 637 * [context].
573 * allowed then the least recently used target will be removed and will have
574 * it's cached AST structure flushed.
575 */ 638 */
576 List<AnalysisTarget> _recentlyUsed = <AnalysisTarget>[]; 639 CachePartition(this.context);
577
578 /**
579 * Initialize a newly created cache partition, belonging to the given
580 * [context]. The partition will maintain at most [_maxCacheSize] AST
581 * structures in the cache, using the [_retentionPolicy] to determine which
582 * AST structures to flush.
583 */
584 CachePartition(this.context, this._maxCacheSize, this._retentionPolicy);
585
586 /**
587 * Return the number of entries in this partition that have an AST associated
588 * with them.
589 */
590 int get astSize {
591 int astSize = 0;
592 int count = _recentlyUsed.length;
593 for (int i = 0; i < count; i++) {
594 AnalysisTarget target = _recentlyUsed[i];
595 CacheEntry entry = _targetMap[target];
596 if (entry.hasAstStructure) {
597 astSize++;
598 }
599 }
600 return astSize;
601 }
602 640
603 /** 641 /**
604 * Return a table mapping the targets known to the context to the information 642 * Return a table mapping the targets known to the context to the information
605 * known about the target. 643 * known about the target.
606 * 644 *
607 * <b>Note:</b> This method is only visible for use by [AnalysisCache] and 645 * <b>Note:</b> This method is only visible for use by [AnalysisCache] and
608 * should not be used for any other purpose. 646 * should not be used for any other purpose.
609 */ 647 */
610 Map<AnalysisTarget, CacheEntry> get map => _targetMap; 648 Map<AnalysisTarget, CacheEntry> get map => _targetMap;
611 649
612 /** 650 /**
613 * Return the maximum size of the cache.
614 */
615 int get maxCacheSize => _maxCacheSize;
616
617 /**
618 * Set the maximum size of the cache to the given [size].
619 */
620 void set maxCacheSize(int size) {
621 _maxCacheSize = size;
622 while (_recentlyUsed.length > _maxCacheSize) {
623 if (!_flushAstFromCache()) {
624 break;
625 }
626 }
627 }
628
629 /**
630 * Record that the AST associated with the given [target] was just read from
631 * the cache.
632 */
633 void accessedAst(AnalysisTarget target) {
634 if (_recentlyUsed.remove(target)) {
635 _recentlyUsed.add(target);
636 return;
637 }
638 while (_recentlyUsed.length >= _maxCacheSize) {
639 if (!_flushAstFromCache()) {
640 break;
641 }
642 }
643 _recentlyUsed.add(target);
644 }
645
646 /**
647 * Return `true` if the given [target] is contained in this partition. 651 * Return `true` if the given [target] is contained in this partition.
648 */ 652 */
649 // TODO(brianwilkerson) Rename this to something more meaningful, such as 653 // TODO(brianwilkerson) Rename this to something more meaningful, such as
650 // isResponsibleFor. 654 // isResponsibleFor.
651 bool contains(AnalysisTarget target); 655 bool contains(AnalysisTarget target);
652 656
653 /** 657 /**
654 * Return the entry associated with the given [target]. 658 * Return the entry associated with the given [target].
655 */ 659 */
656 CacheEntry get(AnalysisTarget target) => _targetMap[target]; 660 CacheEntry get(AnalysisTarget target) => _targetMap[target];
657 661
658 /** 662 /**
659 * Return an iterator returning all of the map entries mapping targets to 663 * Return an iterator returning all of the map entries mapping targets to
660 * cache entries. 664 * cache entries.
661 */ 665 */
662 MapIterator<AnalysisTarget, CacheEntry> iterator() => 666 MapIterator<AnalysisTarget, CacheEntry> iterator() =>
663 new SingleMapIterator<AnalysisTarget, CacheEntry>(_targetMap); 667 new SingleMapIterator<AnalysisTarget, CacheEntry>(_targetMap);
664 668
665 /** 669 /**
666 * Associate the given [entry] with the given [target]. 670 * Associate the given [entry] with the given [target].
667 */ 671 */
668 void put(AnalysisTarget target, CacheEntry entry) { 672 void put(AnalysisTarget target, CacheEntry entry) {
673 if (entry._partition != null) {
674 throw new StateError(
675 'The entry for $target is already in ${entry._partition}');
676 }
677 entry._partition = this;
678 entry._target = target;
669 entry.fixExceptionState(); 679 entry.fixExceptionState();
670 _targetMap[target] = entry; 680 _targetMap[target] = entry;
671 } 681 }
672 682
673 /** 683 /**
674 * Remove all information related to the given [target] from this cache. 684 * Remove all information related to the given [target] from this cache.
675 */ 685 */
676 void remove(AnalysisTarget target) { 686 void remove(AnalysisTarget target) {
677 _recentlyUsed.remove(target); 687 for (CacheFlushManager flushManager in _flushManagerMap.values) {
688 flushManager.targetRemoved(target);
689 }
678 _targetMap.remove(target); 690 _targetMap.remove(target);
679 } 691 }
680 692
681 /** 693 /**
682 * Record that the AST associated with the given [target] was just removed 694 * Records that a value of the result described by the given [descriptor]
683 * from the cache. 695 * for the given [target] was just read from the cache.
684 */ 696 */
685 void removedAst(AnalysisTarget target) { 697 void resultAccessed(AnalysisTarget target, ResultDescriptor descriptor) {
686 _recentlyUsed.remove(target); 698 CacheFlushManager flushManager = _getFlushManager(descriptor);
699 TargetedResult result = new TargetedResult(target, descriptor);
700 flushManager.resultAccessed(result);
701 }
702
703 /**
704 * Records that the given [result] was just stored into the cache.
705 */
706 void resultStored(TargetedResult result, Object value) {
707 CacheFlushManager flushManager = _getFlushManager(result.result);
708 List<TargetedResult> resultsToFlush =
709 flushManager.resultStored(result, value);
710 for (TargetedResult result in resultsToFlush) {
711 CacheEntry entry = get(result.target);
712 ResultData data = entry._resultMap[result.result];
713 data.flush();
714 }
687 } 715 }
688 716
689 /** 717 /**
690 * Return the number of targets that are mapped to cache entries. 718 * Return the number of targets that are mapped to cache entries.
691 */ 719 */
692 int size() => _targetMap.length; 720 int size() => _targetMap.length;
693 721
694 /** 722 ResultData _getDataFor(TargetedResult result) {
695 * Record that the AST associated with the given [target] was just stored to 723 return _cache._getDataFor(result);
696 * the cache.
697 */
698 void storedAst(AnalysisTarget target) {
699 if (_recentlyUsed.contains(target)) {
700 return;
701 }
702 while (_recentlyUsed.length >= _maxCacheSize) {
703 if (!_flushAstFromCache()) {
704 break;
705 }
706 }
707 _recentlyUsed.add(target);
708 } 724 }
709 725
710 /** 726 /**
711 * Attempt to flush one AST structure from the cache. Return `true` if a 727 * Return the [CacheFlushManager] for the given [descriptor], not `null`.
712 * structure was flushed.
713 */ 728 */
714 bool _flushAstFromCache() { 729 CacheFlushManager _getFlushManager(ResultDescriptor descriptor) {
715 AnalysisTarget removedTarget = _removeAstToFlush(); 730 ResultCachingPolicy policy = descriptor.cachingPolicy;
716 if (removedTarget == null) { 731 return _flushManagerMap.putIfAbsent(
717 return false; 732 policy, () => new CacheFlushManager(policy, _isPriorityAnalysisTarget));
718 }
719 CacheEntry entry = _targetMap[removedTarget];
720 entry.flushAstStructures();
721 return true;
722 } 733 }
723 734
724 /** 735 bool _isPriorityAnalysisTarget(AnalysisTarget target) {
725 * Remove and return one target from the list of recently used targets whose 736 return context.priorityTargets.contains(target);
726 * AST structure can be flushed from the cache, or `null` if none of the
727 * targets can be removed. The target that will be returned will be the target
728 * that has been unreferenced for the longest period of time but that is not a
729 * priority for analysis.
730 */
731 AnalysisTarget _removeAstToFlush() {
732 int targetToRemove = -1;
733 for (int i = 0; i < _recentlyUsed.length; i++) {
734 AnalysisTarget target = _recentlyUsed[i];
735 RetentionPriority priority =
736 _retentionPolicy.getAstPriority(target, _targetMap[target]);
737 if (priority == RetentionPriority.LOW) {
738 return _recentlyUsed.removeAt(i);
739 } else if (priority == RetentionPriority.MEDIUM && targetToRemove < 0) {
740 targetToRemove = i;
741 }
742 }
743 if (targetToRemove < 0) {
744 // This happens if the retention policy returns a priority of HIGH for all
745 // of the targets that have been recently used. This is the case, for
746 // example, when the list of priority sources is bigger than the current
747 // cache size.
748 return null;
749 }
750 return _recentlyUsed.removeAt(targetToRemove);
751 } 737 }
752 } 738 }
753 739
754 /**
755 * A policy objecy that determines how important it is for data to be retained
756 * in the analysis cache.
757 */
758 abstract class CacheRetentionPolicy {
759 /**
760 * Return the priority of retaining the AST structure for the given [target]
761 * in the given [entry].
762 */
763 // TODO(brianwilkerson) Find a more general mechanism, probably based on task
764 // descriptors, to determine which data is still needed for analysis and which
765 // can be removed from the cache. Ideally we could (a) remove the need for
766 // this class and (b) be able to flush all result data (not just AST's).
767 RetentionPriority getAstPriority(AnalysisTarget target, CacheEntry entry);
768 }
769
770 /**
771 * A retention policy that will keep AST's in the cache if there is analysis
772 * information that needs to be computed for a source, where the computation is
773 * dependent on having the AST.
774 */
775 class DefaultRetentionPolicy implements CacheRetentionPolicy {
776 /**
777 * An instance of this class that can be shared.
778 */
779 static const DefaultRetentionPolicy POLICY = const DefaultRetentionPolicy();
780
781 /**
782 * Initialize a newly created instance of this class.
783 */
784 const DefaultRetentionPolicy();
785
786 // TODO(brianwilkerson) Implement or delete this.
787 // /**
788 // * Return `true` if there is analysis information in the given entry that ne eds to be
789 // * computed, where the computation is dependent on having the AST.
790 // *
791 // * @param dartEntry the entry being tested
792 // * @return `true` if there is analysis information that needs to be computed from the AST
793 // */
794 // bool astIsNeeded(DartEntry dartEntry) =>
795 // dartEntry.hasInvalidData(DartEntry.HINTS) ||
796 // dartEntry.hasInvalidData(DartEntry.LINTS) ||
797 // dartEntry.hasInvalidData(DartEntry.VERIFICATION_ERRORS) ||
798 // dartEntry.hasInvalidData(DartEntry.RESOLUTION_ERRORS);
799
800 @override
801 RetentionPriority getAstPriority(AnalysisTarget target, CacheEntry entry) {
802 // TODO(brianwilkerson) Implement or replace this.
803 // if (sourceEntry is DartEntry) {
804 // DartEntry dartEntry = sourceEntry;
805 // if (astIsNeeded(dartEntry)) {
806 // return RetentionPriority.MEDIUM;
807 // }
808 // }
809 // return RetentionPriority.LOW;
810 return RetentionPriority.MEDIUM;
811 }
812 }
813
814 /** 740 /**
815 * The data about a single analysis result that is stored in a [CacheEntry]. 741 * The data about a single analysis result that is stored in a [CacheEntry].
816 */ 742 */
817 // TODO(brianwilkerson) Consider making this a generic class so that the value 743 // TODO(brianwilkerson) Consider making this a generic class so that the value
818 // can be typed. 744 // can be typed.
819 class ResultData { 745 class ResultData {
820 /** 746 /**
821 * The [ResultDescriptor] this result is for. 747 * The [ResultDescriptor] this result is for.
822 */ 748 */
823 final ResultDescriptor descriptor; 749 final ResultDescriptor descriptor;
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
859 } 785 }
860 786
861 /** 787 /**
862 * Add the given [result] to the list of dependent results. 788 * Add the given [result] to the list of dependent results.
863 */ 789 */
864 void addDependentResult(TargetedResult result) { 790 void addDependentResult(TargetedResult result) {
865 dependentResults.add(result); 791 dependentResults.add(result);
866 } 792 }
867 793
868 /** 794 /**
795 * Flush this value.
796 */
797 void flush() {
798 state = CacheState.FLUSHED;
799 value = descriptor.defaultValue;
800 }
801
802 /**
869 * Invalidate this [ResultData] that corresponds to [thisResult] and 803 * Invalidate this [ResultData] that corresponds to [thisResult] and
870 * propagate invalidation to the results that depend on this one. 804 * propagate invalidation to the results that depend on this one.
871 */ 805 */
872 void invalidate( 806 void invalidate(CachePartition partition, TargetedResult thisResult,
873 AnalysisCache cache, TargetedResult thisResult, CacheState newState) { 807 CacheState newState) {
874 // Invalidate this result. 808 // Invalidate this result.
875 state = newState; 809 state = newState;
876 value = descriptor.defaultValue; 810 value = descriptor.defaultValue;
877 // Stop depending on other results. 811 // Stop depending on other results.
878 List<TargetedResult> dependedOnResults = this.dependedOnResults; 812 List<TargetedResult> dependedOnResults = this.dependedOnResults;
879 this.dependedOnResults = <TargetedResult>[]; 813 this.dependedOnResults = <TargetedResult>[];
880 dependedOnResults.forEach((TargetedResult dependedOnResult) { 814 dependedOnResults.forEach((TargetedResult dependedOnResult) {
881 ResultData data = cache._getDataFor(dependedOnResult); 815 ResultData data = partition._getDataFor(dependedOnResult);
882 data.removeDependentResult(thisResult); 816 data.removeDependentResult(thisResult);
883 }); 817 });
884 // Invalidate results that depend on this result. 818 // Invalidate results that depend on this result.
885 List<TargetedResult> dependentResults = this.dependentResults; 819 List<TargetedResult> dependentResults = this.dependentResults;
886 this.dependentResults = <TargetedResult>[]; 820 this.dependentResults = <TargetedResult>[];
887 dependentResults.forEach((TargetedResult dependentResult) { 821 dependentResults.forEach((TargetedResult dependentResult) {
888 ResultData data = cache._getDataFor(dependentResult); 822 ResultData data = partition._getDataFor(dependentResult);
889 data.invalidate(cache, dependentResult, newState); 823 data.invalidate(partition, dependentResult, newState);
890 }); 824 });
891 } 825 }
892 826
893 /** 827 /**
894 * Remove the given [result] from the list of dependent results. 828 * Remove the given [result] from the list of dependent results.
895 */ 829 */
896 void removeDependentResult(TargetedResult result) { 830 void removeDependentResult(TargetedResult result) {
897 dependentResults.remove(result); 831 dependentResults.remove(result);
898 } 832 }
899 833
900 /** 834 /**
901 * Set the [dependedOn] on which this result depends. 835 * Set the [dependedOn] on which this result depends.
902 */ 836 */
903 void setDependedOnResults(AnalysisCache cache, TargetedResult thisResult, 837 void setDependedOnResults(CachePartition partition, TargetedResult thisResult,
904 List<TargetedResult> dependedOn) { 838 List<TargetedResult> dependedOn) {
905 dependedOnResults.forEach((TargetedResult dependedOnResult) { 839 dependedOnResults.forEach((TargetedResult dependedOnResult) {
906 ResultData data = cache._getDataFor(dependedOnResult); 840 ResultData data = partition._getDataFor(dependedOnResult);
907 data.removeDependentResult(thisResult); 841 data.removeDependentResult(thisResult);
908 }); 842 });
909 dependedOnResults = dependedOn; 843 dependedOnResults = dependedOn;
910 dependedOnResults.forEach((TargetedResult dependentResult) { 844 dependedOnResults.forEach((TargetedResult dependentResult) {
911 ResultData data = cache._getDataFor(dependentResult); 845 ResultData data = partition._getDataFor(dependentResult);
912 data.addDependentResult(thisResult); 846 data.addDependentResult(thisResult);
913 }); 847 });
914 } 848 }
915 } 849 }
916 850
917 /** 851 /**
918 * A cache partition that contains all of the targets in the SDK. 852 * A cache partition that contains all of the targets in the SDK.
919 */ 853 */
920 class SdkCachePartition extends CachePartition { 854 class SdkCachePartition extends CachePartition {
921 /** 855 /**
922 * Initialize a newly created cache partition, belonging to the given 856 * Initialize a newly created cache partition, belonging to the given
923 * [context]. The partition will maintain at most [maxCacheSize] AST 857 * [context].
924 * structures in the cache.
925 */ 858 */
926 SdkCachePartition(InternalAnalysisContext context, int maxCacheSize) 859 SdkCachePartition(InternalAnalysisContext context) : super(context);
927 : super(context, maxCacheSize, DefaultRetentionPolicy.POLICY);
928 860
929 @override 861 @override
930 bool contains(AnalysisTarget target) { 862 bool contains(AnalysisTarget target) {
931 Source source = target.source; 863 Source source = target.source;
932 return source != null && source.isInSystemLibrary; 864 return source != null && source.isInSystemLibrary;
933 } 865 }
934 } 866 }
935 867
936 /** 868 /**
937 * A specification of a specific result computed for a specific target. 869 * A specification of a specific result computed for a specific target.
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
972 @override 904 @override
973 String toString() => '$result for $target'; 905 String toString() => '$result for $target';
974 } 906 }
975 907
976 /** 908 /**
977 * A cache partition that contains all targets not contained in other partitions . 909 * A cache partition that contains all targets not contained in other partitions .
978 */ 910 */
979 class UniversalCachePartition extends CachePartition { 911 class UniversalCachePartition extends CachePartition {
980 /** 912 /**
981 * Initialize a newly created cache partition, belonging to the given 913 * Initialize a newly created cache partition, belonging to the given
982 * [context]. The partition will maintain at most [maxCacheSize] AST 914 * [context].
983 * structures in the cache, using the [retentionPolicy] to determine which
984 * AST structures to flush.
985 */ 915 */
986 UniversalCachePartition(InternalAnalysisContext context, int maxCacheSize, 916 UniversalCachePartition(InternalAnalysisContext context) : super(context);
987 CacheRetentionPolicy retentionPolicy)
988 : super(context, maxCacheSize, retentionPolicy);
989 917
990 @override 918 @override
991 bool contains(AnalysisTarget target) => true; 919 bool contains(AnalysisTarget target) => true;
992 } 920 }
OLDNEW
« no previous file with comments | « no previous file | pkg/analyzer/lib/src/context/context.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698