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