Certain WebProcesses should opt-out of the freezer.
authorbeidson@apple.com <beidson@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 22 Mar 2019 03:00:44 +0000 (03:00 +0000)
committerbeidson@apple.com <beidson@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 22 Mar 2019 03:00:44 +0000 (03:00 +0000)
<rdar://problem/42846139> and https://bugs.webkit.org/show_bug.cgi?id=196062

Reviewed by Andy Estes.

Source/bmalloc:

* bmalloc.xcodeproj/project.pbxproj:
* bmalloc/darwin/MemoryStatusSPI.h:

Source/WebKit:

WebProcesses should opt-in and opt-out of the freezer as is appropriate.
By default a WebProcess is freezer eligible.
If any of the following become true then it should become ineligible:
- The WebProcess is a pre-warmed process.
- The WebProcess is in the process cache.
- The WebProcess is not actively hosting any web pages (e.g. it only has suspending web pages)

The most complicated part of the above is guaranteeing that any operation that changes
the active pages hosted by the process causes a recalculation of the freezer opt-in state.

To do that this replaces the basic WebPageProxyMap with a custom class.

* UIProcess/WebProcessProxy.cpp:
(WebKit::globalPageMap):
(WebKit::WebProcessProxy::WebProcessProxy):
(WebKit::WebProcessProxy::validateFreezerStatus):
(WebKit::WebProcessProxy::setIsInProcessCache):
(WebKit::WebProcessProxy::markIsNoLongerInPrewarmedPool):
(WebKit::WebProcessProxy::didFinishLaunching):
* UIProcess/WebProcessProxy.h:
(WebKit::WebProcessProxy::WebPageProxyMap::WebPageProxyMap):
(WebKit::WebProcessProxy::WebPageProxyMap::size const):
(WebKit::WebProcessProxy::WebPageProxyMap::values):
(WebKit::WebProcessProxy::WebPageProxyMap::values const):
(WebKit::WebProcessProxy::WebPageProxyMap::begin):
(WebKit::WebProcessProxy::WebPageProxyMap::end):
(WebKit::WebProcessProxy::WebPageProxyMap::get):
(WebKit::WebProcessProxy::WebPageProxyMap::contains const):
(WebKit::WebProcessProxy::WebPageProxyMap::isEmpty const):
(WebKit::WebProcessProxy::WebPageProxyMap::set):
(WebKit::WebProcessProxy::WebPageProxyMap::take):

* WebProcess/WebProcess.cpp:
(WebKit::WebProcess::setFreezable):
* WebProcess/WebProcess.h:
* WebProcess/WebProcess.messages.in:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@243357 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/WebKit/ChangeLog
Source/WebKit/UIProcess/WebProcessProxy.cpp
Source/WebKit/UIProcess/WebProcessProxy.h
Source/WebKit/WebProcess/WebProcess.cpp
Source/WebKit/WebProcess/WebProcess.h
Source/WebKit/WebProcess/WebProcess.messages.in
Source/bmalloc/ChangeLog
Source/bmalloc/bmalloc.xcodeproj/project.pbxproj
Source/bmalloc/bmalloc/darwin/MemoryStatusSPI.h

