Skip to content

Commit ba8a0bb

Browse files
committed
Gracefully handle isolate exceptions
1 parent 8185fd9 commit ba8a0bb

File tree

4 files changed

+97
-48
lines changed

4 files changed

+97
-48
lines changed

webdev/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
## 0.1.1
22

33
- Checks for a dependency on `build_web_compilers`.
4+
- Gracefully handle exceptions.
45

56
## 0.1.0
67

webdev/bin/webdev.dart

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

55
import 'dart:async';
66
import 'dart:io';
7+
import 'dart:isolate';
78

89
import 'package:args/command_runner.dart';
910
import 'package:io/ansi.dart';
@@ -35,5 +36,9 @@ Future main(List<String> args) async {
3536
print(' ${e.path}');
3637
}
3738
exitCode = ExitCode.config.code;
39+
} on IsolateSpawnException catch (e) {
40+
print(red.wrap('An unexpected exception has occurred.'));
41+
print(e.message);
42+
exitCode = ExitCode.software.code;
3843
}
3944
}

webdev/lib/src/command/build_runner_command_base.dart

Lines changed: 61 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -53,26 +53,33 @@ abstract class BuildRunnerCommandBase extends Command<int> {
5353
stderr.writeln(new Trace.parse(trace).terse);
5454
if (exitCode == 0) exitCode = 1;
5555
});
56-
await Isolate.spawnUri(buildRunnerScript, arguments, messagePort.sendPort,
57-
onExit: exitPort.sendPort,
58-
onError: errorPort.sendPort,
59-
automaticPackageResolution: true);
60-
StreamSubscription exitCodeListener;
61-
exitCodeListener = messagePort.listen((isolateExitCode) {
62-
if (isolateExitCode is! int) {
63-
throw new StateError(
64-
'Bad response from isolate, expected an exit code but got '
65-
'$isolateExitCode');
66-
}
67-
exitCode = isolateExitCode as int;
68-
exitCodeListener.cancel();
69-
exitCodeListener = null;
70-
});
71-
await exitPort.first;
72-
await errorListener.cancel();
73-
await exitCodeListener?.cancel();
7456

75-
return exitCode;
57+
try {
58+
await Isolate.spawnUri(buildRunnerScript, arguments, messagePort.sendPort,
59+
onExit: exitPort.sendPort,
60+
onError: errorPort.sendPort,
61+
automaticPackageResolution: true);
62+
StreamSubscription exitCodeListener;
63+
exitCodeListener = messagePort.listen((isolateExitCode) {
64+
if (isolateExitCode is! int) {
65+
throw new StateError(
66+
'Bad response from isolate, expected an exit code but got '
67+
'$isolateExitCode');
68+
}
69+
exitCode = isolateExitCode as int;
70+
exitCodeListener.cancel();
71+
exitCodeListener = null;
72+
});
73+
await exitPort.first;
74+
await errorListener.cancel();
75+
await exitCodeListener?.cancel();
76+
77+
return exitCode;
78+
} finally {
79+
exitPort.close();
80+
errorPort.close();
81+
messagePort.close();
82+
}
7683
}
7784
}
7885

@@ -90,37 +97,43 @@ Future<Uri> _buildRunnerScript() async {
9097
var exitPort = new ReceivePort();
9198
var errorPort = new ReceivePort();
9299

93-
await Isolate.spawnUri(dataUri, [], messagePort.sendPort,
94-
onExit: exitPort.sendPort,
95-
onError: errorPort.sendPort,
96-
errorsAreFatal: true,
97-
packageConfig: new Uri.file(_packagesFileName));
98-
99-
var allErrorsFuture = errorPort.forEach((error) {
100-
var errorList = error as List;
101-
var message = errorList[0] as String;
102-
var stack = new StackTrace.fromString(errorList[1] as String);
103-
104-
stderr.writeln(message);
105-
stderr.writeln(stack);
106-
});
107-
108-
var items = await Future.wait([
109-
messagePort.toList(),
110-
allErrorsFuture,
111-
exitPort.first.whenComplete(() {
112-
messagePort.close();
113-
errorPort.close();
114-
})
115-
]);
100+
try {
101+
await Isolate.spawnUri(dataUri, [], messagePort.sendPort,
102+
onExit: exitPort.sendPort,
103+
onError: errorPort.sendPort,
104+
errorsAreFatal: true,
105+
packageConfig: new Uri.file(_packagesFileName));
116106

117-
var messages = items[0] as List;
118-
if (messages.isEmpty) {
119-
throw new StateError('An error occurred while bootstrapping.');
120-
}
107+
var allErrorsFuture = errorPort.forEach((error) {
108+
var errorList = error as List;
109+
var message = errorList[0] as String;
110+
var stack = new StackTrace.fromString(errorList[1] as String);
121111

122-
assert(messages.length == 1);
123-
return new Uri.file(messages.single as String);
112+
stderr.writeln(message);
113+
stderr.writeln(stack);
114+
});
115+
116+
var items = await Future.wait([
117+
messagePort.toList(),
118+
allErrorsFuture,
119+
exitPort.first.whenComplete(() {
120+
messagePort.close();
121+
errorPort.close();
122+
})
123+
]);
124+
125+
var messages = items[0] as List;
126+
if (messages.isEmpty) {
127+
throw new StateError('An error occurred while bootstrapping.');
128+
}
129+
130+
assert(messages.length == 1);
131+
return new Uri.file(messages.single as String);
132+
} finally {
133+
messagePort.close();
134+
exitPort.close();
135+
errorPort.close();
136+
}
124137
}
125138

126139
const _bootstrapScript = r'''

webdev/test/integration_test.dart

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,4 +127,34 @@ packages:
127127
contains('A `.packages` file does not exist in the target directory.'));
128128
await process.shouldExit(78);
129129
});
130+
131+
test('should fail gracefully if there is an isolate error', () async {
132+
await d.file('pubspec.lock', '''
133+
# Copy-pasted from a valid run
134+
packages:
135+
build_runner:
136+
dependency: "direct main"
137+
description:
138+
name: build_runner
139+
url: "https://pub.dartlang.org"
140+
source: hosted
141+
version: "0.8.0"
142+
build_web_compilers:
143+
dependency: "direct main"
144+
description:
145+
name: build_web_compilers
146+
url: "https://pub.dartlang.org"
147+
source: hosted
148+
version: "0.3.4+2"
149+
''').create();
150+
151+
await d.file('.packages', '').create();
152+
153+
var process = await TestProcess.start('dart', [_webdevBin, 'build'],
154+
workingDirectory: d.sandbox);
155+
var output = (await process.stdoutStream().join('\n')).trim();
156+
157+
expect(output, contains('An unexpected exception has occurred.'));
158+
await process.shouldExit(70);
159+
});
130160
}

0 commit comments

Comments
 (0)