@@ -168,14 +168,61 @@ class ToolDefinition {
168168 }
169169}
170170
171+ /// Manages the creation of a single snapshot file in a context where multiple
172+ /// async functions could be trying to use and/or create it.
173+ ///
174+ /// To use:
175+ ///
176+ /// var s = new Snapshot(...);
177+ ///
178+ /// if (s.needsSnapshot) {
179+ /// // create s.snapshotFile, then call:
180+ /// s.snapshotCompleted();
181+ /// } else {
182+ /// await snapshotValid();
183+ /// // use existing s.snapshotFile;
184+ /// }
185+ ///
186+ class Snapshot {
187+ File _snapshotFile;
188+ File get snapshotFile => _snapshotFile;
189+ final Completer _snapshotCompleter = Completer ();
190+
191+ Snapshot (Directory snapshotCache, String toolPath, int serial) {
192+ if (toolPath.endsWith ('.snapshot' )) {
193+ _needsSnapshot = false ;
194+ _snapshotFile = File (toolPath);
195+ snapshotCompleted ();
196+ } else {
197+ _snapshotFile =
198+ File (pathLib.join (snapshotCache.absolute.path, 'snapshot_$serial ' ));
199+ }
200+ }
201+
202+ bool _needsSnapshot = true ;
203+
204+ /// Will return true precisely once, unless [toolPath] was already a snapshot.
205+ /// In that case, will always return false.
206+ bool get needsSnapshot {
207+ if (_needsSnapshot == true ) {
208+ _needsSnapshot = false ;
209+ return true ;
210+ }
211+ return _needsSnapshot;
212+ }
213+
214+ Future <void > snapshotValid () => _snapshotCompleter.future;
215+ void snapshotCompleted () => _snapshotCompleter.complete ();
216+ }
217+
171218/// A singleton that keeps track of cached snapshot files. The [dispose]
172219/// function must be called before process exit to clean up snapshots in the
173220/// cache.
174221class SnapshotCache {
175222 static SnapshotCache _instance;
176223
177224 Directory snapshotCache;
178- final Map <String , File > snapshots = {};
225+ final Map <String , Snapshot > snapshots = {};
179226 int _serial = 0 ;
180227
181228 SnapshotCache ._()
@@ -187,15 +234,13 @@ class SnapshotCache {
187234 return _instance;
188235 }
189236
190- File getSnapshot (String toolPath) {
237+ Snapshot getSnapshot (String toolPath) {
191238 if (snapshots.containsKey (toolPath)) {
192239 return snapshots[toolPath];
193240 }
194- File snapshot =
195- File (pathLib.join (snapshotCache.absolute.path, 'snapshot_$_serial ' ));
241+ snapshots[toolPath] = new Snapshot (snapshotCache, toolPath, _serial);
196242 _serial++ ;
197- snapshots[toolPath] = snapshot;
198- return snapshot;
243+ return snapshots[toolPath];
199244 }
200245
201246 void dispose () {
@@ -217,43 +262,26 @@ class DartToolDefinition extends ToolDefinition {
217262 assert (args[0 ] == command.first);
218263 // Set up flags to create a new snapshot, if needed, and use the first run as the training
219264 // run.
220- File snapshotFile = await getSnapshotFile ();
221- if (snapshotFile.existsSync ()) {
222- // replace the first argument with the path to the snapshot.
223- args[0 ] = snapshotFile.absolute.path;
224- } else {
265+ Snapshot snapshot = SnapshotCache .instance.getSnapshot (command.first);
266+ File snapshotFile = snapshot.snapshotFile;
267+ bool needsSnapshot = snapshot.needsSnapshot;
268+ if (needsSnapshot) {
225269 args.insertAll (0 , [
226270 '--snapshot=${snapshotFile .absolute .path }' ,
227271 '--snapshot_kind=app-jit'
228272 ]);
273+ } else {
274+ await snapshot.snapshotValid ();
275+ // replace the first argument with the path to the snapshot.
276+ args[0 ] = snapshotFile.absolute.path;
229277 }
230278 return new Tuple2 (Platform .resolvedExecutable,
231- _snapshotCompleter.isCompleted ? null : _snapshotCompleter.complete );
279+ needsSnapshot ? snapshot.snapshotCompleted : null );
232280 }
233281
234282 DartToolDefinition (
235283 List <String > command, List <String > setupCommand, String description)
236- : super (command, setupCommand, description) {
237- // If the dart tool is already a snapshot, then we just use that.
238- if (command[0 ].endsWith ('.snapshot' )) {
239- _snapshotPath = File (command[0 ]);
240- _snapshotCompleter.complete ();
241- }
242- }
243-
244- final Completer _snapshotCompleter = new Completer ();
245-
246- /// If the tool has a pre-built snapshot, it will be stored here.
247- File _snapshotPath;
248-
249- Future <File > getSnapshotFile () async {
250- if (_snapshotPath == null ) {
251- _snapshotPath = SnapshotCache .instance.getSnapshot (command.first);
252- } else {
253- await _snapshotCompleter.future;
254- }
255- return _snapshotPath;
256- }
284+ : super (command, setupCommand, description);
257285}
258286
259287/// A configuration class that can interpret [ToolDefinition] s from a YAML map.
0 commit comments