Index: pkg/analyzer/lib/src/dart/analysis/file_byte_store.dart |
diff --git a/pkg/analyzer/lib/src/dart/analysis/file_byte_store.dart b/pkg/analyzer/lib/src/dart/analysis/file_byte_store.dart |
index 49e668fe08f769ff796c77547e3b546830684fa9..02031ed7d0da4aa7003ade9e8ffc768d65af1123 100644 |
--- a/pkg/analyzer/lib/src/dart/analysis/file_byte_store.dart |
+++ b/pkg/analyzer/lib/src/dart/analysis/file_byte_store.dart |
@@ -2,24 +2,35 @@ |
// for details. All rights reserved. Use of this source code is governed by a |
// BSD-style license that can be found in the LICENSE file. |
-import 'package:analyzer/file_system/file_system.dart'; |
+import 'dart:async'; |
+import 'dart:io'; |
+import 'dart:isolate'; |
+ |
import 'package:analyzer/src/dart/analysis/byte_store.dart'; |
+import 'package:path/path.dart'; |
/** |
- * [ByteStore] that stores values as [File]s. |
- * |
- * TODO(scheglov) Add some eviction policies. |
+ * [ByteStore] that stores values as files. |
*/ |
class FileByteStore implements ByteStore { |
- final Folder folder; |
+ static bool _evictSendPortShouldBePrepared = true; |
+ static SendPort _evictSendPort; |
+ |
+ final String _cachePath; |
+ final String _tempName = 'temp_${pid}'; |
+ final int _maxSizeBytes; |
- FileByteStore(this.folder); |
+ int _currentSizeBytes = 0; |
Paul Berry
2016/11/13 15:18:17
Rename this to something like "_bytesWrittenSinceC
scheglov
2016/11/14 16:51:47
Done.
|
+ bool _evictionIsolateIsRunning = false; |
+ |
+ FileByteStore(this._cachePath, this._maxSizeBytes) { |
+ _requestCacheCleanUp(); |
+ } |
@override |
List<int> get(String key) { |
try { |
- File file = folder.getChildAssumingFile(key); |
- return file.readAsBytesSync(); |
+ return _getFileForKey(key).readAsBytesSync(); |
} catch (_) { |
return null; |
} |
@@ -28,8 +39,104 @@ class FileByteStore implements ByteStore { |
@override |
void put(String key, List<int> bytes) { |
try { |
- File file = folder.getChildAssumingFile(key); |
- file.writeAsBytesSync(bytes); |
+ File tempFile = _getFileForKey(_tempName); |
+ tempFile.writeAsBytesSync(bytes); |
+ File file = _getFileForKey(key); |
+ tempFile.renameSync(file.path); |
+ // Update the current size. |
+ _currentSizeBytes += bytes.length; |
+ if (_currentSizeBytes > _maxSizeBytes ~/ 8) { |
+ _requestCacheCleanUp(); |
+ } |
} catch (_) {} |
} |
+ |
+ File _getFileForKey(String key) { |
+ return new File(join(_cachePath, key)); |
+ } |
+ |
+ /** |
+ * If the cache clean up process has not been requested yet, request it. |
+ */ |
+ Future<Null> _requestCacheCleanUp() async { |
+ if (_evictSendPortShouldBePrepared) { |
+ _evictSendPortShouldBePrepared = false; |
+ ReceivePort response = new ReceivePort(); |
+ await Isolate.spawn(_cacheCleanUpFunction, response.sendPort); |
+ _evictSendPort = await response.first as SendPort; |
+ } else { |
+ while (_evictSendPort == null) { |
+ await new Future.delayed(new Duration(milliseconds: 100), () {}); |
+ } |
+ } |
+ |
+ if (!_evictionIsolateIsRunning) { |
+ _evictionIsolateIsRunning = true; |
+ try { |
+ ReceivePort response = new ReceivePort(); |
+ _evictSendPort.send([_cachePath, _maxSizeBytes, response.sendPort]); |
+ await response.first; |
+ } finally { |
+ _evictionIsolateIsRunning = false; |
+ _currentSizeBytes = 0; |
+ } |
+ } |
+ } |
+ |
+ /** |
+ * This function is stated in a new isolate, receives cache clean up requests |
Paul Berry
2016/11/13 15:18:17
s/stated/started/
scheglov
2016/11/14 16:51:47
Done.
|
+ * and |
+ */ |
+ static void _cacheCleanUpFunction(SendPort initialReplyTo) { |
+ ReceivePort port = new ReceivePort(); |
+ initialReplyTo.send(port.sendPort); |
+ port.listen((args) async { |
+ if (args is List && |
+ args.length == 3 && |
+ args[0] is String && |
+ args[1] is int && |
+ args[2] is SendPort) { |
Paul Berry
2016/11/13 15:18:17
It seems weird to send a list of objects to the is
scheglov
2016/11/14 16:51:47
Unfortunately this does not work.
SendPort.send s
Paul Berry
2016/11/14 21:19:28
+rmacnak, is this due to a bug in the VM?
|
+ String cachePath = args[0] as String; |
+ int maxSizeBytes = args[1] as int; |
+ await _evictFromFolder(cachePath, maxSizeBytes); |
+ // Let that client know that we're done. |
+ SendPort replyTo = args[2] as SendPort; |
+ replyTo.send(true); |
+ } |
+ }); |
+ } |
+ |
+ static Future<Null> _evictFromFolder( |
+ String cachePath, int maxSizeBytes) async { |
+ // Prepare the list of files and their statistics. |
+ List<File> files = <File>[]; |
+ Map<File, FileStat> fileStatMap = {}; |
+ int currentSizeBytes = 0; |
+ List<FileSystemEntity> resources = new Directory(cachePath).listSync(); |
+ for (FileSystemEntity resource in resources) { |
+ if (resource is File) { |
+ try { |
+ FileStat fileStat = await resource.stat(); |
+ files.add(resource); |
+ fileStatMap[resource] = fileStat; |
+ currentSizeBytes += fileStat.size; |
+ } catch (_) {} |
+ } |
+ } |
+ files.sort((a, b) { |
+ return fileStatMap[a].accessed.millisecondsSinceEpoch - |
+ fileStatMap[b].accessed.millisecondsSinceEpoch; |
+ }); |
+ |
+ // Delete files until the current size is less than the max. |
+ for (File file in files) { |
+ if (currentSizeBytes < maxSizeBytes) { |
+ break; |
+ } |
+ try { |
+ await file.delete(); |
+ } catch (_) {} |
+ currentSizeBytes -= fileStatMap[file].size; |
+ } |
+ } |
} |