-
Notifications
You must be signed in to change notification settings - Fork 137
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
Fix Edge Browser Deadlock issue for instantiation during a WebView Callback #1378
Conversation
The temp file is not deleted on exit. Temp files on Windows can accumulate over time and Windows never deletes them, so perhaps |
This PR creates a temporary file and replaces the original implementation of And import org.eclipse.swt.SWT;
import org.eclipse.swt.browser.Browser;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
public class SWTBrowserTest {
public static void main(String[] args) {
final Display display = new Display();
final Shell shell = new Shell(display);
shell.setText("SWT Browser");
shell.setLayout(new FillLayout());
shell.setSize(1024, 768);
String html = makeHTMLEntry();
System.out.println(html);
Browser browser = new Browser(shell, SWT.EDGE);
browser.setText(html);
System.out.println(browser.getText());
shell.open();
while(!shell.isDisposed()) {
if(!display.readAndDispatch()) {
display.sleep();
}
}
display.dispose();
}
private static String makeHTMLEntry() {
StringBuffer html = new StringBuffer();
html.append("<html><head>");
html.append("</head>");
html.append("<body>");
html.append("<p>Hello World!</p>");
html.append("</body>");
html.append("</html>");
return html.toString();
}
} Output is as follows (first line is original HTML, second line is returned from
|
7b7ea3a
to
95d0757
Compare
If |
bundles/org.eclipse.swt/Eclipse SWT Browser/win32/org/eclipse/swt/browser/Edge.java
Outdated
Show resolved
Hide resolved
bundles/org.eclipse.swt/Eclipse SWT Browser/win32/org/eclipse/swt/browser/Edge.java
Show resolved
Hide resolved
The idea to put it in Browser was because this is needed outside swt as well and having it in Edge would lead to problems for other platforms. Moreover, this approach in Edge is also a high level implementation which means that this could be used for other browsers as well, since this problem can occur with any modern browser. I am not sure if a similar issue exists with browsers in other platforms but if so, in future we can do something similar to them as well. But for now, we cant use Edge.BASE_URL outside SWT, since it is windows specific. We need the BASE_URL in 2 places: jdt.ui and eclipse.platform, these PRs would be follow up after this PR, as mentioned in the description. However I can make the static block to only create a file if Edge is the default, if necessary. |
This issue was because of the asynchronous behaviour. I have fixed it now. |
Perhaps create it lazily when |
50bd0bc
to
b3a9b1f
Compare
bundles/org.eclipse.swt/Eclipse SWT Browser/win32/org/eclipse/swt/browser/Edge.java
Outdated
Show resolved
Hide resolved
bundles/org.eclipse.swt/Eclipse SWT Browser/win32/org/eclipse/swt/browser/Edge.java
Outdated
Show resolved
Hide resolved
bundles/org.eclipse.swt/Eclipse SWT Browser/win32/org/eclipse/swt/browser/Edge.java
Outdated
Show resolved
Hide resolved
bundles/org.eclipse.swt/Eclipse SWT Browser/win32/org/eclipse/swt/browser/Edge.java
Outdated
Show resolved
Hide resolved
bundles/org.eclipse.swt/Eclipse SWT Browser/win32/org/eclipse/swt/browser/Edge.java
Outdated
Show resolved
Hide resolved
bundles/org.eclipse.swt/Eclipse SWT Browser/win32/org/eclipse/swt/browser/Edge.java
Outdated
Show resolved
Hide resolved
bundles/org.eclipse.swt/Eclipse SWT Browser/win32/org/eclipse/swt/browser/Edge.java
Outdated
Show resolved
Hide resolved
c0ddfb8
to
47c0484
Compare
5a2fdf4
to
8897897
Compare
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.
Looks far better now but I know this PR is going to be changed so please @Phillipus / @HeikoKlare review too.
I'm out of office the next 2 weeks.
Split the PR into 2 and moved the set text fix to #1395 |
This PR is based on #1395. Once 1395 is merged, it will not be visible. The other commit is from #1395. As mentioned in the description, only the last commit is relevant for this PR. |
My 2 findings need to be addressed in a separate PR
78f900a
to
73be7aa
Compare
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.
The code looks much better structured now. I still see some easy opportunities for improving the WebViewProvider
from a rather data-wrapping class to a better encapsulated provider class via moving more of the initialization logic inside that class and reducing the methods to be called from the consumer.
I also have one remark regarding a potential regressions, but most of the comments concern design and documentation improvements.
private CompletableFuture<ICoreWebView2_11> webView_11Future = new CompletableFuture<>(); | ||
|
||
private CompletableFuture<Void> lastWebViewTask = webViewFuture.thenCompose(__ -> { | ||
return CompletableFuture.allOf(webView_2Future.handle((result, ex) -> null), webView_11Future.handle((result, ex) -> null)); |
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 do not understand the purpose of this line. Maybe you can wrap the initialization of lastWebViewTask
into a method that better explains (by its name or, if not possible, by documentation) what this is supposed 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.
I'll extract it to a method. This one bascially creates a future which will complete after webview is completed and after that webview_2 and webview_11 are either completed or cancelled (depending on the execution in setup task), just to make sure once all the webviews are ready before we start executing the other scheduled tasks
What should I name it? getWaitForWebViewInitializationFuture?
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 do not understand the purpose of the calls to the method handle
.
Anyway, why not simply complete the webViewFuture after the other web view interface have been initialized, i.e., in the initializeWebView
method, replace this:
webViewFuture.complete(new ICoreWebView2(ppv[0]));
initializeWebView_2();
initializeWebView_11();
with something like this:
ICoreWebView2 webView = new ICoreWebView2(ppv[0]);
initializeWebView_2(webView);
initializeWebView_11(webView);
webViewFuture.complete(webView);
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.
handle basically chains the 2 futures telling that we dont care if they are cancelled or completed but it the lastWebViewTask would only be completed after both the webView_2 and webView_11 are done after webView itself. But I like the approach. I'll create a Completablefuture and complete it following the initialization.
bundles/org.eclipse.swt/Eclipse SWT Browser/win32/org/eclipse/swt/browser/Edge.java
Outdated
Show resolved
Hide resolved
bundles/org.eclipse.swt/Eclipse SWT Browser/win32/org/eclipse/swt/browser/Edge.java
Outdated
Show resolved
Hide resolved
bundles/org.eclipse.swt/Eclipse SWT Browser/win32/org/eclipse/swt/browser/Edge.java
Outdated
Show resolved
Hide resolved
bundles/org.eclipse.swt/Eclipse SWT Browser/win32/org/eclipse/swt/browser/Edge.java
Outdated
Show resolved
Hide resolved
bundles/org.eclipse.swt/Eclipse SWT Browser/win32/org/eclipse/swt/browser/Edge.java
Outdated
Show resolved
Hide resolved
bundles/org.eclipse.swt/Eclipse SWT Browser/win32/org/eclipse/swt/browser/Edge.java
Outdated
Show resolved
Hide resolved
bundles/org.eclipse.swt/Eclipse SWT Browser/win32/org/eclipse/swt/browser/Edge.java
Outdated
Show resolved
Hide resolved
bundles/org.eclipse.swt/Eclipse SWT Browser/win32/org/eclipse/swt/browser/Edge.java
Outdated
Show resolved
Hide resolved
....swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_browser_Browser.java
Show resolved
Hide resolved
0f4dd4e
to
91431d7
Compare
bundles/org.eclipse.swt/Eclipse SWT Browser/win32/org/eclipse/swt/browser/Edge.java
Outdated
Show resolved
Hide resolved
bundles/org.eclipse.swt/Eclipse SWT Browser/win32/org/eclipse/swt/browser/Edge.java
Outdated
Show resolved
Hide resolved
@@ -312,12 +407,13 @@ static ICoreWebView2CookieManager getCookieManager() { | |||
SWT.error(SWT.ERROR_NOT_IMPLEMENTED, null, " [WebView2: cookie access requires a Browser instance]"); | |||
} | |||
Edge instance = environmentWrapper.instances().get(0); | |||
if (instance.webView_2 == null) { | |||
instance.webViewProvider.waitForPendingWebviewTasksToFinish(); |
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.
Now all except this calls to waitForPendingWebiewTaskToFinish()
are encapsulated within the WebViewProvider
. I propose to keep that functionality completely hidden in the WebViewProvider
. I guess calls to isWebView*Unavailable()
should probably block until initialization has finished, as otherwise the method will return unreasonable resonable in case the web view has not been initialized yet anyway.
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.
cant put waitForPendingWebiewTaskToFinish inside isWebView*Unavailable cause its used in callbacks and calling it inside that would cause a deadlock
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.
But actually that's not even what you want to do here, is it? You want to wait for webView being initialized, not for all tasks being finished. That is something that should be done in the isWebView*Unavailable()
methods anyway in order to avoid that they return unreasonable results when called while the web view has not been initialized yet.
In any case, hiding the need to wait for pending tasks in all but one case is a design flaw for which we need to have a solution.
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 moved it inside the method and tested it. It seems fine and it isnt causing any deadlock. Seems good.
....swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_browser_Browser.java
Show resolved
Hide resolved
private CompletableFuture<ICoreWebView2_11> webView_11Future = new CompletableFuture<>(); | ||
|
||
private CompletableFuture<Void> lastWebViewTask = webViewFuture.thenCompose(__ -> { | ||
return CompletableFuture.allOf(webView_2Future.handle((result, ex) -> null), webView_11Future.handle((result, ex) -> null)); |
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 do not understand the purpose of the calls to the method handle
.
Anyway, why not simply complete the webViewFuture after the other web view interface have been initialized, i.e., in the initializeWebView
method, replace this:
webViewFuture.complete(new ICoreWebView2(ppv[0]));
initializeWebView_2();
initializeWebView_11();
with something like this:
ICoreWebView2 webView = new ICoreWebView2(ppv[0]);
initializeWebView_2(webView);
initializeWebView_11(webView);
webViewFuture.complete(webView);
91431d7
to
2234dfa
Compare
bundles/org.eclipse.swt/Eclipse SWT Browser/win32/org/eclipse/swt/browser/Edge.java
Outdated
Show resolved
Hide resolved
bundles/org.eclipse.swt/Eclipse SWT Browser/win32/org/eclipse/swt/browser/Edge.java
Outdated
Show resolved
Hide resolved
006a04d
to
696e45c
Compare
bundles/org.eclipse.swt/Eclipse SWT Browser/win32/org/eclipse/swt/browser/Edge.java
Show resolved
Hide resolved
43f0a80
to
d9f776c
Compare
This contribution fixes Edge browser Deadlock issue on instantiating a new Edge Browser object during a webview callback using async execution. The Implementation follows the pattern similar to how WebKit has been implemented imitating the async behaviour in a sync fashion using Futures and callbacks. contributes to eclipse-platform#669
d9f776c
to
1f22a98
Compare
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.
Looks like a rather clean solution now. Locally executing the Browser tests for Edge is successful. In some manual testing, I also did not find any issues so far.
Let's give this a try.
This contribution fixes Edge browser for win32 to handle the deadlock issue during the instantiation of a new edge browser object amidst a webview callback.
The problem with webview is that it can't execute tasks simultaneously and all the requests are serialized. Hence, a new request to the webview inside its callback leads to a deadlock since, both the tasks wait for each other to execute. The solution is to schedule the Webview calls during the callback asynchronously and let the control in the callback move on.
contributes to #669
Scope not covered
Pre-requisites
Testing
org.eclipse.swt.browser.BrowserFactory.createWebBrowser
to: