Bug 224533 - [GTK][WPE] Properly recover from unresponsive web processes
Summary: [GTK][WPE] Properly recover from unresponsive web processes
Status: RESOLVED FIXED
Alias: None
Product: WebKit
Classification: Unclassified
Component: WebKitGTK (show other bugs)
Version: WebKit Nightly Build
Hardware: Unspecified Unspecified
: P2 Normal
Assignee: Miguel Gomez
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2021-04-14 00:41 PDT by Miguel Gomez
Modified: 2021-04-30 11:58 PDT (History)
8 users (show)

See Also:


Attachments
Patch (16.13 KB, patch)
2021-04-29 09:51 PDT, Miguel Gomez
no flags Details | Formatted Diff | Diff
Patch (15.93 KB, patch)
2021-04-30 01:59 PDT, Miguel Gomez
no flags Details | Formatted Diff | Diff
Patch (15.66 KB, patch)
2021-04-30 02:42 PDT, Miguel Gomez
no flags Details | Formatted Diff | Diff
Patch (15.75 KB, patch)
2021-04-30 06:01 PDT, Miguel Gomez
no flags Details | Formatted Diff | Diff
Patch (15.66 KB, patch)
2021-04-30 08:07 PDT, Miguel Gomez
no flags Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Miguel Gomez 2021-04-14 00:41:56 PDT
Bug 224359 added a signal to WebKitWebView to indicate whether the web process associated to it has become unresponsive. But currently the only way to recover from that scenario is by recreating the view, as the other methods that could be useful (reload, uri or load alternate html) will hang if the web process is not responsive. We need to look into those to make them work in that scenario.
Comment 1 Fujii Hironori 2021-04-28 00:38:54 PDT
You can get the pid of the web process by using WKPageGetProcessIdentifier API. You can kill the unresponsive web process. I am not sure this is the proper way to recover.
Comment 2 Miguel Gomez 2021-04-28 01:16:08 PDT
(In reply to Fujii Hironori from comment #1)
> You can get the pid of the web process by using WKPageGetProcessIdentifier
> API. You can kill the unresponsive web process. I am not sure this is the
> proper way to recover.

I think that's the intended way to do it when using the C API (I know some people are doing it that way).
In the glib API we don't have an equivalent to WKPageGetProcessIdentifier though. We could add one and follow the same approach, or we could just add an API method to WebKitWebView to directly kill the web content process.
Comment 3 Michael Catanzaro 2021-04-28 06:12:23 PDT
(In reply to Miguel Gomez from comment #2)
> or we could just add
> an API method to WebKitWebView to directly kill the web content process.

That seems like a better approach, but I'm not convinced it's needed. webkit_web_view_load_* could do the killing for us internally.
Comment 4 Carlos Garcia Campos 2021-04-29 00:49:34 PDT
My initial idea was to implicitly kill the web process if it's unresponsive when new load happens or on refresh, but now I'm more convinced that an explicit approach would be better. It might not be obvious for the user that a new load needs to happen, for example. In the case of a crash we don't have a web process anymore, so a new load is no different than the initial load. And we probably only want to kill the process if the user handled the signal, but it's a property notification so we don't really know what the user replied. If we decide to go with the explicit approach I would expose a method to kill the web view web process, I wouldn't expose process ids.
Comment 5 Miguel Gomez 2021-04-29 09:51:18 PDT
Created attachment 427352 [details]
Patch
Comment 6 EWS Watchlist 2021-04-29 09:52:23 PDT
Thanks for the patch. If this patch contains new public API please make sure it follows the guidelines for new WebKit2 GTK+ API. See https://trac.webkit.org/wiki/WebKitGTK/AddingNewWebKit2API
Comment 7 Michael Catanzaro 2021-04-29 10:29:27 PDT
Comment on attachment 427352 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=427352&action=review

LGTM

> Source/WebKit/UIProcess/API/glib/WebKitNavigationClient.cpp:121
> +        case ProcessTerminationReason::ExceededCPULimit:

Preexisting problem: this one should use WEBKIT_WEB_PROCESS_CRASHED (or ideally get its own public enum value). The other three cases occur when the process is terminated due to implementation detail, so it makes sense that the application is not notified. But exceeded CPU limit is definitely something the application needs to know about in order to offer the process crash error page.

> Source/WebKit/UIProcess/API/glib/WebKitWebView.cpp:4780
> +    if (auto* provisionalPageProxy = page.provisionalPageProxy()) {
> +        Ref<WebKit::WebProcessProxy> protectedProcessProxy(provisionalPageProxy->process());
> +        protectedProcessProxy->requestTermination(WebKit::ProcessTerminationReason::RequestedByClient);
> +    }

Hmm... do we really want to kill the provisional page? I'm not entirely sure what the provisional page is, but my guess is that is the prewarmed process that is waiting to be swapped in. If so, we don't want to kill it, as it's not unresponsive, and that would slow down the process swap that's about to occur. Am I wrong?

> Source/WebKit/UIProcess/API/gtk/WebKitWebView.h:196
> + * @WEBKIT_WEB_PROCESS_REQUESTED_BY_API: the web process termination was requested by an API call.

@WEBKIT_WEB_PROCESS_REQUESTED_BY_API: the web process termination was requested by an API call. Since: 2.34

> Source/WebKit/UIProcess/API/wpe/WebKitWebView.h:165
> + * @WEBKIT_WEB_PROCESS_REQUESTED_BY_API: the web process termination was requested by an API call.

@WEBKIT_WEB_PROCESS_REQUESTED_BY_API: the web process termination was requested by an API call. Since: 2.34

> Tools/TestWebKitAPI/Tests/WebKitGLib/TestWebKitWebView.cpp:1551
> +    static gboolean webProcessTerminatedCallback(WebKitWebView *webView, WebKitWebProcessTerminationReason reason, WebViewTerminateWebProcessTest* test)

This should return void, not gboolean, since the web-process-terminated signal has no return value.

Also the * is hanging wrong: WebKitWebView* webView

> Tools/TestWebKitAPI/Tests/WebKitGLib/TestWebKitWebView.cpp:1586
> +        "   setTimeout(function() {"
> +        "    var start = new Date().getTime();"
> +        "    var end = start;"
> +        "     while(end < start + 4000) {"
> +        "      end = new Date().getTime();"
> +        "     }"
> +        "    }, 500);"

I think you could just do while(true) in the timeout callback.
Comment 8 Michael Catanzaro 2021-04-29 10:31:25 PDT
(In reply to Michael Catanzaro from comment #7)
> Hmm... do we really want to kill the provisional page? I'm not entirely sure
> what the provisional page is, but my guess is that is the prewarmed process
> that is waiting to be swapped in. If so, we don't want to kill it, as it's
> not unresponsive, and that would slow down the process swap that's about to
> occur. Am I wrong?

Hm, maybe it is a page that's used during provisional loads before the load is committed? Then killing it is needed in case it is unresponsive?
Comment 9 Carlos Garcia Campos 2021-04-30 00:40:55 PDT
Comment on attachment 427352 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=427352&action=review

>> Source/WebKit/UIProcess/API/glib/WebKitNavigationClient.cpp:121
>> +        case ProcessTerminationReason::ExceededCPULimit:
> 
> Preexisting problem: this one should use WEBKIT_WEB_PROCESS_CRASHED (or ideally get its own public enum value). The other three cases occur when the process is terminated due to implementation detail, so it makes sense that the application is not notified. But exceeded CPU limit is definitely something the application needs to know about in order to offer the process crash error page.

We don't have CPU limit enabled, only memory limit. I agree we should enable it and then expose the option, but that should be a separate bug.

>> Source/WebKit/UIProcess/API/glib/WebKitWebView.cpp:4780
>> +    }
> 
> Hmm... do we really want to kill the provisional page? I'm not entirely sure what the provisional page is, but my guess is that is the prewarmed process that is waiting to be swapped in. If so, we don't want to kill it, as it's not unresponsive, and that would slow down the process swap that's about to occur. Am I wrong?

This is what cocoa does
Comment 10 Miguel Gomez 2021-04-30 01:59:27 PDT
Created attachment 427401 [details]
Patch
Comment 11 Carlos Garcia Campos 2021-04-30 02:09:58 PDT
Comment on attachment 427401 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=427401&action=review

> Source/WebKit/UIProcess/API/glib/WebKitWebView.cpp:4763
> + * signal is emitted with WEBKIT_WEB_PROCESS_REQUESTED_BY_API as the reason for

WEBKIT_WEB_PROCESS_REQUESTED_BY_API -> %WEBKIT_WEB_PROCESS_REQUESTED_BY_API

> Tools/TestWebKitAPI/Tests/WebKitGLib/TestWebKitWebView.cpp:1561
> +    g_signal_connect_after(test->m_webView, "web-process-terminated", G_CALLBACK(WebViewTerminateWebProcessTest::webProcessTerminatedCallback), test);

I would connect to the signal from the WebViewTerminateWebProcessTest constructor

> Tools/TestWebKitAPI/Tests/WebKitGLib/TestWebKitWebView.cpp:1570
> +    g_signal_handlers_disconnect_by_func(test->m_webView, reinterpret_cast<void*>(WebViewTerminateWebProcessTest::webProcessTerminatedCallback), test);

And disconnect from the destructor using g_signal_handlers_disconnect_by_data

> Tools/TestWebKitAPI/Tests/WebKitGLib/TestWebKitWebView.cpp:1586
> +    g_signal_connect_after(test->m_webView, "web-process-terminated", G_CALLBACK(WebViewTerminateWebProcessTest::webProcessTerminatedCallback), test);

Ditto.

> Tools/TestWebKitAPI/Tests/WebKitGLib/TestWebKitWebView.cpp:1606
> +    g_signal_handlers_disconnect_by_func(test->m_webView, reinterpret_cast<void*>(WebViewTerminateWebProcessTest::webProcessTerminatedCallback), test);

Ditto.
Comment 12 Miguel Gomez 2021-04-30 02:42:51 PDT
Created attachment 427403 [details]
Patch
Comment 13 Michael Catanzaro 2021-04-30 05:16:29 PDT
Comment on attachment 427403 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=427403&action=review

OK, nice.

> Source/WebKit/UIProcess/API/gtk/WebKitWebView.h:196
> + * @WEBKIT_WEB_PROCESS_REQUESTED_BY_API: the web process termination was requested by an API call. Since: 2.34

Let's call it WEBKIT_WEB_PROCESS_TERMINATION_REQUESTED_BY_API or WEBKIT_WEB_PROCESS_KILL_REQUESTED_BY_API
Comment 14 Miguel Gomez 2021-04-30 06:01:59 PDT
Created attachment 427411 [details]
Patch
Comment 15 Adrian Perez 2021-04-30 06:32:26 PDT
(In reply to Michael Catanzaro from comment #13)
> Comment on attachment 427403 [details]
> Patch
> 
> View in context:
> https://bugs.webkit.org/attachment.cgi?id=427403&action=review
> 
> OK, nice.
> 
> > Source/WebKit/UIProcess/API/gtk/WebKitWebView.h:196
> > + * @WEBKIT_WEB_PROCESS_REQUESTED_BY_API: the web process termination was requested by an API call. Since: 2.34
> 
> Let's call it WEBKIT_WEB_PROCESS_TERMINATION_REQUESTED_BY_API or
> WEBKIT_WEB_PROCESS_KILL_REQUESTED_BY_API

Gah! Such a mouthful. How about WEBKIT_WEB_PROCESS_API_TERMINATION,
which is two words less? :)
Comment 16 Carlos Garcia Campos 2021-04-30 07:18:24 PDT
WEBKIT_WEB_PROCESS_TERMINATED_BY_API
Comment 17 Miguel Gomez 2021-04-30 08:07:25 PDT
Created attachment 427419 [details]
Patch
Comment 18 Michael Catanzaro 2021-04-30 08:36:31 PDT
(In reply to Carlos Garcia Campos from comment #16)
> WEBKIT_WEB_PROCESS_TERMINATED_BY_API

Good name.
Comment 19 EWS 2021-04-30 11:58:11 PDT
Committed r276848 (237199@main): <https://commits.webkit.org/237199@main>

All reviewed patches have been landed. Closing bug and clearing flags on attachment 427419 [details].