-
Notifications
You must be signed in to change notification settings - Fork 392
Do not mark service extensions as 'added' so eagerly #9271
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 3 commits
28368b5
a755398
55e3de0
c5a6612
d037261
5c5e0aa
c720b1a
ea40730
6cc4976
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -245,7 +245,7 @@ final class ServiceExtensionManager with DisposerMixin { | |
| } | ||
|
|
||
| Future<void> _addServiceExtension(String name) async { | ||
| if (!_serviceExtensions.add(name)) { | ||
| if (_serviceExtensions.contains(name)) { | ||
| // If the service extension was already added we do not need to add it | ||
| // again. This can happen depending on the timing between when extension | ||
| // added events were received and when we requested the list of all | ||
|
|
@@ -254,68 +254,79 @@ final class ServiceExtensionManager with DisposerMixin { | |
| } | ||
| _hasServiceExtension(name).value = true; | ||
|
|
||
| if (_enabledServiceExtensions.containsKey(name)) { | ||
| final enabledServiceExtension = _enabledServiceExtensions[name]; | ||
| if (enabledServiceExtension != null) { | ||
| // Restore any previously enabled states by calling their service | ||
| // extension. This will restore extension states on the device after a hot | ||
| // restart. [_enabledServiceExtensions] will be empty on page refresh or | ||
| // initial start. | ||
| try { | ||
| return await _callServiceExtension( | ||
| final called = await _callServiceExtension( | ||
| name, | ||
| _enabledServiceExtensions[name]!.value, | ||
| enabledServiceExtension.value, | ||
| ); | ||
| if (called) { | ||
| _serviceExtensions.add(name); | ||
| } | ||
| return; | ||
| } on SentinelException catch (_) { | ||
| // Service extension stopped existing while calling, so do nothing. | ||
| // This typically happens during hot restarts. | ||
| } | ||
| } else { | ||
| // Set any extensions that are already enabled on the device. This will | ||
| // enable extension states in DevTools on page refresh or initial start. | ||
| return await _restoreExtensionFromDevice(name); | ||
| final restored = await _restoreExtensionFromDevice(name); | ||
| if (restored) { | ||
| _serviceExtensions.add(name); | ||
|
srawlins marked this conversation as resolved.
|
||
| } | ||
| } | ||
| } | ||
|
|
||
| IsolateRef? get _mainIsolate => _isolateManager.mainIsolate.value; | ||
|
|
||
| Future<void> _restoreExtensionFromDevice(String name) async { | ||
| /// Restores the service extension named [name] from the device. | ||
| /// | ||
| /// Returns whether the service extension was actually restored. | ||
| Future<bool> _restoreExtensionFromDevice(String name) async { | ||
|
srawlins marked this conversation as resolved.
Outdated
|
||
| final isolateRef = _isolateManager.mainIsolate.value; | ||
| if (isolateRef == null) return; | ||
| if (isolateRef == null) return false; | ||
|
|
||
| if (!extensions.serviceExtensionsAllowlist.containsKey(name)) { | ||
| return; | ||
| return false; | ||
| } | ||
| final expectedValueType = | ||
| extensions.serviceExtensionsAllowlist[name]!.values.first.runtimeType; | ||
|
|
||
| Future<void> restore() async { | ||
| Future<bool> restore() async { | ||
| // The restore request is obsolete if the isolate has changed. | ||
| if (isolateRef != _mainIsolate) return; | ||
| if (isolateRef != _mainIsolate) return false; | ||
| try { | ||
| final response = await _service!.callServiceExtension( | ||
| name, | ||
| isolateId: isolateRef.id, | ||
| ); | ||
|
|
||
| if (isolateRef != _mainIsolate) return; | ||
| if (isolateRef != _mainIsolate) return false; | ||
|
|
||
| switch (expectedValueType) { | ||
| case const (bool): | ||
| final enabled = response.json!['enabled'] == 'true' ? true : false; | ||
| await _maybeRestoreExtension(name, enabled); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we have
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah you're right. OK I've updated the code to use the return value from here. |
||
| return; | ||
| return true; | ||
| case const (String): | ||
| final String? value = response.json!['value']; | ||
| await _maybeRestoreExtension(name, value); | ||
| return; | ||
| return true; | ||
| case const (int): | ||
| case const (double): | ||
| final value = num.parse( | ||
| response.json![name.substring(name.lastIndexOf('.') + 1)], | ||
| ); | ||
| await _maybeRestoreExtension(name, value); | ||
| return; | ||
| return true; | ||
| default: | ||
| return; | ||
| return false; | ||
|
srawlins marked this conversation as resolved.
Outdated
|
||
| } | ||
| } catch (e) { | ||
| // Do not report an error if the VMService has gone away or the | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should we still return false on catch? or at least when the error indicates the service is disposed? See
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I really don't understand the lifecycle of when service extensions are added, or restored. For example I don't know why the previous changes caused test timeouts. The comment for the catch has an incomplete thought: // Do not report an error if the VMService has gone away or the
// selectedIsolate has been closed probably due to a hot restart.
// There is no needIf the bool return value essentially means "don't retry this operation later," like true = "service extension was restored, or some other state that means you shouldn't retry," and false = "service extension was not restored, and it's maybe safe to try again," then it sounds like your idea is sound. We could do something like
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah we can do:
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, I've moved that extension getter into |
||
|
|
@@ -325,12 +336,13 @@ final class ServiceExtensionManager with DisposerMixin { | |
| // of allowed network related exceptions rather than ignoring all | ||
| // exceptions. | ||
| } | ||
| return false; | ||
| } | ||
|
|
||
| if (isolateRef != _mainIsolate) return; | ||
| if (isolateRef != _mainIsolate) return false; | ||
|
|
||
| final isolate = await _isolateManager.isolateState(isolateRef).isolate; | ||
| if (isolateRef != _mainIsolate) return; | ||
| if (isolateRef != _mainIsolate) return false; | ||
|
|
||
| // Do not try to restore Dart IO extensions for a paused isolate. | ||
| if (extensions.isDartIoExtension(name) && | ||
|
|
@@ -339,6 +351,8 @@ final class ServiceExtensionManager with DisposerMixin { | |
| } else { | ||
| await restore(); | ||
|
srawlins marked this conversation as resolved.
Outdated
srawlins marked this conversation as resolved.
Outdated
|
||
| } | ||
|
|
||
| return true; | ||
| } | ||
|
|
||
| Future<void> _maybeRestoreExtension(String name, Object? value) async { | ||
|
|
@@ -362,14 +376,15 @@ final class ServiceExtensionManager with DisposerMixin { | |
| } | ||
| } | ||
|
|
||
| Future<void> _callServiceExtension(String name, Object? value) async { | ||
| if (_service == null) { | ||
| return; | ||
| } | ||
| /// Calls the service extension named [name] with [value]. | ||
| /// | ||
| /// Returns whether the service extension was successfully called. | ||
| Future<bool> _callServiceExtension(String name, Object? value) async { | ||
| if (_service == null) return false; | ||
|
|
||
| final mainIsolate = _isolateManager.mainIsolate.value; | ||
| Future<void> callExtension() async { | ||
| if (_isolateManager.mainIsolate.value != mainIsolate) return; | ||
| final mainIsolate = _mainIsolate; | ||
| Future<bool> callExtension() async { | ||
| if (_mainIsolate != mainIsolate) return false; | ||
|
|
||
| assert(value != null); | ||
| try { | ||
|
|
@@ -408,28 +423,34 @@ final class ServiceExtensionManager with DisposerMixin { | |
| // of the extension name (ext.flutter.extensionName => extensionName). | ||
| args: {name.substring(name.lastIndexOf('.') + 1): value}, | ||
| ); | ||
| } else { | ||
| return false; | ||
|
srawlins marked this conversation as resolved.
Outdated
|
||
| } | ||
|
srawlins marked this conversation as resolved.
|
||
| } on RPCError catch (e) { | ||
| if (e.code == RPCErrorKind.kServerError.code) { | ||
| // Connection disappeared | ||
| return; | ||
| // The connection disappeared. | ||
| return false; | ||
| } | ||
| rethrow; | ||
| } | ||
|
|
||
| return true; | ||
| } | ||
|
|
||
| if (mainIsolate == null) return; | ||
| if (mainIsolate == null) return false; | ||
|
|
||
| final isolate = await _isolateManager.isolateState(mainIsolate).isolate; | ||
| if (_isolateManager.mainIsolate.value != mainIsolate) return; | ||
| if (_mainIsolate != mainIsolate) return false; | ||
|
|
||
| // Do not try to call Dart IO extensions for a paused isolate. | ||
| if (extensions.isDartIoExtension(name) && | ||
| isolate?.pauseEvent?.kind?.contains('Pause') == true) { | ||
| _callbacksOnIsolateResume | ||
| .putIfAbsent(mainIsolate, () => []) | ||
| .add(callExtension); | ||
| return true; | ||
| } else { | ||
| await callExtension(); | ||
| return await callExtension(); | ||
| } | ||
| } | ||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.