index d3b3638..4f3ef90 100644 (file)
@@ -1,3 +1,47 @@
+2019-03-21  Brady Eidson  <beidson@apple.com>
+
+        Certain WebProcesses should opt-out of the freezer.
+        <rdar://problem/42846139> and https://bugs.webkit.org/show_bug.cgi?id=196062
+
+        Reviewed by Andy Estes.
+
+        WebProcesses should opt-in and opt-out of the freezer as is appropriate.
+        By default a WebProcess is freezer eligible.
+        If any of the following become true then it should become ineligible:
+        - The WebProcess is a pre-warmed process.
+        - The WebProcess is in the process cache.
+        - The WebProcess is not actively hosting any web pages (e.g. it only has suspending web pages)
+
+        The most complicated part of the above is guaranteeing that any operation that changes
+        the active pages hosted by the process causes a recalculation of the freezer opt-in state.
+
+        To do that this replaces the basic WebPageProxyMap with a custom class.
+
+        * UIProcess/WebProcessProxy.cpp:
+        (WebKit::globalPageMap):
+        (WebKit::WebProcessProxy::WebProcessProxy):
+        (WebKit::WebProcessProxy::validateFreezerStatus):
+        (WebKit::WebProcessProxy::setIsInProcessCache):
+        (WebKit::WebProcessProxy::markIsNoLongerInPrewarmedPool):
+        (WebKit::WebProcessProxy::didFinishLaunching):
+        * UIProcess/WebProcessProxy.h:
+        (WebKit::WebProcessProxy::WebPageProxyMap::WebPageProxyMap):
+        (WebKit::WebProcessProxy::WebPageProxyMap::size const):
+        (WebKit::WebProcessProxy::WebPageProxyMap::values):
+        (WebKit::WebProcessProxy::WebPageProxyMap::values const):
+        (WebKit::WebProcessProxy::WebPageProxyMap::begin):
+        (WebKit::WebProcessProxy::WebPageProxyMap::end):
+        (WebKit::WebProcessProxy::WebPageProxyMap::get):
+        (WebKit::WebProcessProxy::WebPageProxyMap::contains const):
+        (WebKit::WebProcessProxy::WebPageProxyMap::isEmpty const):
+        (WebKit::WebProcessProxy::WebPageProxyMap::set):
+        (WebKit::WebProcessProxy::WebPageProxyMap::take):
+
+        * WebProcess/WebProcess.cpp:
+        (WebKit::WebProcess::setFreezable):
+        * WebProcess/WebProcess.h:
+        * WebProcess/WebProcess.messages.in:
+
 2019-03-21  Tim Horton  <timothy_horton@apple.com>
 
         Adopt UIWKDocumentContext
index 2f448dc..d4d9f68 100644 (file)
@@ -115,10 +115,10 @@ uint64_t WebProcessProxy::generatePageID()
     return ++uniquePageID;
 }
 
-static WebProcessProxy::WebPageProxyMap& globalPageMap()
+static WebProcessProxy::WebPageProxyMap::MapType& globalPageMap()
 {
     ASSERT(isMainThreadOrCheckDisabled());
-    static NeverDestroyed<WebProcessProxy::WebPageProxyMap> pageMap;
+    static NeverDestroyed<WebProcessProxy::WebPageProxyMap::MapType> pageMap;
     return pageMap;
 }
 
@@ -136,6 +136,7 @@ WebProcessProxy::WebProcessProxy(WebProcessPool& processPool, WebsiteDataStore&
     , m_backgroundResponsivenessTimer(*this)
     , m_processPool(processPool, isPrewarmed == IsPrewarmed::Yes ? IsWeak::Yes : IsWeak::No)
     , m_mayHaveUniversalFileReadSandboxExtension(false)
+    , m_pageMap(*this)
     , m_numberOfTimesSuddenTerminationWasDisabled(0)
     , m_throttler(*this, processPool.shouldTakeUIBackgroundAssertion())
     , m_isResponsive(NoOrMaybe::Maybe)
@@ -187,6 +188,18 @@ WebProcessProxy::~WebProcessProxy()
 #endif
 }
 
+void WebProcessProxy::validateFreezerStatus()
+{
+#if PLATFORM(IOS_FAMILY)
+    bool value = !m_isPrewarmed && !m_isInProcessCache && !m_pageMap.isEmpty() && !isServiceWorkerProcess();
+    if (m_currentIsFreezableValue != WTF::nullopt && m_currentIsFreezableValue == value)
+        return;
+
+    m_currentIsFreezableValue = value;
+    send(Messages::WebProcess::SetFreezable(value), 0);
+#endif
+}
+
 void WebProcessProxy::setIsInProcessCache(bool value)
 {
     ASSERT(m_isInProcessCache != value);
@@ -202,6 +215,8 @@ void WebProcessProxy::setIsInProcessCache(bool value)
         RELEASE_ASSERT(m_processPool);
         m_processPool.setIsWeak(IsWeak::No);
     }
+    
+    validateFreezerStatus();
 }
 
 void WebProcessProxy::getLaunchOptions(ProcessLauncher::LaunchOptions& launchOptions)
@@ -379,6 +394,8 @@ void WebProcessProxy::markIsNoLongerInPrewarmedPool()
     RELEASE_ASSERT(m_processPool);
     m_processPool.setIsWeak(IsWeak::No);
 
