Skip to content

Commit ffcf1db

Browse files
[flutter_tools] reland migrate FlutterViews to package:vm_service (flutter#55797)
Move FlutterView and related RPCs to the package:vm_service implementation. Update some getIsolate calls with catchError to match previous behavior. - Updates tests that were previously mocking FlutterViews to use real views - Moves the FlutterView cache from VM to FlutterDevice - Catch SentinelException during Isolate.kill
1 parent 5cc87a5 commit ffcf1db

File tree

13 files changed

+565
-368
lines changed

13 files changed

+565
-368
lines changed

packages/flutter_tools/lib/src/fuchsia/fuchsia_device.dart

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -620,13 +620,12 @@ class FuchsiaDevice extends Device {
620620
// loopback (::1).
621621
final Uri uri = Uri.parse('http://[$_ipv6Loopback]:$port');
622622
final VMService vmService = await VMService.connect(uri);
623-
await vmService.getVMOld();
624-
await vmService.refreshViews();
625-
for (final FlutterView flutterView in vmService.vm.views) {
623+
final List<FlutterView> flutterViews = await vmService.getFlutterViews();
624+
for (final FlutterView flutterView in flutterViews) {
626625
if (flutterView.uiIsolate == null) {
627626
continue;
628627
}
629-
final Uri address = flutterView.owner.vmService.httpAddress;
628+
final Uri address = vmService.httpAddress;
630629
if (flutterView.uiIsolate.name.contains(isolateName)) {
631630
return address.port;
632631
}
@@ -717,13 +716,12 @@ class FuchsiaIsolateDiscoveryProtocol {
717716
continue;
718717
}
719718
}
720-
await service.getVMOld();
721-
await service.refreshViews();
722-
for (final FlutterView flutterView in service.vm.views) {
719+
final List<FlutterView> flutterViews = await service.getFlutterViews();
720+
for (final FlutterView flutterView in flutterViews) {
723721
if (flutterView.uiIsolate == null) {
724722
continue;
725723
}
726-
final Uri address = flutterView.owner.vmService.httpAddress;
724+
final Uri address = service.httpAddress;
727725
if (flutterView.uiIsolate.name.contains(_isolateName)) {
728726
_foundUri.complete(_device.ipv6
729727
? Uri.parse('http://[$_ipv6Loopback]:${address.port}/')

packages/flutter_tools/lib/src/resident_runner.dart

Lines changed: 29 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -233,18 +233,25 @@ class FlutterDevice {
233233
if (vmService == null) {
234234
return;
235235
}
236-
await flutterDeprecatedVmService.vm.refreshViews(waitForViews: true);
236+
final List<FlutterView> updatedViews = await vmService.getFlutterViews();
237+
_views
238+
..clear()
239+
..addAll(updatedViews);
237240
}
241+
final List<FlutterView> _views = <FlutterView>[];
238242

239243
List<FlutterView> get views {
240-
if (vmService == null || flutterDeprecatedVmService.isClosed) {
244+
if (vmService == null) {
241245
return <FlutterView>[];
242246
}
243-
244-
245-
return (viewFilter != null
246-
? flutterDeprecatedVmService.vm.allViewsWithName(viewFilter)
247-
: flutterDeprecatedVmService.vm.views).toList();
247+
if (viewFilter != null) {
248+
return <FlutterView>[
249+
for (final FlutterView flutterView in _views)
250+
if (flutterView.uiIsolate.name.contains(viewFilter))
251+
flutterView
252+
];
253+
}
254+
return _views;
248255
}
249256

250257
Future<void> getVMs() => flutterDeprecatedVmService.getVMOld();
@@ -254,35 +261,32 @@ class FlutterDevice {
254261
await device.stopApp(package);
255262
return;
256263
}
257-
final List<FlutterView> flutterViews = views;
258-
if (flutterViews == null || flutterViews.isEmpty) {
264+
await refreshViews();
265+
if (views == null || views.isEmpty) {
259266
return;
260267
}
261268
// If any of the flutter views are paused, we might not be able to
262269
// cleanly exit since the service extension may not have been registered.
263-
if (flutterViews.any((FlutterView view) {
264-
return view != null &&
265-
view.uiIsolate != null &&
266-
view.uiIsolate.pauseEvent != null &&
267-
view.uiIsolate.pauseEvent.isPauseEvent;
270+
for (final FlutterView flutterView in views) {
271+
final vm_service.Isolate isolate = await vmService
272+
.getIsolateOrNull(flutterView.uiIsolate.id);
273+
if (isolate == null) {
274+
continue;
275+
}
276+
if (isPauseEvent(isolate.pauseEvent.kind)) {
277+
await device.stopApp(package);
278+
return;
268279
}
269-
)) {
270-
await device.stopApp(package);
271-
return;
272280
}
273-
final List<Future<void>> futures = <Future<void>>[];
274-
for (final FlutterView view in flutterViews) {
281+
for (final FlutterView view in views) {
275282
if (view != null && view.uiIsolate != null) {
276-
assert(!view.uiIsolate.pauseEvent.isPauseEvent);
277-
futures.add(vmService.flutterExit(
283+
// If successful, there will be no response from flutterExit.
284+
unawaited(vmService.flutterExit(
278285
isolateId: view.uiIsolate.id,
279286
));
280287
}
281288
}
282-
// The flutterExit message only returns if it fails, so just wait a few
283-
// seconds then assume it worked.
284-
// TODO(ianh): We should make this return once the VM service disconnects.
285-
await Future.wait(futures).timeout(const Duration(seconds: 2), onTimeout: () => <void>[]);
289+
return vmService.onDone;
286290
}
287291

288292
Future<Uri> setupDevFS(

packages/flutter_tools/lib/src/run_hot.dart

Lines changed: 73 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -514,10 +514,8 @@ class HotRunner extends ResidentRunner {
514514
String reason,
515515
bool benchmarkMode = false,
516516
}) async {
517-
if (!_isPaused()) {
518-
globals.printTrace('Refreshing active FlutterViews before restarting.');
519-
await refreshViews();
520-
}
517+
globals.printTrace('Refreshing active FlutterViews before restarting.');
518+
await refreshViews();
521519

522520
final Stopwatch restartTimer = Stopwatch()..start();
523521
// TODO(aam): Add generator reset logic once we switch to using incremental
@@ -542,31 +540,36 @@ class HotRunner extends ResidentRunner {
542540
// Check if the isolate is paused and resume it.
543541
final List<Future<void>> operations = <Future<void>>[];
544542
for (final FlutterDevice device in flutterDevices) {
545-
final Set<Isolate> uiIsolates = <Isolate>{};
543+
final Set<String> uiIsolatesIds = <String>{};
546544
for (final FlutterView view in device.views) {
547545
if (view.uiIsolate == null) {
548546
continue;
549547
}
550-
uiIsolates.add(view.uiIsolate);
548+
uiIsolatesIds.add(view.uiIsolate.id);
551549
// Reload the isolate.
552-
operations.add(view.uiIsolate.reload().then((ServiceObject _) {
553-
final ServiceEvent pauseEvent = view.uiIsolate.pauseEvent;
554-
if ((pauseEvent != null) && pauseEvent.isPauseEvent) {
550+
final Future<vm_service.Isolate> reloadIsolate = device.vmService
551+
.getIsolateOrNull(view.uiIsolate.id);
552+
operations.add(reloadIsolate.then((vm_service.Isolate isolate) async {
553+
if ((isolate != null) && isPauseEvent(isolate.pauseEvent.kind)) {
555554
// Resume the isolate so that it can be killed by the embedder.
556-
return device.vmService.resume(view.uiIsolate.id);
555+
await device.vmService.resume(view.uiIsolate.id);
557556
}
558-
return null;
559557
}));
560558
}
559+
561560
// The engine handles killing and recreating isolates that it has spawned
562561
// ("uiIsolates"). The isolates that were spawned from these uiIsolates
563562
// will not be restared, and so they must be manually killed.
564-
for (final Isolate isolate in device?.flutterDeprecatedVmService?.vm?.isolates ?? <Isolate>[]) {
565-
if (!uiIsolates.contains(isolate)) {
566-
operations.add(isolate.invokeRpcRaw('kill', params: <String, dynamic>{
567-
'isolateId': isolate.id,
568-
}));
563+
final vm_service.VM vm = await device.vmService.getVM();
564+
for (final vm_service.IsolateRef isolateRef in vm.isolates) {
565+
if (uiIsolatesIds.contains(isolateRef.id)) {
566+
continue;
569567
}
568+
operations.add(device.vmService.kill(isolateRef.id)
569+
.catchError((dynamic error, StackTrace stackTrace) {
570+
// Do nothing on a SentinelException since it means the isolate
571+
// has already been killed.
572+
}, test: (dynamic error) => error is vm_service.SentinelException));
570573
}
571574
}
572575
await Future.wait(operations);
@@ -589,13 +592,11 @@ class HotRunner extends ResidentRunner {
589592
} on vm_service.RPCError {
590593
// Do nothing, we're already subcribed.
591594
}
592-
for (final FlutterView view in device.views) {
593-
isolateNotifications.add(
594-
view.owner.vm.vmService.onIsolateEvent.firstWhere((vm_service.Event event) {
595-
return event.kind == vm_service.EventKind.kIsolateRunnable;
596-
}),
597-
);
598-
}
595+
isolateNotifications.add(
596+
device.vmService.onIsolateEvent.firstWhere((vm_service.Event event) {
597+
return event.kind == vm_service.EventKind.kIsolateRunnable;
598+
}),
599+
);
599600
}
600601
await Future.wait(isolateNotifications);
601602
}
@@ -818,11 +819,9 @@ class HotRunner extends ResidentRunner {
818819

819820
final Stopwatch reloadTimer = Stopwatch()..start();
820821

821-
if (!_isPaused()) {
822-
globals.printTrace('Refreshing active FlutterViews before reloading.');
823-
await refreshVM();
824-
await refreshViews();
825-
}
822+
globals.printTrace('Refreshing active FlutterViews before reloading.');
823+
await refreshVM();
824+
await refreshViews();
826825

827826
final Stopwatch devFSTimer = Stopwatch()..start();
828827
final UpdateFSReport updatedDevFS = await _updateDevFS();
@@ -914,27 +913,19 @@ class HotRunner extends ResidentRunner {
914913
// Record time it took for the VM to reload the sources.
915914
_addBenchmarkData('hotReloadVMReloadMilliseconds', vmReloadTimer.elapsed.inMilliseconds);
916915
final Stopwatch reassembleTimer = Stopwatch()..start();
917-
// Reload the isolate.
918-
final List<Future<void>> allDevices = <Future<void>>[];
919-
for (final FlutterDevice device in flutterDevices) {
920-
globals.printTrace('Sending reload events to ${device.device.name}');
921-
final List<Future<ServiceObject>> futuresViews = <Future<ServiceObject>>[];
922-
for (final FlutterView view in device.views) {
923-
globals.printTrace('Sending reload event to "${view.uiIsolate.name}"');
924-
futuresViews.add(view.uiIsolate.reload());
925-
}
926-
allDevices.add(Future.wait(futuresViews).whenComplete(() {
927-
return device.refreshViews();
928-
}));
929-
}
930-
await Future.wait(allDevices);
916+
917+
// Reload the isolate data.
918+
await Future.wait(<Future<void>>[
919+
for (final FlutterDevice device in flutterDevices)
920+
device.refreshViews()
921+
]);
931922

932923
globals.printTrace('Evicting dirty assets');
933924
await _evictDirtyAssets();
934925

935926
// Check if any isolates are paused and reassemble those
936927
// that aren't.
937-
final List<FlutterView> reassembleViews = <FlutterView>[];
928+
final Map<FlutterView, vm_service.VmService> reassembleViews = <FlutterView, vm_service.VmService>{};
938929
final List<Future<void>> reassembleFutures = <Future<void>>[];
939930
String serviceEventKind;
940931
int pausedIsolatesFound = 0;
@@ -943,16 +934,20 @@ class HotRunner extends ResidentRunner {
943934
for (final FlutterView view in device.views) {
944935
// Check if the isolate is paused, and if so, don't reassemble. Ignore the
945936
// PostPauseEvent event - the client requesting the pause will resume the app.
946-
final ServiceEvent pauseEvent = view.uiIsolate.pauseEvent;
947-
if (pauseEvent != null && pauseEvent.isPauseEvent && pauseEvent.kind != ServiceEvent.kPausePostRequest) {
937+
final vm_service.Isolate isolate = await device.vmService
938+
.getIsolateOrNull(view.uiIsolate.id);
939+
final vm_service.Event pauseEvent = isolate?.pauseEvent;
940+
if (pauseEvent != null
941+
&& isPauseEvent(pauseEvent.kind)
942+
&& pauseEvent.kind != vm_service.EventKind.kPausePostRequest) {
948943
pausedIsolatesFound += 1;
949944
if (serviceEventKind == null) {
950945
serviceEventKind = pauseEvent.kind;
951946
} else if (serviceEventKind != pauseEvent.kind) {
952947
serviceEventKind = ''; // many kinds
953948
}
954949
} else {
955-
reassembleViews.add(view);
950+
reassembleViews[view] = device.vmService;
956951
reassembleFutures.add(device.vmService.flutterReassemble(
957952
isolateId: view.uiIsolate.id,
958953
).catchError((dynamic error) {
@@ -974,6 +969,7 @@ class HotRunner extends ResidentRunner {
974969
assert(reassembleViews.isNotEmpty);
975970

976971
globals.printTrace('Reassembling application');
972+
977973
final Future<void> reassembleFuture = Future.wait<void>(reassembleFutures);
978974
await reassembleFuture.timeout(
979975
const Duration(seconds: 2),
@@ -986,14 +982,17 @@ class HotRunner extends ResidentRunner {
986982
globals.printTrace('This is taking a long time; will now check for paused isolates.');
987983
int postReloadPausedIsolatesFound = 0;
988984
String serviceEventKind;
989-
for (final FlutterView view in reassembleViews) {
990-
await view.uiIsolate.reload();
991-
final ServiceEvent pauseEvent = view.uiIsolate.pauseEvent;
992-
if (pauseEvent != null && pauseEvent.isPauseEvent) {
985+
for (final FlutterView view in reassembleViews.keys) {
986+
final vm_service.Isolate isolate = await reassembleViews[view]
987+
.getIsolateOrNull(view.uiIsolate.id);
988+
if (isolate == null) {
989+
continue;
990+
}
991+
if (isolate.pauseEvent != null && isPauseEvent(isolate.pauseEvent.kind)) {
993992
postReloadPausedIsolatesFound += 1;
994993
if (serviceEventKind == null) {
995-
serviceEventKind = pauseEvent.kind;
996-
} else if (serviceEventKind != pauseEvent.kind) {
994+
serviceEventKind = isolate.pauseEvent.kind;
995+
} else if (serviceEventKind != isolate.pauseEvent.kind) {
997996
serviceEventKind = ''; // many kinds
998997
}
999998
}
@@ -1069,32 +1068,33 @@ class HotRunner extends ResidentRunner {
10691068
}
10701069
assert(serviceEventKind != null);
10711070
switch (serviceEventKind) {
1072-
case ServiceEvent.kPauseStart: message.write('paused (probably due to --start-paused)'); break;
1073-
case ServiceEvent.kPauseExit: message.write('paused because ${ plural ? 'they have' : 'it has' } terminated'); break;
1074-
case ServiceEvent.kPauseBreakpoint: message.write('paused in the debugger on a breakpoint'); break;
1075-
case ServiceEvent.kPauseInterrupted: message.write('paused due in the debugger'); break;
1076-
case ServiceEvent.kPauseException: message.write('paused in the debugger after an exception was thrown'); break;
1077-
case ServiceEvent.kPausePostRequest: message.write('paused'); break;
1078-
case '': message.write('paused for various reasons'); break;
1071+
case vm_service.EventKind.kPauseStart:
1072+
message.write('paused (probably due to --start-paused)');
1073+
break;
1074+
case vm_service.EventKind.kPauseExit:
1075+
message.write('paused because ${ plural ? 'they have' : 'it has' } terminated');
1076+
break;
1077+
case vm_service.EventKind.kPauseBreakpoint:
1078+
message.write('paused in the debugger on a breakpoint');
1079+
break;
1080+
case vm_service.EventKind.kPauseInterrupted:
1081+
message.write('paused due in the debugger');
1082+
break;
1083+
case vm_service.EventKind.kPauseException:
1084+
message.write('paused in the debugger after an exception was thrown');
1085+
break;
1086+
case vm_service.EventKind.kPausePostRequest:
1087+
message.write('paused');
1088+
break;
1089+
case '':
1090+
message.write('paused for various reasons');
1091+
break;
10791092
default:
10801093
message.write('paused');
10811094
}
10821095
return message.toString();
10831096
}
10841097

1085-
bool _isPaused() {
1086-
for (final FlutterDevice device in flutterDevices) {
1087-
for (final FlutterView view in device.views) {
1088-
if (view.uiIsolate != null) {
1089-
final ServiceEvent pauseEvent = view.uiIsolate.pauseEvent;
1090-
if (pauseEvent != null && pauseEvent.isPauseEvent) {
1091-
return true;
1092-
}
1093-
}
1094-
}
1095-
}
1096-
return false;
1097-
}
10981098

10991099
@override
11001100
void printHelp({ @required bool details }) {
@@ -1134,7 +1134,7 @@ class HotRunner extends ResidentRunner {
11341134
}
11351135
for (final String assetPath in device.devFS.assetPathsToEvict) {
11361136
futures.add(
1137-
device.views.first.uiIsolate.vmService
1137+
device.vmService
11381138
.flutterEvictAsset(
11391139
assetPath,
11401140
isolateId: device.views.first.uiIsolate.id,

packages/flutter_tools/lib/src/tracing.dart

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,9 @@ class Tracing {
5353
}
5454
});
5555
bool done = false;
56-
for (final FlutterView view in vmService.vm.views) {
57-
if (await view.uiIsolate.vmService
56+
final List<FlutterView> views = await vmService.getFlutterViews();
57+
for (final FlutterView view in views) {
58+
if (await vmService
5859
.flutterAlreadyPaintedFirstUsefulFrame(
5960
isolateId: view.uiIsolate.id,
6061
)) {

0 commit comments

Comments
 (0)