Unverified Commit 0b68068d authored by Alexander Aprelev's avatar Alexander Aprelev Committed by GitHub

Revert "Run reload asynchronously so that multiple devices can reload in...

Revert "Run reload asynchronously so that multiple devices can reload in parallel. (#22693)" (#23598)

This reverts commit 709f54f4 as it seems to have broken two tests: flutter_gallery__back_button_memory, named_isolates_test.
parent c54cf048
...@@ -79,15 +79,11 @@ class FlutterDevice { ...@@ -79,15 +79,11 @@ class FlutterDevice {
vmServices = localVmServices; vmServices = localVmServices;
} }
Future<void> refreshViews() { Future<void> refreshViews() async {
if (vmServices == null || vmServices.isEmpty) if (vmServices == null || vmServices.isEmpty)
return Future<void>.value(null); return;
final List<Future<void>> futures = <Future<void>>[];
for (VMService service in vmServices) for (VMService service in vmServices)
futures.add(service.vm.refreshViews()); await service.vm.refreshViews();
final Completer<void> completer = Completer<void>();
Future.wait(futures).whenComplete(() => completer.complete(null)); // ignore: unawaited_futures
return completer.future;
} }
List<FlutterView> get views { List<FlutterView> get views {
......
...@@ -41,13 +41,6 @@ HotRunnerConfig get hotRunnerConfig => context[HotRunnerConfig]; ...@@ -41,13 +41,6 @@ HotRunnerConfig get hotRunnerConfig => context[HotRunnerConfig];
const bool kHotReloadDefault = true; const bool kHotReloadDefault = true;
class DeviceReloadReport {
DeviceReloadReport(this.device, this.reports);
FlutterDevice device;
List<Map<String, dynamic>> reports; // List has one report per Flutter view.
}
// TODO(flutter/flutter#23031): Test this. // TODO(flutter/flutter#23031): Test this.
class HotRunner extends ResidentRunner { class HotRunner extends ResidentRunner {
HotRunner( HotRunner(
...@@ -327,9 +320,8 @@ class HotRunner extends ResidentRunner { ...@@ -327,9 +320,8 @@ class HotRunner extends ResidentRunner {
return false; return false;
} }
final List<bool> results = <bool>[];
for (FlutterDevice device in flutterDevices) { for (FlutterDevice device in flutterDevices) {
results.add(await device.updateDevFS( final bool result = await device.updateDevFS(
mainPath: mainPath, mainPath: mainPath,
target: target, target: target,
bundle: assetBundle, bundle: assetBundle,
...@@ -340,10 +332,8 @@ class HotRunner extends ResidentRunner { ...@@ -340,10 +332,8 @@ class HotRunner extends ResidentRunner {
fullRestart: fullRestart, fullRestart: fullRestart,
projectRootPath: projectRootPath, projectRootPath: projectRootPath,
pathToReload: getReloadPath(fullRestart: fullRestart), pathToReload: getReloadPath(fullRestart: fullRestart),
)); );
} if (!result)
// If there any failures reported, bail out.
if (results.any((bool result) => !result)) {
return false; return false;
} }
...@@ -354,20 +344,16 @@ class HotRunner extends ResidentRunner { ...@@ -354,20 +344,16 @@ class HotRunner extends ResidentRunner {
return true; return true;
} }
Future<void> _evictDirtyAssets() { Future<void> _evictDirtyAssets() async {
final List<Future<Map<String, dynamic>>> futures = <Future<Map<String, dynamic>>>[];
for (FlutterDevice device in flutterDevices) { for (FlutterDevice device in flutterDevices) {
if (device.devFS.assetPathsToEvict.isEmpty) if (device.devFS.assetPathsToEvict.isEmpty)
continue; return;
if (device.views.first.uiIsolate == null) { if (device.views.first.uiIsolate == null)
printError('Application isolate not found for $device'); throw 'Application isolate not found';
continue;
}
for (String assetPath in device.devFS.assetPathsToEvict) for (String assetPath in device.devFS.assetPathsToEvict)
futures.add(device.views.first.uiIsolate.flutterEvictAsset(assetPath)); await device.views.first.uiIsolate.flutterEvictAsset(assetPath);
device.devFS.assetPathsToEvict.clear(); device.devFS.assetPathsToEvict.clear();
} }
return Future.wait<Map<String, dynamic>>(futures);
} }
void _resetDirtyAssets() { void _resetDirtyAssets() {
...@@ -375,59 +361,46 @@ class HotRunner extends ResidentRunner { ...@@ -375,59 +361,46 @@ class HotRunner extends ResidentRunner {
device.devFS.assetPathsToEvict.clear(); device.devFS.assetPathsToEvict.clear();
} }
Future<void> _cleanupDevFS() { Future<void> _cleanupDevFS() async {
final List<Future<void>> futures = <Future<void>>[];
for (FlutterDevice device in flutterDevices) { for (FlutterDevice device in flutterDevices) {
if (device.devFS != null) { if (device.devFS != null) {
// Cleanup the devFS; don't wait indefinitely, and ignore any errors. // Cleanup the devFS; don't wait indefinitely, and ignore any errors.
futures.add(device.devFS.destroy() await device.devFS.destroy()
.timeout(const Duration(milliseconds: 250)) .timeout(const Duration(milliseconds: 250))
.catchError((dynamic error) { .catchError((dynamic error) {
printTrace('$error'); printTrace('$error');
})); });
} }
device.devFS = null; device.devFS = null;
} }
final Completer<void> completer = Completer<void>();
Future.wait(futures).whenComplete(() { completer.complete(null); } ); // ignore: unawaited_futures
return completer.future;
} }
Future<void> _launchInView(FlutterDevice device, Future<void> _launchInView(FlutterDevice device,
Uri entryUri, Uri entryUri,
Uri packagesUri, Uri packagesUri,
Uri assetsDirectoryUri) { Uri assetsDirectoryUri) async {
final List<Future<void>> futures = <Future<void>>[];
for (FlutterView view in device.views) for (FlutterView view in device.views)
futures.add(view.runFromSource(entryUri, packagesUri, assetsDirectoryUri)); await view.runFromSource(entryUri, packagesUri, assetsDirectoryUri);
final Completer<void> completer = Completer<void>();
Future.wait(futures).whenComplete(() { completer.complete(null); }); // ignore: unawaited_futures
return completer.future;
} }
Future<void> _launchFromDevFS(String mainScript) async { Future<void> _launchFromDevFS(String mainScript) async {
final String entryUri = fs.path.relative(mainScript, from: projectRootPath); final String entryUri = fs.path.relative(mainScript, from: projectRootPath);
final List<Future<void>> futures = <Future<void>>[];
for (FlutterDevice device in flutterDevices) { for (FlutterDevice device in flutterDevices) {
final Uri deviceEntryUri = device.devFS.baseUri.resolveUri( final Uri deviceEntryUri = device.devFS.baseUri.resolveUri(
fs.path.toUri(entryUri)); fs.path.toUri(entryUri));
final Uri devicePackagesUri = device.devFS.baseUri.resolve('.packages'); final Uri devicePackagesUri = device.devFS.baseUri.resolve('.packages');
final Uri deviceAssetsDirectoryUri = device.devFS.baseUri.resolveUri( final Uri deviceAssetsDirectoryUri = device.devFS.baseUri.resolveUri(
fs.path.toUri(getAssetBuildDirectory())); fs.path.toUri(getAssetBuildDirectory()));
futures.add(_launchInView(device, await _launchInView(device,
deviceEntryUri, deviceEntryUri,
devicePackagesUri, devicePackagesUri,
deviceAssetsDirectoryUri)); deviceAssetsDirectoryUri);
}
await Future.wait(futures);
if (benchmarkMode) { if (benchmarkMode) {
futures.clear();
for (FlutterDevice device in flutterDevices) for (FlutterDevice device in flutterDevices)
for (FlutterView view in device.views) for (FlutterView view in device.views)
futures.add(view.flushUIThreadTasks()); await view.flushUIThreadTasks();
await Future.wait(futures); }
} }
} }
Future<OperationResult> _restartFromSources({ String reason }) async { Future<OperationResult> _restartFromSources({ String reason }) async {
...@@ -460,24 +433,19 @@ class HotRunner extends ResidentRunner { ...@@ -460,24 +433,19 @@ class HotRunner extends ResidentRunner {
device.generator.accept(); device.generator.accept();
} }
// Check if the isolate is paused and resume it. // Check if the isolate is paused and resume it.
final List<Future<void>> futures = <Future<void>>[];
for (FlutterDevice device in flutterDevices) { for (FlutterDevice device in flutterDevices) {
for (FlutterView view in device.views) { for (FlutterView view in device.views) {
if (view.uiIsolate != null) { if (view.uiIsolate != null) {
// Reload the isolate. // Reload the isolate.
final Completer<void> completer = Completer<void>(); await view.uiIsolate.reload();
futures.add(completer.future);
view.uiIsolate.reload().then((ServiceObject _) { // ignore: unawaited_futures
final ServiceEvent pauseEvent = view.uiIsolate.pauseEvent; final ServiceEvent pauseEvent = view.uiIsolate.pauseEvent;
if ((pauseEvent != null) && pauseEvent.isPauseEvent) { if ((pauseEvent != null) && pauseEvent.isPauseEvent) {
// Resume the isolate so that it can be killed by the embedder. // Resume the isolate so that it can be killed by the embedder.
return view.uiIsolate.resume(); await view.uiIsolate.resume();
} }
}).whenComplete(() { completer.complete(null); });
} }
} }
} }
await Future.wait(futures);
// We are now running from source. // We are now running from source.
_runningFromSnapshot = false; _runningFromSnapshot = false;
await _launchFromDevFS(mainPath + '.dill'); await _launchFromDevFS(mainPath + '.dill');
...@@ -612,7 +580,9 @@ class HotRunner extends ResidentRunner { ...@@ -612,7 +580,9 @@ class HotRunner extends ResidentRunner {
getReloadPath(fullRestart: false), getReloadPath(fullRestart: false),
from: projectRootPath, from: projectRootPath,
); );
final List<Future<DeviceReloadReport>> allReportsFutures = <Future<DeviceReloadReport>>[]; final Completer<Map<String, dynamic>> retrieveFirstReloadReport = Completer<Map<String, dynamic>>();
int countExpectedReports = 0;
for (FlutterDevice device in flutterDevices) { for (FlutterDevice device in flutterDevices) {
if (_runningFromSnapshot) { if (_runningFromSnapshot) {
// Asset directory has to be set only once when we switch from // Asset directory has to be set only once when we switch from
...@@ -620,25 +590,41 @@ class HotRunner extends ResidentRunner { ...@@ -620,25 +590,41 @@ class HotRunner extends ResidentRunner {
await device.resetAssetDirectory(); await device.resetAssetDirectory();
} }
final Completer<DeviceReloadReport> completer = Completer<DeviceReloadReport>(); // List has one report per Flutter view.
allReportsFutures.add(completer.future); final List<Future<Map<String, dynamic>>> reports = device.reloadSources(
final List<Future<Map<String, dynamic>>> reportFutures = device.reloadSources( entryPath,
entryPath, pause: pause pause: pause
); );
Future.wait(reportFutures).then((List<Map<String, dynamic>> reports) { // ignore: unawaited_futures countExpectedReports += reports.length;
await Future
.wait<Map<String, dynamic>>(reports)
.catchError((dynamic error) {
return <Map<String, dynamic>>[error];
})
.then<void>(
(List<Map<String, dynamic>> list) {
// TODO(aam): Investigate why we are validating only first reload report, // TODO(aam): Investigate why we are validating only first reload report,
// which seems to be current behavior // which seems to be current behavior
final Map<String, dynamic> firstReport = reports.first; final Map<String, dynamic> firstReport = list.first;
// Don't print errors because they will be printed further down when // Don't print errors because they will be printed further down when
// `validateReloadReport` is called again. // `validateReloadReport` is called again.
device.updateReloadStatus(validateReloadReport(firstReport, printErrors: false)); device.updateReloadStatus(
completer.complete(DeviceReloadReport(device, reports)); validateReloadReport(firstReport, printErrors: false)
}); );
if (!retrieveFirstReloadReport.isCompleted)
retrieveFirstReloadReport.complete(firstReport);
},
onError: (dynamic error, StackTrace stack) {
retrieveFirstReloadReport.completeError(error, stack);
},
);
} }
final List<DeviceReloadReport> reports = await Future.wait(allReportsFutures); if (countExpectedReports == 0) {
for (DeviceReloadReport report in reports) { printError('Unable to hot reload. No instance of Flutter is currently running.');
final Map<String, dynamic> reloadReport = report.reports[0]; return OperationResult(1, 'No instances running');
}
final Map<String, dynamic> reloadReport = await retrieveFirstReloadReport.future;
if (!validateReloadReport(reloadReport)) { if (!validateReloadReport(reloadReport)) {
// Reload failed. // Reload failed.
flutterUsage.sendEvent('hot', 'reload-reject'); flutterUsage.sendEvent('hot', 'reload-reject');
...@@ -650,7 +636,6 @@ class HotRunner extends ResidentRunner { ...@@ -650,7 +636,6 @@ class HotRunner extends ResidentRunner {
printTrace('reloaded $loadedLibraryCount of $finalLibraryCount libraries'); printTrace('reloaded $loadedLibraryCount of $finalLibraryCount libraries');
reloadMessage = 'Reloaded $loadedLibraryCount of $finalLibraryCount libraries'; reloadMessage = 'Reloaded $loadedLibraryCount of $finalLibraryCount libraries';
} }
}
} on Map<String, dynamic> catch (error, st) { } on Map<String, dynamic> catch (error, st) {
printError('Hot reload failed: $error\n$st'); printError('Hot reload failed: $error\n$st');
final int errorCode = error['code']; final int errorCode = error['code'];
...@@ -676,22 +661,14 @@ class HotRunner extends ResidentRunner { ...@@ -676,22 +661,14 @@ class HotRunner extends ResidentRunner {
vmReloadTimer.elapsed.inMilliseconds); vmReloadTimer.elapsed.inMilliseconds);
final Stopwatch reassembleTimer = Stopwatch()..start(); final Stopwatch reassembleTimer = Stopwatch()..start();
// Reload the isolate. // Reload the isolate.
final List<Future<void>> allDevices = <Future<void>>[];
for (FlutterDevice device in flutterDevices) { for (FlutterDevice device in flutterDevices) {
printTrace('Sending reload events to ${device.device.name}'); printTrace('Sending reload events to ${device.device.name}');
final List<Future<ServiceObject>> futuresViews = <Future<ServiceObject>>[];
for (FlutterView view in device.views) { for (FlutterView view in device.views) {
printTrace('Sending reload event to "${view.uiIsolate.name}"'); printTrace('Sending reload event to "${view.uiIsolate.name}"');
futuresViews.add(view.uiIsolate.reload()); await view.uiIsolate.reload();
} }
final Completer<void> deviceCompleter = Completer<void>(); await device.refreshViews();
Future.wait(futuresViews).whenComplete(() { // ignore: unawaited_futures
deviceCompleter.complete(device.refreshViews());
});
allDevices.add(deviceCompleter.future);
} }
await Future.wait(allDevices);
// We are now running from source. // We are now running from source.
_runningFromSnapshot = false; _runningFromSnapshot = false;
// Check if the isolate is paused. // Check if the isolate is paused.
...@@ -717,23 +694,27 @@ class HotRunner extends ResidentRunner { ...@@ -717,23 +694,27 @@ class HotRunner extends ResidentRunner {
printTrace('Reassembling application'); printTrace('Reassembling application');
bool reassembleAndScheduleErrors = false; bool reassembleAndScheduleErrors = false;
bool reassembleTimedOut = false; bool reassembleTimedOut = false;
final List<Future<void>> futures = <Future<void>>[];
for (FlutterView view in reassembleViews) { for (FlutterView view in reassembleViews) {
futures.add(view.uiIsolate.flutterReassemble().then((_) { try {
return view.uiIsolate.uiWindowScheduleFrame(); await view.uiIsolate.flutterReassemble();
}).catchError((dynamic error) { } on TimeoutException {
if (error is TimeoutException) {
reassembleTimedOut = true; reassembleTimedOut = true;
printTrace('Reassembling ${view.uiIsolate.name} took too long.'); printTrace('Reassembling ${view.uiIsolate.name} took too long.');
printStatus('Hot reloading ${view.uiIsolate.name} took too long; the reload may have failed.'); printStatus('Hot reloading ${view.uiIsolate.name} took too long; the reload may have failed.');
} else { continue;
} catch (error) {
reassembleAndScheduleErrors = true; reassembleAndScheduleErrors = true;
printError('Reassembling ${view.uiIsolate.name} failed: $error'); printError('Reassembling ${view.uiIsolate.name} failed: $error');
continue;
}
try {
/* ensure that a frame is scheduled */
await view.uiIsolate.uiWindowScheduleFrame();
} catch (error) {
reassembleAndScheduleErrors = true;
printError('Scheduling a frame for ${view.uiIsolate.name} failed: $error');
} }
}));
} }
await Future.wait(futures);
// Record time it took for Flutter to reassemble the application. // Record time it took for Flutter to reassemble the application.
_addBenchmarkData('hotReloadFlutterReassembleMilliseconds', _addBenchmarkData('hotReloadFlutterReassembleMilliseconds',
reassembleTimer.elapsed.inMilliseconds); reassembleTimer.elapsed.inMilliseconds);
......
...@@ -949,19 +949,15 @@ class VM extends ServiceObjectOwner { ...@@ -949,19 +949,15 @@ class VM extends ServiceObjectOwner {
return invokeRpcRaw('_getVMTimeline', timeout: kLongRequestTimeout); return invokeRpcRaw('_getVMTimeline', timeout: kLongRequestTimeout);
} }
Future<void> refreshViews() { Future<void> refreshViews() async {
if (!isFlutterEngine) if (!isFlutterEngine)
return Future<void>.value(null); return;
_viewCache.clear(); _viewCache.clear();
final Completer<void> completer = Completer<void>();
final List<Future<ServiceObject>> futures = <Future<ServiceObject>>[];
for (Isolate isolate in isolates.toList()) { for (Isolate isolate in isolates.toList()) {
futures.add(vmService.vm.invokeRpc<ServiceObject>('_flutter.listViews', await vmService.vm.invokeRpc<ServiceObject>('_flutter.listViews',
timeout: kLongRequestTimeout, timeout: kLongRequestTimeout,
params: <String, dynamic> {'isolateId': isolate.id})); params: <String, dynamic> {'isolateId': isolate.id});
} }
Future.wait(futures).whenComplete(() => completer.complete(null)); // ignore: unawaited_futures
return completer.future;
} }
Iterable<FlutterView> get views => _viewCache.values; Iterable<FlutterView> get views => _viewCache.values;
...@@ -1230,15 +1226,15 @@ class Isolate extends ServiceObjectOwner { ...@@ -1230,15 +1226,15 @@ class Isolate extends ServiceObjectOwner {
Duration timeout, Duration timeout,
bool timeoutFatal = true, bool timeoutFatal = true,
} }
) { ) async {
return invokeRpcRaw(method, params: params, timeout: timeout, try {
timeoutFatal: timeoutFatal).catchError((dynamic error) { return await invokeRpcRaw(method, params: params, timeout: timeout, timeoutFatal: timeoutFatal);
if (error is rpc.RpcException) { } on rpc.RpcException catch (e) {
// If an application is not using the framework // If an application is not using the framework
if (error.code == rpc_error_code.METHOD_NOT_FOUND) if (e.code == rpc_error_code.METHOD_NOT_FOUND)
return null; return null;
throw error; rethrow;
}}); }
} }
// Debug dump extension methods. // Debug dump extension methods.
...@@ -1292,20 +1288,20 @@ class Isolate extends ServiceObjectOwner { ...@@ -1292,20 +1288,20 @@ class Isolate extends ServiceObjectOwner {
} }
// Reload related extension methods. // Reload related extension methods.
Future<Map<String, dynamic>> flutterReassemble() { Future<Map<String, dynamic>> flutterReassemble() async {
return invokeFlutterExtensionRpcRaw( return await invokeFlutterExtensionRpcRaw(
'ext.flutter.reassemble', 'ext.flutter.reassemble',
timeout: kShortRequestTimeout, timeout: kShortRequestTimeout,
timeoutFatal: true, timeoutFatal: true,
); );
} }
Future<Map<String, dynamic>> uiWindowScheduleFrame() { Future<Map<String, dynamic>> uiWindowScheduleFrame() async {
return invokeFlutterExtensionRpcRaw('ext.ui.window.scheduleFrame'); return await invokeFlutterExtensionRpcRaw('ext.ui.window.scheduleFrame');
} }
Future<Map<String, dynamic>> flutterEvictAsset(String assetPath) { Future<Map<String, dynamic>> flutterEvictAsset(String assetPath) async {
return invokeFlutterExtensionRpcRaw('ext.flutter.evict', return await invokeFlutterExtensionRpcRaw('ext.flutter.evict',
params: <String, dynamic>{ params: <String, dynamic>{
'value': assetPath, 'value': assetPath,
} }
...@@ -1313,8 +1309,8 @@ class Isolate extends ServiceObjectOwner { ...@@ -1313,8 +1309,8 @@ class Isolate extends ServiceObjectOwner {
} }
// Application control extension methods. // Application control extension methods.
Future<Map<String, dynamic>> flutterExit() { Future<Map<String, dynamic>> flutterExit() async {
return invokeFlutterExtensionRpcRaw( return await invokeFlutterExtensionRpcRaw(
'ext.flutter.exit', 'ext.flutter.exit',
timeout: const Duration(seconds: 2), timeout: const Duration(seconds: 2),
timeoutFatal: false, timeoutFatal: false,
......
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