+    validateFreezerStatus();
+
     send(Messages::WebProcess::MarkIsNoLongerPrewarmed(), 0);
 }
 
@@ -772,6 +789,8 @@ void WebProcessProxy::didFinishLaunching(ProcessLauncher* launcher, IPC::Connect
 
     unblockAccessibilityServerIfNeeded();
 #endif
+
+    validateFreezerStatus();
 }
 
 WebFrameProxy* WebProcessProxy::webFrame(uint64_t frameID) const
index 276490d..477eba5 100644 (file)
@@ -96,7 +96,6 @@ enum class AllowProcessCaching { No, Yes };
 class WebProcessProxy : public AuxiliaryProcessProxy, public ResponsivenessTimer::Client, public ThreadSafeRefCounted<WebProcessProxy>, public CanMakeWeakPtr<WebProcessProxy>, private ProcessThrottlerClient {
 public:
     typedef HashMap<uint64_t, RefPtr<WebFrameProxy>> WebFrameProxyMap;
-    typedef HashMap<uint64_t, WebPageProxy*> WebPageProxyMap;
     typedef HashMap<uint64_t, RefPtr<API::UserInitiatedAction>> UserInitiatedActionMap;
 
     enum class IsPrewarmed {
@@ -137,6 +136,45 @@ public:
     void addProvisionalPageProxy(ProvisionalPageProxy& provisionalPage) { ASSERT(!m_provisionalPages.contains(&provisionalPage)); m_provisionalPages.add(&provisionalPage); }
     void removeProvisionalPageProxy(ProvisionalPageProxy& provisionalPage) { ASSERT(m_provisionalPages.contains(&provisionalPage)); m_provisionalPages.remove(&provisionalPage); }
 
+    class WebPageProxyMap {
+    public:
+        WebPageProxyMap(WebProcessProxy& proxy)
+            : m_proxy(proxy)
+        {
+        }
+
+        typedef HashMap<uint64_t, WebPageProxy*> MapType;
+        using ValuesConstIteratorRange = MapType::ValuesConstIteratorRange;
+
+        auto size() const { return m_map.size(); }
+        auto values() { return m_map.values(); }
+        auto values() const { return m_map.values(); }
+        auto begin() { return m_map.begin(); }
+        auto end() { return m_map.end(); }
+        auto get(uint64_t key) { return m_map.get(key); }
+        auto contains(uint64_t key) const { return m_map.contains(key); }
+        auto isEmpty() const { return m_map.isEmpty(); }
+
+        auto set(uint64_t key, WebPageProxy* value)
+        {
+            auto result = m_map.set(key, value);
+            m_proxy.validateFreezerStatus();
+            return result;
+        }
+
+        auto take(uint64_t key)
+        {
+            auto result = m_map.take(key);
+            m_proxy.validateFreezerStatus();
+            return result;
+        }
+
+    private:
+        WebProcessProxy& m_proxy;
+        MapType m_map;
+    };
+
+    
     typename WebPageProxyMap::ValuesConstIteratorRange pages() const { return m_pageMap.values(); }
     unsigned pageCount() const { return m_pageMap.size(); }
     unsigned provisionalPageCount() const { return m_provisionalPages.size(); }
@@ -321,6 +359,8 @@ protected:
 
     bool isJITEnabled() const final;
 
+    void validateFreezerStatus();
+
 private:
     // IPC message handlers.
     void updateBackForwardItem(const BackForwardListItemState&);
@@ -469,6 +509,7 @@ private:
     unsigned m_suspendedPageCount { 0 };
     bool m_hasCommittedAnyProvisionalLoads { false };
     bool m_isPrewarmed;
+    Optional<bool> m_currentIsFreezableValue;
 
 #if PLATFORM(WATCHOS)
     ProcessThrottler::BackgroundActivityToken m_backgroundActivityTokenForFullscreenFormControls;
index a1441ea..9f36eb8 100644 (file)
 #include <JavaScriptCore/RemoteInspector.h>
 #endif
 
+#if PLATFORM(IOS_FAMILY)
+#include <bmalloc/MemoryStatusSPI.h>
+#endif
+
 // This should be less than plugInAutoStartExpirationTimeThreshold in PlugInAutoStartProvider.
 static const Seconds plugInAutoStartExpirationTimeUpdateThreshold { 29 * 24 * 60 * 60 };
 
@@ -1871,6 +1875,14 @@ void WebProcess::clearCurrentModifierStateForTesting()
     PlatformKeyboardEvent::setCurrentModifierState({ });
 }
 
+void WebProcess::setFreezable(bool freezable)
+{
+#if PLATFORM(IOS_FAMILY)
+    auto result = memorystatus_control(MEMORYSTATUS_CMD_SET_PROCESS_IS_FREEZABLE, getpid(), freezable ? 1 : 0, nullptr, 0);
+    ASSERT_UNUSED(result, !result);
+#endif
+}
+
 #if PLATFORM(IOS_FAMILY)
 void WebProcess::unblockAccessibilityServer(const SandboxExtension::Handle& handle)
 {
index e4c75b8..29520e4 100644 (file)
@@ -426,6 +426,7 @@ private:
 #endif
 
     void clearCurrentModifierStateForTesting();
+    void setFreezable(bool);
 
     RefPtr<WebConnectionToUIProcess> m_webConnection;
 
index 95e38ce..124285d 100644 (file)
@@ -161,4 +161,6 @@ messages -> WebProcess LegacyReceiver {
 #if PLATFORM(IOS_FAMILY)
     UnblockAccessibilityServer(WebKit::SandboxExtension::Handle handle)
 #endif
+
+    SetFreezable(bool freezable)
 }
index d2fbbc4..13e0636 100644 (file)
@@ -1,3 +1,13 @@
+2019-03-21  Brady Eidson  <beidson@apple.com>
+
+        Certain WebProcesses should opt-out of the freezer.
+        <rdar://problem/42846139> and https://bugs.webkit.org/show_bug.cgi?id=196062
+
+        Reviewed by Andy Estes.
+
+        * bmalloc.xcodeproj/project.pbxproj:
+        * bmalloc/darwin/MemoryStatusSPI.h:
+
 2019-03-19  Michael Catanzaro  <mcatanzaro@igalia.com>
 
         Unreviewed, fix -Wformat warning
index 5840f53..233806e 100644 (file)
                4426E2801C838EE0008EB042 /* Logging.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4426E27E1C838EE0008EB042 /* Logging.cpp */; };
                4426E2811C838EE0008EB042 /* Logging.h in Headers */ = {isa = PBXBuildFile; fileRef = 4426E27F1C838EE0008EB042 /* Logging.h */; settings = {ATTRIBUTES = (Private, ); }; };
                4426E2831C839547008EB042 /* BSoftLinking.h in Headers */ = {isa = PBXBuildFile; fileRef = 4426E2821C839547008EB042 /* BSoftLinking.h */; };
-               52F47249210BA30200B730BB /* MemoryStatusSPI.h in Headers */ = {isa = PBXBuildFile; fileRef = 52F47248210BA2F500B730BB /* MemoryStatusSPI.h */; };
+               52F47249210BA30200B730BB /* MemoryStatusSPI.h in Headers */ = {isa = PBXBuildFile; fileRef = 52F47248210BA2F500B730BB /* MemoryStatusSPI.h */; settings = {ATTRIBUTES = (Private, ); }; };
                6543DDB420EEAEF3003B23D8 /* PerThread.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6543DDB320EEAEF3003B23D8 /* PerThread.cpp */; };
                6599C5CC1EC3F15900A2F7BB /* AvailableMemory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6599C5CA1EC3F15900A2F7BB /* AvailableMemory.cpp */; };
                6599C5CD1EC3F15900A2F7BB /* AvailableMemory.h in Headers */ = {isa = PBXBuildFile; fileRef = 6599C5CB1EC3F15900A2F7BB /* AvailableMemory.h */; settings = {ATTRIBUTES = (Private, ); }; };
index 469fa03..6383544 100644 (file)
@@ -43,6 +43,8 @@ typedef struct memorystatus_memlimit_properties {
 } memorystatus_memlimit_properties_t;
 
 #define MEMORYSTATUS_CMD_GET_MEMLIMIT_PROPERTIES 8
+#define MEMORYSTATUS_CMD_SET_PROCESS_IS_FREEZABLE 18
+#define MEMORYSTATUS_CMD_GET_PROCESS_IS_FREEZABLE 19
 
 }
 #endif // __has_include(<System/sys/kern_memorystatus.h>)