Unverified Commit 08068fd9 authored by Ben Konyi's avatar Ben Konyi Committed by GitHub

Handle service disappeared RPCError when VM service connection disappears (#74424)

* Handle service disappeared RPCError when VM service connection
disappears while invoking a service extension registered by the
framework

* Add unit test, handle non-trivial cases
parent 1f07fde4
...@@ -417,6 +417,25 @@ extension FlutterVmService on vm_service.VmService { ...@@ -417,6 +417,25 @@ extension FlutterVmService on vm_service.VmService {
Uri get httpAddress => this != null ? _httpAddressExpando[this] : null; Uri get httpAddress => this != null ? _httpAddressExpando[this] : null;
Future<vm_service.Response> callMethodWrapper(
String method, {
String isolateId,
Map<String, dynamic> args
}) async {
try {
return await callMethod(method, isolateId: isolateId, args: args);
} on vm_service.RPCError catch (e) {
// If the service disappears mid-request the tool is unable to recover
// and should begin to shutdown due to the service connection closing.
// Swallow the exception here and let the shutdown logic elsewhere deal
// with cleaning up.
if (e.code == RPCErrorCodes.kServiceDisappeared) {
return null;
}
rethrow;
}
}
/// Set the asset directory for the an attached Flutter view. /// Set the asset directory for the an attached Flutter view.
Future<void> setAssetDirectory({ Future<void> setAssetDirectory({
@required Uri assetsDirectory, @required Uri assetsDirectory,
...@@ -424,7 +443,7 @@ extension FlutterVmService on vm_service.VmService { ...@@ -424,7 +443,7 @@ extension FlutterVmService on vm_service.VmService {
@required String uiIsolateId, @required String uiIsolateId,
}) async { }) async {
assert(assetsDirectory != null); assert(assetsDirectory != null);
await callMethod(kSetAssetBundlePathMethod, await callMethodWrapper(kSetAssetBundlePathMethod,
isolateId: uiIsolateId, isolateId: uiIsolateId,
args: <String, dynamic>{ args: <String, dynamic>{
'viewId': viewId, 'viewId': viewId,
...@@ -439,12 +458,15 @@ extension FlutterVmService on vm_service.VmService { ...@@ -439,12 +458,15 @@ extension FlutterVmService on vm_service.VmService {
Future<Map<String, Object>> getSkSLs({ Future<Map<String, Object>> getSkSLs({
@required String viewId, @required String viewId,
}) async { }) async {
final vm_service.Response response = await callMethod( final vm_service.Response response = await callMethodWrapper(
kGetSkSLsMethod, kGetSkSLsMethod,
args: <String, String>{ args: <String, String>{
'viewId': viewId, 'viewId': viewId,
}, },
); );
if (response == null) {
return null;
}
return response.json['SkSLs'] as Map<String, Object>; return response.json['SkSLs'] as Map<String, Object>;
} }
...@@ -454,7 +476,7 @@ extension FlutterVmService on vm_service.VmService { ...@@ -454,7 +476,7 @@ extension FlutterVmService on vm_service.VmService {
Future<void> flushUIThreadTasks({ Future<void> flushUIThreadTasks({
@required String uiIsolateId, @required String uiIsolateId,
}) async { }) async {
await callMethod( await callMethodWrapper(
kFlushUIThreadTasksMethod, kFlushUIThreadTasksMethod,
args: <String, String>{ args: <String, String>{
'isolateId': uiIsolateId, 'isolateId': uiIsolateId,
...@@ -480,7 +502,7 @@ extension FlutterVmService on vm_service.VmService { ...@@ -480,7 +502,7 @@ extension FlutterVmService on vm_service.VmService {
final Future<void> onRunnable = onIsolateEvent.firstWhere((vm_service.Event event) { final Future<void> onRunnable = onIsolateEvent.firstWhere((vm_service.Event event) {
return event.kind == vm_service.EventKind.kIsolateRunnable; return event.kind == vm_service.EventKind.kIsolateRunnable;
}); });
await callMethod( await callMethodWrapper(
kRunInViewMethod, kRunInViewMethod,
args: <String, Object>{ args: <String, Object>{
'viewId': viewId, 'viewId': viewId,
...@@ -745,9 +767,12 @@ extension FlutterVmService on vm_service.VmService { ...@@ -745,9 +767,12 @@ extension FlutterVmService on vm_service.VmService {
Duration delay = const Duration(milliseconds: 50), Duration delay = const Duration(milliseconds: 50),
}) async { }) async {
while (true) { while (true) {
final vm_service.Response response = await callMethod( final vm_service.Response response = await callMethodWrapper(
kListViewsMethod, kListViewsMethod,
); );
if (response == null) {
return null;
}
final List<Object> rawViews = response.json['views'] as List<Object>; final List<Object> rawViews = response.json['views'] as List<Object>;
final List<FlutterView> views = <FlutterView>[ final List<FlutterView> views = <FlutterView>[
for (final Object rawView in rawViews) for (final Object rawView in rawViews)
......
...@@ -312,6 +312,27 @@ void main() { ...@@ -312,6 +312,27 @@ void main() {
expect(fakeVmServiceHost.hasRemainingExpectations, false); expect(fakeVmServiceHost.hasRemainingExpectations, false);
}); });
testWithoutContext('Framework service extension invocations return null if service disappears ', () async {
final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost(
requests: <VmServiceExpectation>[
const FakeVmServiceRequest(method: kGetSkSLsMethod, args: <String, Object>{
'viewId': '1234',
}, errorCode: RPCErrorCodes.kServiceDisappeared),
const FakeVmServiceRequest(method: kListViewsMethod, errorCode: RPCErrorCodes.kServiceDisappeared),
]
);
final Map<String, Object> skSLs = await fakeVmServiceHost.vmService.getSkSLs(
viewId: '1234',
);
expect(skSLs, null);
final List<FlutterView> views = await fakeVmServiceHost.vmService.getFlutterViews();
expect(views, null);
expect(fakeVmServiceHost.hasRemainingExpectations, false);
});
testWithoutContext('getFlutterViews polls until a view is returned', () async { testWithoutContext('getFlutterViews polls until a view is returned', () async {
final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost( final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost(
requests: <VmServiceExpectation>[ requests: <VmServiceExpectation>[
......
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