Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 08d5d51

Browse files
[flutter_tools] Instantiate shutdown hooks before localfilesystem (#110693)
1 parent 1560d0c commit 08d5d51

File tree

11 files changed

+79
-48
lines changed

11 files changed

+79
-48
lines changed

packages/flutter_tools/lib/executable.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ Future<void> main(List<String> args) async {
127127
},
128128
PreRunValidator: () => PreRunValidator(fileSystem: globals.fs),
129129
},
130+
shutdownHooks: globals.shutdownHooks,
130131
);
131132
}
132133

packages/flutter_tools/lib/runner.dart

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ Future<int> run(
3434
bool? reportCrashes,
3535
String? flutterVersion,
3636
Map<Type, Generator>? overrides,
37+
required ShutdownHooks shutdownHooks,
3738
}) async {
3839
if (muteCommandLogging) {
3940
// Remove the verbose option; for help and doctor, users don't need to see
@@ -64,7 +65,7 @@ Future<int> run(
6465
// Triggering [runZoned]'s error callback does not necessarily mean that
6566
// we stopped executing the body. See https://github.com/dart-lang/sdk/issues/42150.
6667
if (firstError == null) {
67-
return await _exit(0);
68+
return await _exit(0, shutdownHooks: shutdownHooks);
6869
}
6970

7071
// We already hit some error, so don't return success. The error path
@@ -74,15 +75,15 @@ Future<int> run(
7475
// This catches all exceptions to send to crash logging, etc.
7576
firstError = error;
7677
firstStackTrace = stackTrace;
77-
return _handleToolError(error, stackTrace, verbose, args, reportCrashes!, getVersion);
78+
return _handleToolError(error, stackTrace, verbose, args, reportCrashes!, getVersion, shutdownHooks);
7879
}
7980
}, onError: (Object error, StackTrace stackTrace) async { // ignore: deprecated_member_use
8081
// If sending a crash report throws an error into the zone, we don't want
8182
// to re-try sending the crash report with *that* error. Rather, we want
8283
// to send the original error that triggered the crash report.
8384
firstError ??= error;
8485
firstStackTrace ??= stackTrace;
85-
await _handleToolError(firstError!, firstStackTrace, verbose, args, reportCrashes!, getVersion);
86+
await _handleToolError(firstError!, firstStackTrace, verbose, args, reportCrashes!, getVersion, shutdownHooks);
8687
});
8788
}, overrides: overrides);
8889
}
@@ -94,27 +95,28 @@ Future<int> _handleToolError(
9495
List<String> args,
9596
bool reportCrashes,
9697
String Function() getFlutterVersion,
98+
ShutdownHooks shutdownHooks,
9799
) async {
98100
if (error is UsageException) {
99101
globals.printError('${error.message}\n');
100102
globals.printError("Run 'flutter -h' (or 'flutter <command> -h') for available flutter commands and options.");
101103
// Argument error exit code.
102-
return _exit(64);
104+
return _exit(64, shutdownHooks: shutdownHooks);
103105
} else if (error is ToolExit) {
104106
if (error.message != null) {
105107
globals.printError(error.message!);
106108
}
107109
if (verbose) {
108110
globals.printError('\n$stackTrace\n');
109111
}
110-
return _exit(error.exitCode ?? 1);
112+
return _exit(error.exitCode ?? 1, shutdownHooks: shutdownHooks);
111113
} else if (error is ProcessExit) {
112114
// We've caught an exit code.
113115
if (error.immediate) {
114116
exit(error.exitCode);
115117
return error.exitCode;
116118
} else {
117-
return _exit(error.exitCode);
119+
return _exit(error.exitCode, shutdownHooks: shutdownHooks);
118120
}
119121
} else {
120122
// We've crashed; emit a log report.
@@ -124,7 +126,7 @@ Future<int> _handleToolError(
124126
// Print the stack trace on the bots - don't write a crash report.
125127
globals.stdio.stderrWrite('$error\n');
126128
globals.stdio.stderrWrite('$stackTrace\n');
127-
return _exit(1);
129+
return _exit(1, shutdownHooks: shutdownHooks);
128130
}
129131

130132
// Report to both [Usage] and [CrashReportSender].
@@ -165,7 +167,7 @@ Future<int> _handleToolError(
165167
final File file = await _createLocalCrashReport(details);
166168
await globals.crashReporter!.informUser(details, file);
167169

168-
return _exit(1);
170+
return _exit(1, shutdownHooks: shutdownHooks);
169171
// This catch catches all exceptions to ensure the message below is printed.
170172
} catch (error, st) { // ignore: avoid_catches_without_on_clauses
171173
globals.stdio.stderrWrite(
@@ -228,7 +230,7 @@ Future<File> _createLocalCrashReport(CrashDetails details) async {
228230
return crashFile;
229231
}
230232

231-
Future<int> _exit(int code) async {
233+
Future<int> _exit(int code, {required ShutdownHooks shutdownHooks}) async {
232234
// Prints the welcome message if needed.
233235
globals.flutterUsage.printWelcome();
234236

@@ -241,7 +243,7 @@ Future<int> _exit(int code) async {
241243
}
242244

243245
// Run shutdown hooks before flushing logs
244-
await globals.shutdownHooks!.runShutdownHooks();
246+
await shutdownHooks.runShutdownHooks(globals.logger);
245247

246248
final Completer<void> completer = Completer<void>();
247249

packages/flutter_tools/lib/src/base/file_system.dart

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -177,17 +177,18 @@ File getUniqueFile(Directory dir, String baseName, String ext) {
177177
/// directories and files that the tool creates under the system temporary
178178
/// directory when the tool exits either normally or when killed by a signal.
179179
class LocalFileSystem extends local_fs.LocalFileSystem {
180-
LocalFileSystem(this._signals, this._fatalSignals, this._shutdownHooks);
180+
LocalFileSystem(this._signals, this._fatalSignals, this.shutdownHooks);
181181

182182
@visibleForTesting
183183
LocalFileSystem.test({
184184
required Signals signals,
185185
List<ProcessSignal> fatalSignals = Signals.defaultExitSignals,
186-
}) : this(signals, fatalSignals, null);
186+
}) : this(signals, fatalSignals, ShutdownHooks());
187187

188188
Directory? _systemTemp;
189189
final Map<ProcessSignal, Object> _signalTokens = <ProcessSignal, Object>{};
190-
final ShutdownHooks? _shutdownHooks;
190+
191+
final ShutdownHooks shutdownHooks;
191192

192193
Future<void> dispose() async {
193194
_tryToDeleteTemp();
@@ -206,7 +207,7 @@ class LocalFileSystem extends local_fs.LocalFileSystem {
206207
_systemTemp?.deleteSync(recursive: true);
207208
}
208209
} on FileSystemException {
209-
// ignore.
210+
// ignore
210211
}
211212
_systemTemp = null;
212213
}
@@ -239,7 +240,7 @@ class LocalFileSystem extends local_fs.LocalFileSystem {
239240
}
240241
// Make sure that the temporary directory is cleaned up when the tool
241242
// exits normally.
242-
_shutdownHooks?.addShutdownHook(
243+
shutdownHooks.addShutdownHook(
243244
_tryToDeleteTemp,
244245
);
245246
}

packages/flutter_tools/lib/src/base/process.dart

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import 'dart:async';
66

7+
import 'package:meta/meta.dart';
78
import 'package:process/process.dart';
89

910
import '../convert.dart';
@@ -13,7 +14,7 @@ import 'logger.dart';
1314
typedef StringConverter = String? Function(String string);
1415

1516
/// A function that will be run before the VM exits.
16-
typedef ShutdownHook = FutureOr<dynamic> Function();
17+
typedef ShutdownHook = FutureOr<void> Function();
1718

1819
// TODO(ianh): We have way too many ways to run subprocesses in this project.
1920
// Convert most of these into one or more lightweight wrappers around the
@@ -22,34 +23,34 @@ typedef ShutdownHook = FutureOr<dynamic> Function();
2223
// for more details.
2324

2425
abstract class ShutdownHooks {
25-
factory ShutdownHooks({
26-
required Logger logger,
27-
}) => _DefaultShutdownHooks(
28-
logger: logger,
29-
);
26+
factory ShutdownHooks() => _DefaultShutdownHooks();
3027

3128
/// Registers a [ShutdownHook] to be executed before the VM exits.
3229
void addShutdownHook(
3330
ShutdownHook shutdownHook
3431
);
3532

33+
@visibleForTesting
34+
List<ShutdownHook> get registeredHooks;
35+
3636
/// Runs all registered shutdown hooks and returns a future that completes when
3737
/// all such hooks have finished.
3838
///
3939
/// Shutdown hooks will be run in groups by their [ShutdownStage]. All shutdown
4040
/// hooks within a given stage will be started in parallel and will be
4141
/// guaranteed to run to completion before shutdown hooks in the next stage are
4242
/// started.
43-
Future<void> runShutdownHooks();
43+
///
44+
/// This class is constructed before the [Logger], so it cannot be direct
45+
/// injected in the constructor.
46+
Future<void> runShutdownHooks(Logger logger);
4447
}
4548

4649
class _DefaultShutdownHooks implements ShutdownHooks {
47-
_DefaultShutdownHooks({
48-
required Logger logger,
49-
}) : _logger = logger;
50+
_DefaultShutdownHooks();
5051

51-
final Logger _logger;
52-
final List<ShutdownHook> _shutdownHooks = <ShutdownHook>[];
52+
@override
53+
final List<ShutdownHook> registeredHooks = <ShutdownHook>[];
5354

5455
bool _shutdownHooksRunning = false;
5556

@@ -58,16 +59,18 @@ class _DefaultShutdownHooks implements ShutdownHooks {
5859
ShutdownHook shutdownHook
5960
) {
6061
assert(!_shutdownHooksRunning);
61-
_shutdownHooks.add(shutdownHook);
62+
registeredHooks.add(shutdownHook);
6263
}
6364

6465
@override
65-
Future<void> runShutdownHooks() async {
66-
_logger.printTrace('Running shutdown hooks');
66+
Future<void> runShutdownHooks(Logger logger) async {
67+
logger.printTrace(
68+
'Running ${registeredHooks.length} shutdown hook${registeredHooks.length == 1 ? '' : 's'}',
69+
);
6770
_shutdownHooksRunning = true;
6871
try {
6972
final List<Future<dynamic>> futures = <Future<dynamic>>[];
70-
for (final ShutdownHook shutdownHook in _shutdownHooks) {
73+
for (final ShutdownHook shutdownHook in registeredHooks) {
7174
final FutureOr<dynamic> result = shutdownHook();
7275
if (result is Future<dynamic>) {
7376
futures.add(result);
@@ -77,7 +80,7 @@ class _DefaultShutdownHooks implements ShutdownHooks {
7780
} finally {
7881
_shutdownHooksRunning = false;
7982
}
80-
_logger.printTrace('Shutdown hooks complete');
83+
logger.printTrace('Shutdown hooks complete');
8184
}
8285
}
8386

packages/flutter_tools/lib/src/context_runner.dart

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,6 @@ Future<T> runInContext<T>(
313313
platform: globals.platform,
314314
usage: globals.flutterUsage,
315315
),
316-
ShutdownHooks: () => ShutdownHooks(logger: globals.logger),
317316
Stdio: () => Stdio(),
318317
SystemClock: () => const SystemClock(),
319318
Usage: () => Usage(

packages/flutter_tools/lib/src/globals.dart

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,11 @@ PlistParser? _plistInstance;
248248
/// The global template renderer.
249249
TemplateRenderer get templateRenderer => context.get<TemplateRenderer>()!;
250250

251-
ShutdownHooks? get shutdownHooks => context.get<ShutdownHooks>();
251+
/// Global [ShutdownHooks] that should be run before the tool process exits.
252+
///
253+
/// This is depended on by [localFileSystem] which is called before any
254+
/// [Context] is set up, and thus this cannot be a Context getter.
255+
final ShutdownHooks shutdownHooks = ShutdownHooks();
252256

253257
// Unless we're in a test of this class's signal handling features, we must
254258
// have only one instance created with the singleton LocalSignals instance

packages/flutter_tools/test/general.shard/base/file_system_test.dart

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import 'package:file_testing/file_testing.dart';
1010
import 'package:flutter_tools/src/base/common.dart';
1111
import 'package:flutter_tools/src/base/file_system.dart';
1212
import 'package:flutter_tools/src/base/io.dart';
13+
import 'package:flutter_tools/src/base/logger.dart';
1314
import 'package:flutter_tools/src/base/platform.dart';
1415
import 'package:flutter_tools/src/base/signals.dart';
1516
import 'package:test/fake.dart';
@@ -162,6 +163,21 @@ void main() {
162163
signalUnderTest = ProcessSignal(fakeSignal);
163164
});
164165

166+
testWithoutContext('runs shutdown hooks', () async {
167+
final Signals signals = Signals.test();
168+
final LocalFileSystem localFileSystem = LocalFileSystem.test(
169+
signals: signals,
170+
);
171+
final Directory temp = localFileSystem.systemTempDirectory;
172+
173+
expect(temp.existsSync(), isTrue);
174+
expect(localFileSystem.shutdownHooks.registeredHooks, hasLength(1));
175+
final BufferLogger logger = BufferLogger.test();
176+
await localFileSystem.shutdownHooks.runShutdownHooks(logger);
177+
expect(temp.existsSync(), isFalse);
178+
expect(logger.traceText, contains('Running 1 shutdown hook'));
179+
});
180+
165181
testWithoutContext('deletes system temp entry on a fatal signal', () async {
166182
final Completer<void> completer = Completer<void>();
167183
final Signals signals = Signals.test();

packages/flutter_tools/test/general.shard/base/process_test.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,13 @@ void main() {
4242
int i = 1;
4343
int? cleanup;
4444

45-
final ShutdownHooks shutdownHooks = ShutdownHooks(logger: BufferLogger.test());
45+
final ShutdownHooks shutdownHooks = ShutdownHooks();
4646

4747
shutdownHooks.addShutdownHook(() async {
4848
cleanup = i++;
4949
});
5050

51-
await shutdownHooks.runShutdownHooks();
51+
await shutdownHooks.runShutdownHooks(BufferLogger.test());
5252

5353
expect(cleanup, 1);
5454
});

packages/flutter_tools/test/general.shard/runner/runner_test.dart

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import 'package:flutter_tools/src/base/file_system.dart';
1313
import 'package:flutter_tools/src/base/io.dart' as io;
1414
import 'package:flutter_tools/src/base/net.dart';
1515
import 'package:flutter_tools/src/base/platform.dart';
16+
import 'package:flutter_tools/src/base/process.dart';
1617
import 'package:flutter_tools/src/base/user_messages.dart';
1718
import 'package:flutter_tools/src/cache.dart';
1819
import 'package:flutter_tools/src/globals.dart' as globals;
@@ -68,6 +69,7 @@ void main() {
6869
// This flutterVersion disables crash reporting.
6970
flutterVersion: '[user-branch]/',
7071
reportCrashes: true,
72+
shutdownHooks: ShutdownHooks(),
7173
));
7274
return null;
7375
},
@@ -120,6 +122,7 @@ void main() {
120122
// This flutterVersion disables crash reporting.
121123
flutterVersion: '[user-branch]/',
122124
reportCrashes: true,
125+
shutdownHooks: ShutdownHooks(),
123126
));
124127
return null;
125128
},
@@ -159,16 +162,17 @@ void main() {
159162
// catch it in a zone.
160163
unawaited(runZoned<Future<void>>(
161164
() {
162-
unawaited(runner.run(
163-
<String>['crash'],
164-
() => <FlutterCommand>[
165-
CrashingFlutterCommand(),
166-
],
167-
// This flutterVersion disables crash reporting.
168-
flutterVersion: '[user-branch]/',
169-
reportCrashes: true,
170-
));
171-
return null;
165+
unawaited(runner.run(
166+
<String>['crash'],
167+
() => <FlutterCommand>[
168+
CrashingFlutterCommand(),
169+
],
170+
// This flutterVersion disables crash reporting.
171+
flutterVersion: '[user-branch]/',
172+
reportCrashes: true,
173+
shutdownHooks: ShutdownHooks(),
174+
));
175+
return null;
172176
},
173177
onError: (Object error, StackTrace stack) { // ignore: deprecated_member_use
174178
expect(firstExitCode, isNotNull);

packages/flutter_tools/test/integration.shard/command_output_test.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ void main() {
6868
]);
6969

7070
// Check for message only printed in verbose mode.
71-
expect(result.stdout, contains('Running shutdown hooks'));
71+
expect(result.stdout, contains('Shutdown hooks complete'));
7272
});
7373

7474
testWithoutContext('flutter config contains all features', () async {

packages/flutter_tools/test/integration.shard/overall_experience_test.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -384,7 +384,8 @@ void main() {
384384
return null;
385385
}),
386386
Barrier('Performing hot restart...'.padRight(progressMessageWidth)),
387-
Multiple(<Pattern>[RegExp(r'^Restarted application in [0-9]+ms.$'), 'called main', 'called paint'], handler: (String line) {
387+
// This could look like 'Restarted application in 1,237ms.'
388+
Multiple(<Pattern>[RegExp(r'^Restarted application in .+m?s.$'), 'called main', 'called paint'], handler: (String line) {
388389
return 'q';
389390
}),
390391
const Barrier('Application finished.'),

0 commit comments

Comments
 (0)