Skip to content

Commit c0f78f3

Browse files
authored
Use sync I/O. (#4000)
1 parent 33a0a24 commit c0f78f3

File tree

8 files changed

+121
-265
lines changed

8 files changed

+121
-265
lines changed

build/lib/src/state/filesystem.dart

+5-114
Original file line numberDiff line numberDiff line change
@@ -6,36 +6,20 @@ import 'dart:convert';
66
import 'dart:io';
77
import 'dart:typed_data';
88

9-
import 'package:pool/pool.dart';
10-
119
/// The filesystem the build is running on.
1210
///
1311
/// Methods behave as the `dart:io` methods with the same names, with some
1412
/// exceptions noted in the docs.
1513
abstract interface class Filesystem {
16-
/// Whether the file exists.
17-
Future<bool> exists(String path);
18-
1914
/// Whether the file exists.
2015
bool existsSync(String path);
2116

22-
/// Reads a file as a string.
23-
Future<String> readAsString(String path, {Encoding encoding = utf8});
24-
2517
/// Reads a file as a string.
2618
String readAsStringSync(String path, {Encoding encoding = utf8});
2719

28-
/// Reads a file as bytes.
29-
Future<Uint8List> readAsBytes(String path);
30-
3120
/// Reads a file as bytes.
3221
Uint8List readAsBytesSync(String path);
3322

34-
/// Deletes a file.
35-
///
36-
/// If the file does not exist, does nothing.
37-
Future<void> delete(String path);
38-
3923
/// Deletes a file.
4024
///
4125
/// If the file does not exist, does nothing.
@@ -44,7 +28,7 @@ abstract interface class Filesystem {
4428
/// Deletes a directory recursively.
4529
///
4630
/// If the directory does not exist, does nothing.
47-
Future<void> deleteDirectory(String path);
31+
void deleteDirectorySync(String path);
4832

4933
/// Writes a file.
5034
///
@@ -55,48 +39,20 @@ abstract interface class Filesystem {
5539
Encoding encoding = utf8,
5640
});
5741

58-
/// Writes a file.
59-
///
60-
/// Creates enclosing directories as needed if they don't exist.
61-
Future<void> writeAsString(
62-
String path,
63-
String contents, {
64-
Encoding encoding = utf8,
65-
});
66-
6742
/// Writes a file.
6843
///
6944
/// Creates enclosing directories as needed if they don't exist.
7045
void writeAsBytesSync(String path, List<int> contents);
71-
72-
/// Writes a file.
73-
///
74-
/// Creates enclosing directories as needed if they don't exist.
75-
Future<void> writeAsBytes(String path, List<int> contents);
7646
}
7747

7848
/// The `dart:io` filesystem.
7949
class IoFilesystem implements Filesystem {
80-
/// Pool for async file operations.
81-
final _pool = Pool(32);
82-
83-
@override
84-
Future<bool> exists(String path) => _pool.withResource(File(path).exists);
85-
8650
@override
8751
bool existsSync(String path) => File(path).existsSync();
8852

89-
@override
90-
Future<Uint8List> readAsBytes(String path) =>
91-
_pool.withResource(File(path).readAsBytes);
92-
9353
@override
9454
Uint8List readAsBytesSync(String path) => File(path).readAsBytesSync();
9555

96-
@override
97-
Future<String> readAsString(String path, {Encoding encoding = utf8}) =>
98-
_pool.withResource(() => File(path).readAsString(encoding: encoding));
99-
10056
@override
10157
String readAsStringSync(String path, {Encoding encoding = utf8}) =>
10258
File(path).readAsStringSync(encoding: encoding);
@@ -108,19 +64,9 @@ class IoFilesystem implements Filesystem {
10864
}
10965

11066
@override
111-
Future<void> delete(String path) {
112-
return _pool.withResource(() async {
113-
final file = File(path);
114-
if (await file.exists()) await file.delete();
115-
});
116-
}
117-
118-
@override
119-
Future<void> deleteDirectory(String path) {
120-
return _pool.withResource(() async {
121-
final directory = Directory(path);
122-
if (await directory.exists()) await directory.delete(recursive: true);
123-
});
67+
void deleteDirectorySync(String path) {
68+
final directory = Directory(path);
69+
if (directory.existsSync()) directory.deleteSync(recursive: true);
12470
}
12571

12672
@override
@@ -134,15 +80,6 @@ class IoFilesystem implements Filesystem {
13480
file.writeAsBytesSync(contents);
13581
}
13682

137-
@override
138-
Future<void> writeAsBytes(String path, List<int> contents) {
139-
return _pool.withResource(() async {
140-
final file = File(path);
141-
await file.parent.create(recursive: true);
142-
await file.writeAsBytes(contents);
143-
});
144-
}
145-
14683
@override
14784
void writeAsStringSync(
14885
String path,
@@ -153,19 +90,6 @@ class IoFilesystem implements Filesystem {
15390
file.parent.createSync(recursive: true);
15491
file.writeAsStringSync(contents, encoding: encoding);
15592
}
156-
157-
@override
158-
Future<void> writeAsString(
159-
String path,
160-
String contents, {
161-
Encoding encoding = utf8,
162-
}) {
163-
return _pool.withResource(() async {
164-
final file = File(path);
165-
await file.parent.create(recursive: true);
166-
await file.writeAsString(contents, encoding: encoding);
167-
});
168-
}
16993
}
17094

17195
/// An in-memory [Filesystem].
@@ -175,53 +99,30 @@ class InMemoryFilesystem implements Filesystem {
17599
/// The paths to all files present on the filesystem.
176100
Iterable<String> get filePaths => _files.keys;
177101

178-
@override
179-
Future<bool> exists(String path) => Future.value(_files.containsKey(path));
180-
181102
@override
182103
bool existsSync(String path) => _files.containsKey(path);
183104

184-
@override
185-
Future<Uint8List> readAsBytes(String path) => Future.value(_files[path]!);
186-
187105
@override
188106
Uint8List readAsBytesSync(String path) => _files[path]!;
189107

190-
@override
191-
Future<String> readAsString(String path, {Encoding encoding = utf8}) =>
192-
Future.value(encoding.decode(_files[path]!));
193-
194108
@override
195109
String readAsStringSync(String path, {Encoding encoding = utf8}) =>
196110
encoding.decode(_files[path]!);
197111

198-
@override
199-
Future<void> delete(String path) {
200-
_files.remove(path);
201-
return Future.value();
202-
}
203-
204112
@override
205113
void deleteSync(String path) => _files.remove(path);
206114

207115
@override
208-
Future<void> deleteDirectory(String path) {
116+
void deleteDirectorySync(String path) {
209117
final prefix = '$path/';
210118
_files.removeWhere((filePath, _) => filePath.startsWith(prefix));
211-
return Future.value();
212119
}
213120

214121
@override
215122
void writeAsBytesSync(String path, List<int> contents) {
216123
_files[path] = Uint8List.fromList(contents);
217124
}
218125

219-
@override
220-
Future<void> writeAsBytes(String path, List<int> contents) {
221-
_files[path] = Uint8List.fromList(contents);
222-
return Future.value();
223-
}
224-
225126
@override
226127
void writeAsStringSync(
227128
String path,
@@ -230,14 +131,4 @@ class InMemoryFilesystem implements Filesystem {
230131
}) {
231132
_files[path] = Uint8List.fromList(encoding.encode(contents));
232133
}
233-
234-
@override
235-
Future<void> writeAsString(
236-
String path,
237-
String contents, {
238-
Encoding encoding = utf8,
239-
}) {
240-
_files[path] = Uint8List.fromList(encoding.encode(contents));
241-
return Future.value();
242-
}
243134
}

build/lib/src/state/filesystem_cache.dart

+36-63
Original file line numberDiff line numberDiff line change
@@ -14,30 +14,25 @@ import 'lru_cache.dart';
1414
/// TODO(davidmorgan): benchmark, optimize the caching strategy.
1515
abstract interface class FilesystemCache {
1616
/// Clears all [ids] from all caches.
17-
///
18-
/// Waits for any pending reads to complete first.
19-
Future<void> invalidate(Iterable<AssetId> ids);
17+
void invalidate(Iterable<AssetId> ids);
2018

2119
/// Whether [id] exists.
2220
///
2321
/// Returns a cached result if available, or caches and returns `ifAbsent()`.
24-
Future<bool> exists(AssetId id, {required Future<bool> Function() ifAbsent});
22+
bool exists(AssetId id, {required bool Function() ifAbsent});
2523

2624
/// Reads [id] as bytes.
2725
///
2826
/// Returns a cached result if available, or caches and returns `ifAbsent()`.
29-
Future<Uint8List> readAsBytes(
30-
AssetId id, {
31-
required Future<Uint8List> Function() ifAbsent,
32-
});
27+
Uint8List readAsBytes(AssetId id, {required Uint8List Function() ifAbsent});
3328

3429
/// Reads [id] as a `String`.
3530
///
3631
/// Returns a cached result if available, or caches and returns `ifAbsent()`.
37-
Future<String> readAsString(
32+
String readAsString(
3833
AssetId id, {
3934
Encoding encoding = utf8,
40-
required Future<Uint8List> Function() ifAbsent,
35+
required Uint8List Function() ifAbsent,
4136
});
4237
}
4338

@@ -49,23 +44,18 @@ class PassthroughFilesystemCache implements FilesystemCache {
4944
Future<void> invalidate(Iterable<AssetId> ids) async {}
5045

5146
@override
52-
Future<bool> exists(
53-
AssetId id, {
54-
required Future<bool> Function() ifAbsent,
55-
}) => ifAbsent();
47+
bool exists(AssetId id, {required bool Function() ifAbsent}) => ifAbsent();
5648

5749
@override
58-
Future<Uint8List> readAsBytes(
59-
AssetId id, {
60-
required Future<Uint8List> Function() ifAbsent,
61-
}) => ifAbsent();
50+
Uint8List readAsBytes(AssetId id, {required Uint8List Function() ifAbsent}) =>
51+
ifAbsent();
6252

6353
@override
64-
Future<String> readAsString(
54+
String readAsString(
6555
AssetId id, {
6656
Encoding encoding = utf8,
67-
required Future<Uint8List> Function() ifAbsent,
68-
}) async => encoding.decode(await ifAbsent());
57+
required Uint8List Function() ifAbsent,
58+
}) => encoding.decode(ifAbsent());
6959
}
7060

7161
/// [FilesystemCache] that stores data in memory.
@@ -77,13 +67,10 @@ class InMemoryFilesystemCache implements FilesystemCache {
7767
(value) => value.lengthInBytes,
7868
);
7969

80-
/// Pending [readAsBytes] operations.
81-
final _pendingBytesContentCache = <AssetId, Future<Uint8List>>{};
82-
8370
/// Cached results of [exists].
8471
///
8572
/// Don't bother using an LRU cache for this since it's just booleans.
86-
final _canReadCache = <AssetId, Future<bool>>{};
73+
final _existsCache = <AssetId, bool>{};
8774

8875
/// Cached results of [readAsString].
8976
///
@@ -96,64 +83,50 @@ class InMemoryFilesystemCache implements FilesystemCache {
9683
(value) => value.length,
9784
);
9885

99-
/// Pending `readAsString` operations.
100-
final _pendingStringContentCache = <AssetId, Future<String>>{};
101-
10286
@override
10387
Future<void> invalidate(Iterable<AssetId> ids) async {
104-
// First finish all pending operations, as they will write to the cache.
105-
for (var id in ids) {
106-
await _canReadCache.remove(id);
107-
await _pendingBytesContentCache.remove(id);
108-
await _pendingStringContentCache.remove(id);
109-
}
11088
for (var id in ids) {
89+
_existsCache.remove(id);
11190
_bytesContentCache.remove(id);
11291
_stringContentCache.remove(id);
11392
}
11493
}
11594

11695
@override
117-
Future<bool> exists(
118-
AssetId id, {
119-
required Future<bool> Function() ifAbsent,
120-
}) => _canReadCache.putIfAbsent(id, ifAbsent);
96+
bool exists(AssetId id, {required bool Function() ifAbsent}) =>
97+
_existsCache.putIfAbsent(id, ifAbsent);
12198

12299
@override
123-
Future<Uint8List> readAsBytes(
124-
AssetId id, {
125-
required Future<Uint8List> Function() ifAbsent,
126-
}) {
127-
var cached = _bytesContentCache[id];
128-
if (cached != null) return Future.value(cached);
129-
130-
return _pendingBytesContentCache.putIfAbsent(id, () async {
131-
final result = await ifAbsent();
132-
_bytesContentCache[id] = result;
133-
unawaited(_pendingBytesContentCache.remove(id));
134-
return result;
135-
});
100+
Uint8List readAsBytes(AssetId id, {required Uint8List Function() ifAbsent}) {
101+
final maybeResult = _bytesContentCache[id];
102+
if (maybeResult != null) return maybeResult;
103+
104+
final result = ifAbsent();
105+
_bytesContentCache[id] = result;
106+
return result;
136107
}
137108

138109
@override
139-
Future<String> readAsString(
110+
String readAsString(
140111
AssetId id, {
141112
Encoding encoding = utf8,
142-
required Future<Uint8List> Function() ifAbsent,
143-
}) async {
113+
required Uint8List Function() ifAbsent,
114+
}) {
144115
if (encoding != utf8) {
145-
final bytes = await readAsBytes(id, ifAbsent: ifAbsent);
116+
final bytes = readAsBytes(id, ifAbsent: ifAbsent);
146117
return encoding.decode(bytes);
147118
}
148119

149-
var cached = _stringContentCache[id];
150-
if (cached != null) return cached;
120+
final maybeResult = _stringContentCache[id];
121+
if (maybeResult != null) return maybeResult;
151122

152-
return _pendingStringContentCache.putIfAbsent(id, () async {
153-
final bytes = await ifAbsent();
154-
final result = _stringContentCache[id] = utf8.decode(bytes);
155-
unawaited(_pendingStringContentCache.remove(id));
156-
return result;
157-
});
123+
var bytes = _bytesContentCache[id];
124+
if (bytes == null) {
125+
bytes = ifAbsent();
126+
_bytesContentCache[id] = bytes;
127+
}
128+
final result = utf8.decode(bytes);
129+
_stringContentCache[id] = result;
130+
return result;
158131
}
159132
}

0 commit comments

Comments
 (0)