OLD | NEW |
---|---|
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 import 'package:analyzer/file_system/file_system.dart'; | 5 import 'dart:async'; |
6 import 'dart:io'; | |
7 import 'dart:isolate'; | |
8 | |
6 import 'package:analyzer/src/dart/analysis/byte_store.dart'; | 9 import 'package:analyzer/src/dart/analysis/byte_store.dart'; |
10 import 'package:path/path.dart'; | |
7 | 11 |
8 /** | 12 /** |
9 * [ByteStore] that stores values as [File]s. | 13 * [ByteStore] that stores values as files. |
10 * | |
11 * TODO(scheglov) Add some eviction policies. | |
12 */ | 14 */ |
13 class FileByteStore implements ByteStore { | 15 class FileByteStore implements ByteStore { |
14 final Folder folder; | 16 static bool _evictSendPortShouldBePrepared = true; |
17 static SendPort _evictSendPort; | |
15 | 18 |
16 FileByteStore(this.folder); | 19 final String _cachePath; |
20 final String _tempName = 'temp_${pid}'; | |
21 final int _maxSizeBytes; | |
22 | |
23 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.
| |
24 bool _evictionIsolateIsRunning = false; | |
25 | |
26 FileByteStore(this._cachePath, this._maxSizeBytes) { | |
27 _requestCacheCleanUp(); | |
28 } | |
17 | 29 |
18 @override | 30 @override |
19 List<int> get(String key) { | 31 List<int> get(String key) { |
20 try { | 32 try { |
21 File file = folder.getChildAssumingFile(key); | 33 return _getFileForKey(key).readAsBytesSync(); |
22 return file.readAsBytesSync(); | |
23 } catch (_) { | 34 } catch (_) { |
24 return null; | 35 return null; |
25 } | 36 } |
26 } | 37 } |
27 | 38 |
28 @override | 39 @override |
29 void put(String key, List<int> bytes) { | 40 void put(String key, List<int> bytes) { |
30 try { | 41 try { |
31 File file = folder.getChildAssumingFile(key); | 42 File tempFile = _getFileForKey(_tempName); |
32 file.writeAsBytesSync(bytes); | 43 tempFile.writeAsBytesSync(bytes); |
44 File file = _getFileForKey(key); | |
45 tempFile.renameSync(file.path); | |
46 // Update the current size. | |
47 _currentSizeBytes += bytes.length; | |
48 if (_currentSizeBytes > _maxSizeBytes ~/ 8) { | |
49 _requestCacheCleanUp(); | |
50 } | |
33 } catch (_) {} | 51 } catch (_) {} |
34 } | 52 } |
53 | |
54 File _getFileForKey(String key) { | |
55 return new File(join(_cachePath, key)); | |
56 } | |
57 | |
58 /** | |
59 * If the cache clean up process has not been requested yet, request it. | |
60 */ | |
61 Future<Null> _requestCacheCleanUp() async { | |
62 if (_evictSendPortShouldBePrepared) { | |
63 _evictSendPortShouldBePrepared = false; | |
64 ReceivePort response = new ReceivePort(); | |
65 await Isolate.spawn(_cacheCleanUpFunction, response.sendPort); | |
66 _evictSendPort = await response.first as SendPort; | |
67 } else { | |
68 while (_evictSendPort == null) { | |
69 await new Future.delayed(new Duration(milliseconds: 100), () {}); | |
70 } | |
71 } | |
72 | |
73 if (!_evictionIsolateIsRunning) { | |
74 _evictionIsolateIsRunning = true; | |
75 try { | |
76 ReceivePort response = new ReceivePort(); | |
77 _evictSendPort.send([_cachePath, _maxSizeBytes, response.sendPort]); | |
78 await response.first; | |
79 } finally { | |
80 _evictionIsolateIsRunning = false; | |
81 _currentSizeBytes = 0; | |
82 } | |
83 } | |
84 } | |
85 | |
86 /** | |
87 * 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.
| |
88 * and | |
89 */ | |
90 static void _cacheCleanUpFunction(SendPort initialReplyTo) { | |
91 ReceivePort port = new ReceivePort(); | |
92 initialReplyTo.send(port.sendPort); | |
93 port.listen((args) async { | |
94 if (args is List && | |
95 args.length == 3 && | |
96 args[0] is String && | |
97 args[1] is int && | |
98 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?
| |
99 String cachePath = args[0] as String; | |
100 int maxSizeBytes = args[1] as int; | |
101 await _evictFromFolder(cachePath, maxSizeBytes); | |
102 // Let that client know that we're done. | |
103 SendPort replyTo = args[2] as SendPort; | |
104 replyTo.send(true); | |
105 } | |
106 }); | |
107 } | |
108 | |
109 static Future<Null> _evictFromFolder( | |
110 String cachePath, int maxSizeBytes) async { | |
111 // Prepare the list of files and their statistics. | |
112 List<File> files = <File>[]; | |
113 Map<File, FileStat> fileStatMap = {}; | |
114 int currentSizeBytes = 0; | |
115 List<FileSystemEntity> resources = new Directory(cachePath).listSync(); | |
116 for (FileSystemEntity resource in resources) { | |
117 if (resource is File) { | |
118 try { | |
119 FileStat fileStat = await resource.stat(); | |
120 files.add(resource); | |
121 fileStatMap[resource] = fileStat; | |
122 currentSizeBytes += fileStat.size; | |
123 } catch (_) {} | |
124 } | |
125 } | |
126 files.sort((a, b) { | |
127 return fileStatMap[a].accessed.millisecondsSinceEpoch - | |
128 fileStatMap[b].accessed.millisecondsSinceEpoch; | |
129 }); | |
130 | |
131 // Delete files until the current size is less than the max. | |
132 for (File file in files) { | |
133 if (currentSizeBytes < maxSizeBytes) { | |
134 break; | |
135 } | |
136 try { | |
137 await file.delete(); | |
138 } catch (_) {} | |
139 currentSizeBytes -= fileStatMap[file].size; | |
140 } | |
141 } | |
35 } | 142 } |
OLD | NEW |