-
Notifications
You must be signed in to change notification settings - Fork 598
Protect execute from errors caused by 2 interleaved calls #3413
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
base: main
Are you sure you want to change the base?
Changes from all commits
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 |
---|---|---|
|
@@ -21,6 +21,7 @@ class ExecutionServiceImpl implements ExecutionService { | |
|
||
web.HTMLIFrameElement _frame; | ||
late String _frameSrc; | ||
Completer<void>? _activeExecuteCompleter; | ||
Completer<void> _readyCompleter = Completer(); | ||
|
||
ExecutionServiceImpl(this._frame) { | ||
|
@@ -37,11 +38,20 @@ class ExecutionServiceImpl implements ExecutionService { | |
required bool isNewDDC, | ||
required bool isFlutter, | ||
}) async { | ||
if (_activeExecuteCompleter != null) { | ||
await _activeExecuteCompleter!.future.timeout( | ||
Duration(seconds: 5), | ||
onTimeout: () { | ||
_activeExecuteCompleter?.complete(); | ||
}, | ||
); | ||
} | ||
_activeExecuteCompleter = Completer(); | ||
if (!reload) { | ||
await _reset(); | ||
} | ||
|
||
return _send(reload ? 'executeReload' : 'execute', { | ||
await _send(reload ? 'executeReload' : 'execute', { | ||
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. Looking at the implementation of 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. The completer is more for the I agree
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! I missed that this also included the |
||
'js': _decorateJavaScript( | ||
javaScript, | ||
modulesBaseUrl: modulesBaseUrl, | ||
|
@@ -52,6 +62,9 @@ class ExecutionServiceImpl implements ExecutionService { | |
if (engineVersion != null) | ||
'canvasKitBaseUrl': _canvasKitUrl(engineVersion), | ||
}); | ||
|
||
_activeExecuteCompleter?.complete(); | ||
_activeExecuteCompleter = null; | ||
} | ||
|
||
@override | ||
|
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we must have a general design issue to allow us request an execution while already performing one. The UI does disable the 'run' button when executing; perhaps this was introduced as a side effect of the genai preview feature? We may want to file an issue to track down how nested executions are possible.
In any case, guarding against it in the execution impl (here) seems reasonable. You're waiting (a bit) for the first execution to finish. I think that's reasonable; you could also just do an early exit if you see that there's a non-null completer (not start a 2nd execution). I don't have an opinion which to do.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this case I think the double execute is from the embedding context. I tried to track down what's triggering the two events but wasn't very successful.
But given there are probably other ways to introduce the same effect, making this atomic seems like a safety measure to avoid these double calls everywhere.
I was worried about the second execute actually containing different code than the first. For example, if the embedding environment sends an "empty" request followed by a real request in quick succession. We likely just want the second one.
Maybe we could throw out the first request, that seems like a safe alternative here. I don't have any strong opinions on that solution vs the one I have here. Do you have a preference?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be simplest to do an early exit if
_activeExecuteCompleter
is non-null and assume that the existing completer will complete correctly in the future, but no, no real preference.