Commit acd75c0a authored by John McCutchan's avatar John McCutchan Committed by GitHub

Improve flutter_tools handling of the device being unplugged while syncing DevFS (#9431)

- [x] Catch SocketErrors and handle them gracefully.
- [x] Print 'Lost connection to device' when the service protocol connection is severed unexpectedly.
- [x] Print 'Application finished' when the application exits otherwise.

After this PR:

```
Launching lib/main.dart on Nexus 7 in debug mode...
Running 'gradle assembleDebug'...                     1.2s
Built build/app/outputs/apk/app-debug.apk (21.7MB).
Syncing files to device...
Application finished.
DevFS sync failed. Lost connection to device: SocketException: OS Error: Connection refused, errno = 111, address = 127.0.0.1, port = 53062
Could not perform initial file synchronization.
```

Fixes #6705
parent b645e4ea
...@@ -220,6 +220,13 @@ class ServiceProtocolDevFSOperations implements DevFSOperations { ...@@ -220,6 +220,13 @@ class ServiceProtocolDevFSOperations implements DevFSOperations {
} }
} }
class DevFSException implements Exception {
DevFSException(this.message, [this.error, this.stackTrace]);
final String message;
final dynamic error;
final StackTrace stackTrace;
}
class _DevFSHttpWriter { class _DevFSHttpWriter {
_DevFSHttpWriter(this.fsName, VMService serviceProtocol) _DevFSHttpWriter(this.fsName, VMService serviceProtocol)
: httpAddress = serviceProtocol.httpAddress; : httpAddress = serviceProtocol.httpAddress;
...@@ -279,6 +286,11 @@ class _DevFSHttpWriter { ...@@ -279,6 +286,11 @@ class _DevFSHttpWriter {
await request.addStream(contents); await request.addStream(contents);
final HttpClientResponse response = await request.close(); final HttpClientResponse response = await request.close();
await response.drain<Null>(); await response.drain<Null>();
} on SocketException catch (socketException, stackTrace) {
// We have one completer and can get up to kMaxInFlight errors.
if (!_completer.isCompleted)
_completer.completeError(socketException, stackTrace);
return;
} catch (e) { } catch (e) {
if (retry < kMaxRetries) { if (retry < kMaxRetries) {
printTrace('Retrying writing "$deviceUri" to DevFS due to error: $e'); printTrace('Retrying writing "$deviceUri" to DevFS due to error: $e');
...@@ -422,8 +434,12 @@ class DevFS { ...@@ -422,8 +434,12 @@ class DevFS {
try { try {
await _httpWriter.write(dirtyEntries, await _httpWriter.write(dirtyEntries,
progressReporter: progressReporter); progressReporter: progressReporter);
} catch (e) { } on SocketException catch (socketException, stackTrace) {
printError("Could not update files on device: $e"); printTrace("DevFS sync failed. Lost connection to device: $socketException");
throw new DevFSException('Lost connection to device.', socketException, stackTrace);
} catch (exception, stackTrace) {
printError("Could not update files on device: $exception");
throw new DevFSException('Sync failed', exception, stackTrace);
} }
} else { } else {
// Make service protocol requests for each. // Make service protocol requests for each.
......
...@@ -49,6 +49,7 @@ abstract class ResidentRunner { ...@@ -49,6 +49,7 @@ abstract class ResidentRunner {
final bool usesTerminalUI; final bool usesTerminalUI;
final bool stayResident; final bool stayResident;
final Completer<int> _finished = new Completer<int>(); final Completer<int> _finished = new Completer<int>();
bool _stopped = false;
String _packagesFilePath; String _packagesFilePath;
String get packagesFilePath => _packagesFilePath; String get packagesFilePath => _packagesFilePath;
String _projectRootPath; String _projectRootPath;
...@@ -101,6 +102,7 @@ abstract class ResidentRunner { ...@@ -101,6 +102,7 @@ abstract class ResidentRunner {
} }
Future<Null> stop() async { Future<Null> stop() async {
_stopped = true;
await stopEchoingDeviceLog(); await stopEchoingDeviceLog();
await preStop(); await preStop();
return stopApp(); return stopApp();
...@@ -259,7 +261,7 @@ abstract class ResidentRunner { ...@@ -259,7 +261,7 @@ abstract class ResidentRunner {
service.done.then<Null>( service.done.then<Null>(
_serviceProtocolDone, _serviceProtocolDone,
onError: _serviceProtocolError onError: _serviceProtocolError
).whenComplete(appFinished); ).whenComplete(_serviceDisconnected);
} }
} }
...@@ -336,6 +338,18 @@ abstract class ResidentRunner { ...@@ -336,6 +338,18 @@ abstract class ResidentRunner {
} }
} }
void _serviceDisconnected() {
if (_stopped) {
// User requested the application exit.
return;
}
if (_finished.isCompleted)
return;
printStatus('Lost connection to device.');
_resetTerminal();
_finished.complete(0);
}
void appFinished() { void appFinished() {
if (_finished.isCompleted) if (_finished.isCompleted)
return; return;
......
...@@ -120,7 +120,6 @@ class HotRunner extends ResidentRunner { ...@@ -120,7 +120,6 @@ class HotRunner extends ResidentRunner {
} }
final bool devfsResult = await _updateDevFS(); final bool devfsResult = await _updateDevFS();
if (!devfsResult) { if (!devfsResult) {
printError('Could not perform initial file synchronization.');
return 3; return 3;
} }
...@@ -263,10 +262,16 @@ class HotRunner extends ResidentRunner { ...@@ -263,10 +262,16 @@ class HotRunner extends ResidentRunner {
} }
final Status devFSStatus = logger.startProgress('Syncing files to device...', final Status devFSStatus = logger.startProgress('Syncing files to device...',
expectSlowOperation: true); expectSlowOperation: true);
final int bytes = await _devFS.update(progressReporter: progressReporter, int bytes = 0;
try {
bytes = await _devFS.update(progressReporter: progressReporter,
bundle: assetBundle, bundle: assetBundle,
bundleDirty: rebuildBundle, bundleDirty: rebuildBundle,
fileFilter: _dartDependencies); fileFilter: _dartDependencies);
} on DevFSException {
devFSStatus.cancel();
return false;
}
devFSStatus.stop(); devFSStatus.stop();
if (!hotRunnerConfig.stableDartDependencies) { if (!hotRunnerConfig.stableDartDependencies) {
// Clear the set after the sync so they are recomputed next time. // Clear the set after the sync so they are recomputed next time.
...@@ -323,7 +328,7 @@ class HotRunner extends ResidentRunner { ...@@ -323,7 +328,7 @@ class HotRunner extends ResidentRunner {
restartTimer.start(); restartTimer.start();
final bool updatedDevFS = await _updateDevFS(); final bool updatedDevFS = await _updateDevFS();
if (!updatedDevFS) if (!updatedDevFS)
return new OperationResult(1, 'Dart Source Error'); return new OperationResult(1, 'DevFS Synchronization Failed');
await _launchFromDevFS(package, mainPath); await _launchFromDevFS(package, mainPath);
restartTimer.stop(); restartTimer.stop();
printTrace('Restart performed in ' printTrace('Restart performed in '
...@@ -413,6 +418,8 @@ class HotRunner extends ResidentRunner { ...@@ -413,6 +418,8 @@ class HotRunner extends ResidentRunner {
reassembleTimer = new Stopwatch(); reassembleTimer = new Stopwatch();
} }
final bool updatedDevFS = await _updateDevFS(); final bool updatedDevFS = await _updateDevFS();
if (!updatedDevFS)
return new OperationResult(1, 'DevFS Synchronization Failed');
if (benchmarkMode) { if (benchmarkMode) {
devFSTimer.stop(); devFSTimer.stop();
// Record time it took to synchronize to DevFS. // Record time it took to synchronize to DevFS.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment