[WK2][iOS] Limit the number of vnodes used by the WebContent processes
authorcdumez@apple.com <cdumez@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 5 Jun 2015 23:20:06 +0000 (23:20 +0000)
committercdumez@apple.com <cdumez@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 5 Jun 2015 23:20:06 +0000 (23:20 +0000)
https://bugs.webkit.org/show_bug.cgi?id=145672
<rdar://problem/21126637>

Reviewed by Antti Koivisto.

Source/WebCore:

Limit the number of vnodes used by the WebContent processes to reduce
the chance of getting killed due to the system running out of vnodes.
We sometimes see the WebContent process use up to 50% of the system's
vnode limit on some tests on iOS, which seems excessive. Most vnodes
are due to CachedResources which are mmap'd from the WebKit disk cache
and kept alive due to caches such as the Memory Cache / PageCache.

This patch adds tracking for the number of SharedBuffer objects that are
backed by a CFDataRef as this should track most of the vnodes used by
the process. The WebContent process registers a vnode pressure handler
upon initialization (likely it already does for memory pressure
handling). This handler gets called when the process uses over 15% of
of system's vnode limit and the "critical" flag is set if it uses over
20% of the system limit. The vnode pressure handler currently calls the
memory pressure handler as freeing our caches (especially PageCache,
MemoryCache) and doing a JS GC frees up vnodes.

On iPhone 6 Plus, the system limit is at 3000, which would lead to the
following limits being used:
soft: 450 / hard: 600

However, on older hardware, the system limit can be as low as 2000,
which would lead to the following limits being used:
soft: 300 / hard: 400

vnode tracking is currently only enabled on iOS because system vnode
limits are usually much higher on Mac (e.g. 473695 on Mac Pro, 9984
on a 2011 MacBook Air) and we normally don't get killed.

* WebCore.xcodeproj/project.pbxproj:

Add new files to the XCode project.

* platform/SharedBuffer.h:

Add a VNodeTracker::Token member next to the CFDataRef member that we
initialize if the SharedBuffer is constructed from a CFDataRef.

* platform/VNodeTracker.cpp: Added.
(WebCore::VNodeTracker::singleton):
(WebCore::VNodeTracker::VNodeTracker):
(WebCore::VNodeTracker::checkPressureState):
(WebCore::VNodeTracker::pressureWarningTimerFired):
(WebCore::VNodeTracker::nextPressureWarningInterval):
(WebCore::VNodeTracker::platformInitialize):
* platform/VNodeTracker.h: Added.
(WebCore::VNodeTracker::setPressureHandler):

Add a new VNodeTracker singleton to keep track the number of vnodes
used by the process since the system does not issue warnings before
reaching its vnode limit. Call sites should request Tokens for
every vnode they use for tracking purposes. Each process can then
set a vnode pressure handler function that will be called when the
process is over the designated limits.

* platform/cf/SharedBufferCF.cpp:
(WebCore::SharedBuffer::SharedBuffer):

Take a VNodeToken from the VNodeTracker when constructing the
SharedBuffer from a CFDataRef as these usually use mmap.

* platform/cocoa/VNodeTrackerCocoa.cpp: Added.
(WebCore::VNodeTracker::platformInitialize):

Get the system's vnode limit and uses 15% of that value as soft
limit for the process and 20% of that value as hard limit.

Source/WebKit2:

Have the WebContent process register a vnode pressure handler on iOS,
which calls the memory pressure handler.

On non-critical pressure, it will prune dead resources from the memory
cache, which should free up some vnodes. On critical pressure, the
handler will clear the PageCache and do a JS GC, which should free even
more vnodes.

* WebProcess/cocoa/WebProcessCocoa.mm:
(WebKit::WebProcess::platformInitializeWebProcess):

Source/WTF:

* wtf/RefCounter.h:
(WTF::RefCounter::value):

Expose the actual refcount instead of a boolean value.

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

13 files changed:
Source/WTF/ChangeLog
Source/WTF/wtf/RefCounter.h
Source/WebCore/ChangeLog
Source/WebCore/WebCore.vcxproj/WebCore.vcxproj
Source/WebCore/WebCore.vcxproj/WebCore.vcxproj.filters
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Source/WebCore/platform/SharedBuffer.h
Source/WebCore/platform/VNodeTracker.cpp [new file with mode: 0644]
Source/WebCore/platform/VNodeTracker.h [new file with mode: 0644]
Source/WebCore/platform/cf/SharedBufferCF.cpp
Source/WebCore/platform/cocoa/VNodeTrackerCocoa.cpp [new file with mode: 0644]
Source/WebKit2/ChangeLog
Source/WebKit2/WebProcess/cocoa/WebProcessCocoa.mm

index 5fefd92..4538433 100644 (file)
@@ -1,3 +1,16 @@
+2015-06-05  Chris Dumez  <cdumez@apple.com>
+
+        [WK2][iOS] Limit the number of vnodes used by the WebContent processes
+        https://bugs.webkit.org/show_bug.cgi?id=145672
+        <rdar://problem/21126637>
+
+        Reviewed by Antti Koivisto.
+
+        * wtf/RefCounter.h:
+        (WTF::RefCounter::value):
+
+        Expose the actual refcount instead of a boolean value.
+
 2015-06-05  Alex Christensen  <achristensen@webkit.org>
 
         [Web Timing] Use new SPI to enable data collection.
index e304e6b..bc8bb8c 100644 (file)
@@ -85,7 +85,7 @@ public:
         return Token<T>(m_count);
     }
 
-    bool value() const
+    unsigned value() const
     {
         return m_count->m_value;
     }
index 5a2dd7f..8db66dd 100644 (file)
@@ -1,3 +1,78 @@
+2015-06-05  Chris Dumez  <cdumez@apple.com>
+
+        [WK2][iOS] Limit the number of vnodes used by the WebContent processes
+        https://bugs.webkit.org/show_bug.cgi?id=145672
+        <rdar://problem/21126637>
+
+        Reviewed by Antti Koivisto.
+
+        Limit the number of vnodes used by the WebContent processes to reduce
+        the chance of getting killed due to the system running out of vnodes.
+        We sometimes see the WebContent process use up to 50% of the system's
+        vnode limit on some tests on iOS, which seems excessive. Most vnodes
+        are due to CachedResources which are mmap'd from the WebKit disk cache
+        and kept alive due to caches such as the Memory Cache / PageCache.
+
+        This patch adds tracking for the number of SharedBuffer objects that are
+        backed by a CFDataRef as this should track most of the vnodes used by
+        the process. The WebContent process registers a vnode pressure handler
+        upon initialization (likely it already does for memory pressure
+        handling). This handler gets called when the process uses over 15% of
+        of system's vnode limit and the "critical" flag is set if it uses over
+        20% of the system limit. The vnode pressure handler currently calls the
+        memory pressure handler as freeing our caches (especially PageCache,
+        MemoryCache) and doing a JS GC frees up vnodes.
+
+        On iPhone 6 Plus, the system limit is at 3000, which would lead to the
+        following limits being used:
+        soft: 450 / hard: 600
+
+        However, on older hardware, the system limit can be as low as 2000,
+        which would lead to the following limits being used:
+        soft: 300 / hard: 400
+
+        vnode tracking is currently only enabled on iOS because system vnode
+        limits are usually much higher on Mac (e.g. 473695 on Mac Pro, 9984
+        on a 2011 MacBook Air) and we normally don't get killed.
+
+        * WebCore.xcodeproj/project.pbxproj:
+
+        Add new files to the XCode project.
+
+        * platform/SharedBuffer.h:
+
+        Add a VNodeTracker::Token member next to the CFDataRef member that we
+        initialize if the SharedBuffer is constructed from a CFDataRef.
+
+        * platform/VNodeTracker.cpp: Added.
+        (WebCore::VNodeTracker::singleton):
+        (WebCore::VNodeTracker::VNodeTracker):
+        (WebCore::VNodeTracker::checkPressureState):
+        (WebCore::VNodeTracker::pressureWarningTimerFired):
+        (WebCore::VNodeTracker::nextPressureWarningInterval):
+        (WebCore::VNodeTracker::platformInitialize):
+        * platform/VNodeTracker.h: Added.
+        (WebCore::VNodeTracker::setPressureHandler):
+
+        Add a new VNodeTracker singleton to keep track the number of vnodes
+        used by the process since the system does not issue warnings before
+        reaching its vnode limit. Call sites should request Tokens for
+        every vnode they use for tracking purposes. Each process can then
+        set a vnode pressure handler function that will be called when the
+        process is over the designated limits.
+
+        * platform/cf/SharedBufferCF.cpp:
+        (WebCore::SharedBuffer::SharedBuffer):
+
+        Take a VNodeToken from the VNodeTracker when constructing the
+        SharedBuffer from a CFDataRef as these usually use mmap.
+
+        * platform/cocoa/VNodeTrackerCocoa.cpp: Added.
+        (WebCore::VNodeTracker::platformInitialize):
+
+        Get the system's vnode limit and uses 15% of that value as soft
+        limit for the process and 20% of that value as hard limit.
+
 2015-06-05  Dean Jackson  <dino@apple.com>
 
         Lingering checkbox animations when rapidly switching between views on WK1
index 2cfe53e..9cd1ff1 100644 (file)
     <ClCompile Include="..\platform\Timer.cpp" />
     <ClCompile Include="..\platform\UserActivity.cpp" />
     <ClCompile Include="..\platform\UUID.cpp" />
+    <ClCompile Include="..\platform\VNodeTracker.cpp" />
     <ClCompile Include="..\platform\Widget.cpp" />
     <ClCompile Include="..\platform\soup\SharedBufferSoup.cpp">
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
     <ClInclude Include="..\platform\ThreadTimers.h" />
     <ClInclude Include="..\platform\Timer.h" />
     <ClInclude Include="..\platform\UserActivity.h" />
+    <ClInclude Include="..\platform\VNodeTracker.h" />
     <ClInclude Include="..\platform\HysteresisActivity.h" />
     <ClInclude Include="..\platform\UUID.h" />
     <ClInclude Include="..\platform\Widget.h" />
index bbdae60..58a827d 100644 (file)
     <ClCompile Include="..\platform\UUID.cpp">
       <Filter>platform</Filter>
     </ClCompile>
+    <ClCompile Include="..\platform\VNodeTracker.cpp">
+      <Filter>platform</Filter>
+    </ClCompile>
     <ClCompile Include="..\platform\Widget.cpp">
       <Filter>platform</Filter>
     </ClCompile>
index c82dcc8..c726ebe 100644 (file)
                4689F1AF1267BAE100E8D380 /* FileMetadata.h in Headers */ = {isa = PBXBuildFile; fileRef = 4689F1AE1267BAE100E8D380 /* FileMetadata.h */; };
                46C83EFD1A9BBE2900A79A41 /* GeoNotifier.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 46C83EFB1A9BBE2900A79A41 /* GeoNotifier.cpp */; };
                46C83EFE1A9BBE2900A79A41 /* GeoNotifier.h in Headers */ = {isa = PBXBuildFile; fileRef = 46C83EFC1A9BBE2900A79A41 /* GeoNotifier.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               46DB7D571B20FE46005651B2 /* VNodeTrackerCocoa.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 46DB7D561B20FE3C005651B2 /* VNodeTrackerCocoa.cpp */; };
                46DBB6501AB8C96F00D9A813 /* PowerObserverMac.h in Headers */ = {isa = PBXBuildFile; fileRef = 46DBB64E1AB8C96F00D9A813 /* PowerObserverMac.h */; };
+               46F3E3F91B2109000087ED13 /* VNodeTracker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 46DB7D581B20FE58005651B2 /* VNodeTracker.cpp */; };
+               46F3E3FA1B2109100087ED13 /* VNodeTracker.h in Headers */ = {isa = PBXBuildFile; fileRef = 46DB7D591B20FE58005651B2 /* VNodeTracker.h */; settings = {ATTRIBUTES = (Private, ); }; };
                46FCB6181A70820E00C5A21E /* DiagnosticLoggingKeys.h in Headers */ = {isa = PBXBuildFile; fileRef = CD37B37515C1A7E1006DC898 /* DiagnosticLoggingKeys.h */; settings = {ATTRIBUTES = (Private, ); }; };
                490707E61219C04300D90E51 /* ANGLEWebKitBridge.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 490707E41219C04300D90E51 /* ANGLEWebKitBridge.cpp */; };
                490707E71219C04300D90E51 /* ANGLEWebKitBridge.h in Headers */ = {isa = PBXBuildFile; fileRef = 490707E51219C04300D90E51 /* ANGLEWebKitBridge.h */; };
                4689F1AE1267BAE100E8D380 /* FileMetadata.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FileMetadata.h; sourceTree = "<group>"; };
                46C83EFB1A9BBE2900A79A41 /* GeoNotifier.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GeoNotifier.cpp; sourceTree = "<group>"; };
                46C83EFC1A9BBE2900A79A41 /* GeoNotifier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GeoNotifier.h; sourceTree = "<group>"; };
+               46DB7D561B20FE3C005651B2 /* VNodeTrackerCocoa.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = VNodeTrackerCocoa.cpp; sourceTree = "<group>"; };
+               46DB7D581B20FE58005651B2 /* VNodeTracker.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = VNodeTracker.cpp; sourceTree = "<group>"; };
+               46DB7D591B20FE58005651B2 /* VNodeTracker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VNodeTracker.h; sourceTree = "<group>"; };
                46DBB64E1AB8C96F00D9A813 /* PowerObserverMac.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PowerObserverMac.h; sourceTree = "<group>"; };
                490707E41219C04300D90E51 /* ANGLEWebKitBridge.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ANGLEWebKitBridge.cpp; sourceTree = "<group>"; };
                490707E51219C04300D90E51 /* ANGLEWebKitBridge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ANGLEWebKitBridge.h; sourceTree = "<group>"; };
                                5D5975B119635F1100D00878 /* SystemVersion.h */,
                                5D5975B219635F1100D00878 /* SystemVersion.mm */,
                                7CC564B918BAC720001B9652 /* TelephoneNumberDetectorCocoa.cpp */,
+                               46DB7D561B20FE3C005651B2 /* VNodeTrackerCocoa.cpp */,
                        );
                        path = cocoa;
                        sourceTree = "<group>";
                                868160D2187669C40021E79D /* UserActivity.h */,
                                2E3BBF051162DA1100B9409A /* UUID.cpp */,
                                2E3BBF061162DA1100B9409A /* UUID.h */,
+                               46DB7D581B20FE58005651B2 /* VNodeTracker.cpp */,
+                               46DB7D591B20FE58005651B2 /* VNodeTracker.h */,
                                9A1142031832D134000BB8AD /* ValueToString.h */,
                                9380F47109A11AB4001FDB34 /* Widget.cpp */,
                                9380F47209A11AB4001FDB34 /* Widget.h */,
                                A74BB76B13BDA86300FF7BF0 /* ExceptionCodePlaceholder.h in Headers */,
                                148AFDA50AF58360008CC700 /* ExceptionHandlers.h in Headers */,
                                9767CE0B145ABC13005E64DB /* ExceptionHeaders.h in Headers */,
+                               46F3E3FA1B2109100087ED13 /* VNodeTracker.h in Headers */,
                                9767CE0C145ABC13005E64DB /* ExceptionInterfaces.h in Headers */,
                                6E67D2A91280E8BD008758F7 /* Extensions3D.h in Headers */,
                                6E67D2A71280E8A4008758F7 /* Extensions3DOpenGL.h in Headers */,
                                070584FF17F9F05E005F2BCB /* CapabilityRange.cpp in Sources */,
                                072CA86116CB4DC3008AE131 /* CaptionUserPreferences.cpp in Sources */,
                                079D086C162F21F900DB8658 /* CaptionUserPreferencesMediaAF.cpp in Sources */,
+                               46F3E3F91B2109000087ED13 /* VNodeTracker.cpp in Sources */,
                                99CC0B4E18BE9849006CEBCC /* CapturingInputCursor.cpp in Sources */,
                                CDC734141977896C0046BFC5 /* CARingBuffer.cpp in Sources */,
                                6550B69D099DF0270090D781 /* CDATASection.cpp in Sources */,
                                371F53EA0D2704F900ECE0D5 /* CSSUnicodeRangeValue.cpp in Sources */,
                                AD03AAFA1468455300A39B5B /* CSSValue.cpp in Sources */,
                                1ABA76CB11D20E57004C201C /* CSSValueKeywords.cpp in Sources */,
+                               46DB7D571B20FE46005651B2 /* VNodeTrackerCocoa.cpp in Sources */,
                                A80E6CE40A1989CA007FB8C5 /* CSSValueList.cpp in Sources */,
                                E49BDA0B131FD3E5003C56F0 /* CSSValuePool.cpp in Sources */,
                                E11AF15111B9A1A300805103 /* Cursor.cpp in Sources */,
index 052a2ae..4030bda 100644 (file)
@@ -36,6 +36,7 @@
 #include <wtf/text/WTFString.h>
 
 #if USE(CF)
+#include "VNodeTracker.h"
 #include <wtf/RetainPtr.h>
 #endif
 
@@ -167,6 +168,7 @@ private:
 #if USE(CF)
     explicit SharedBuffer(CFDataRef);
     RetainPtr<CFDataRef> m_cfData;
+    VNodeTracker::Token m_vnodeToken;
 #endif
 
 #if USE(SOUP)
diff --git a/Source/WebCore/platform/VNodeTracker.cpp b/Source/WebCore/platform/VNodeTracker.cpp
new file mode 100644 (file)
index 0000000..8b8dd97
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "VNodeTracker.h"
+
+#include "Logging.h"
+#include <wtf/NeverDestroyed.h>
+#include <wtf/RunLoop.h>
+
+namespace WebCore {
+
+VNodeTracker& VNodeTracker::singleton()
+{
+    static NeverDestroyed<VNodeTracker> vnodeTracker;
+    return vnodeTracker;
+}
+
+VNodeTracker::VNodeTracker()
+    : m_pressureWarningTimer(*this, &VNodeTracker::pressureWarningTimerFired)
+    , m_lastWarningTime(std::chrono::steady_clock::now())
+{
+    platformInitialize();
+
+    LOG(MemoryPressure, "Using following vnode limits for this process: soft=%u, hard=%u", m_softVNodeLimit, m_hardVNodeLimit);
+}
+
+void VNodeTracker::checkPressureState()
+{
+    ASSERT(m_pressureHandler);
+
+    if (m_vnodeCounter.value() <= m_softVNodeLimit)
+        return;
+
+    if (!m_pressureWarningTimer.isActive())
+        m_pressureWarningTimer.startOneShot(nextPressureWarningInterval());
+}
+
+void VNodeTracker::pressureWarningTimerFired()
+{
+    if (m_vnodeCounter.value() <= m_softVNodeLimit)
+        return;
+
+    m_lastWarningTime = std::chrono::steady_clock::now();
+    unsigned vnodeCount = m_vnodeCounter.value();
+    auto critical = vnodeCount > m_hardVNodeLimit ? Critical::Yes : Critical::No;
+    m_pressureHandler(critical);
+    LOG(MemoryPressure, "vnode pressure handler freed %d vnodes out of %u (critical pressure: %s)", vnodeCount - m_vnodeCounter.value(), vnodeCount, critical == Critical::Yes ? "Yes" : "No");
+}
+
+std::chrono::milliseconds VNodeTracker::nextPressureWarningInterval() const
+{
+    // We run the vnode pressure handler every 30 seconds at most.
+    static const auto minimumWarningInterval = std::chrono::seconds { 30 };
+    auto timeSinceLastWarning = std::chrono::steady_clock::now() - m_lastWarningTime;
+    if (timeSinceLastWarning < minimumWarningInterval)
+        return std::chrono::duration_cast<std::chrono::milliseconds>(minimumWarningInterval - timeSinceLastWarning);
+    return std::chrono::milliseconds { 0 };
+}
+
+#if !PLATFORM(COCOA)
+
+void VNodeTracker::platformInitialize()
+{
+}
+
+#endif
+
+} // namespace WebCore
diff --git a/Source/WebCore/platform/VNodeTracker.h b/Source/WebCore/platform/VNodeTracker.h
new file mode 100644 (file)
index 0000000..6d70631
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef VNodeTracker_h
+#define VNodeTracker_h
+
+#include "MemoryPressureHandler.h"
+#include "Timer.h"
+#include <chrono>
+#include <wtf/Forward.h>
+#include <wtf/RefCounter.h>
+
+namespace WebCore {
+
+class VNodeTracker {
+    friend class WTF::NeverDestroyed<VNodeTracker>;
+public:
+    using PressureHandler = std::function<void(Critical)>;
+
+    enum TokenType { };
+    using Token = RefCounter::Token<TokenType>;
+
+    WEBCORE_EXPORT static VNodeTracker& singleton();
+
+    void setPressureHandler(PressureHandler);
+    Token token();
+
+private:
+    VNodeTracker();
+
+    void checkPressureState();
+    void pressureWarningTimerFired();
+    std::chrono::milliseconds nextPressureWarningInterval() const;
+
+    void platformInitialize();
+
+    unsigned m_hardVNodeLimit { 400 };
+    unsigned m_softVNodeLimit { 300 };
+    PressureHandler m_pressureHandler;
+    RefCounter m_vnodeCounter;
+    Timer m_pressureWarningTimer;
+    std::chrono::time_point<std::chrono::steady_clock> m_lastWarningTime;
+};
+
+inline void VNodeTracker::setPressureHandler(PressureHandler handler)
+{
+    m_pressureHandler = handler;
+}
+
+inline auto VNodeTracker::token() -> Token
+{
+    if (!m_pressureHandler)
+        return Token();
+
+    Token token(m_vnodeCounter.token<TokenType>());
+    checkPressureState();
+    return token;
+}
+
+} // namespace WebCore
+
+#endif // VNodeTracker_h
index 650b6a8..2a75e56 100644 (file)
@@ -35,6 +35,7 @@ namespace WebCore {
 SharedBuffer::SharedBuffer(CFDataRef cfData)
     : m_buffer(adoptRef(new DataBuffer))
     , m_cfData(cfData)
+    , m_vnodeToken(VNodeTracker::singleton().token())
 {
 }
 
diff --git a/Source/WebCore/platform/cocoa/VNodeTrackerCocoa.cpp b/Source/WebCore/platform/cocoa/VNodeTrackerCocoa.cpp
new file mode 100644 (file)
index 0000000..ab0e2e0
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "VNodeTracker.h"
+
+#include "Logging.h"
+#include <algorithm>
+#include <sys/sysctl.h>
+#include <sys/types.h>
+
+namespace WebCore {
+
+void VNodeTracker::platformInitialize()
+{
+    unsigned systemMaxVNodes = 0;
+    size_t len = sizeof(systemMaxVNodes);
+
+    // Query the maximum number of vnodes on the system and use 15% of that value as soft limit for this process,
+    // and 20% of that value as hard limit.
+    if (sysctlbyname("kern.maxvnodes", &systemMaxVNodes, &len, nullptr, 0))
+        return;
+
+    LOG(MemoryPressure, "System vnode limit is %u", systemMaxVNodes);
+
+    m_softVNodeLimit = std::max(m_softVNodeLimit, static_cast<unsigned>(systemMaxVNodes * 0.15));
+    m_hardVNodeLimit = std::max(m_hardVNodeLimit, static_cast<unsigned>(systemMaxVNodes * 0.2));
+}
+
+} // namespace WebCore
index 9b6e11a..359b1dd 100644 (file)
@@ -1,3 +1,22 @@
+2015-06-05  Chris Dumez  <cdumez@apple.com>
+
+        [WK2][iOS] Limit the number of vnodes used by the WebContent processes
+        https://bugs.webkit.org/show_bug.cgi?id=145672
+        <rdar://problem/21126637>
+
+        Reviewed by Antti Koivisto.
+
+        Have the WebContent process register a vnode pressure handler on iOS,
+        which calls the memory pressure handler.
+
+        On non-critical pressure, it will prune dead resources from the memory
+        cache, which should free up some vnodes. On critical pressure, the
+        handler will clear the PageCache and do a JS GC, which should free even
+        more vnodes.
+
+        * WebProcess/cocoa/WebProcessCocoa.mm:
+        (WebKit::WebProcess::platformInitializeWebProcess):
+
 2015-06-05  Anders Carlsson  <andersca@apple.com>
 
         Fix refacto, don't put IndexedDB databases in the cache directory
index d3e6dd3..3b353df 100644 (file)
@@ -50,6 +50,7 @@
 #import <WebCore/MemoryCache.h>
 #import <WebCore/MemoryPressureHandler.h>
 #import <WebCore/PageCache.h>
+#import <WebCore/VNodeTracker.h>
 #import <WebCore/WebCoreNSURLExtras.h>
 #import <WebKitSystemInterface.h>
 #import <algorithm>
@@ -147,6 +148,15 @@ void WebProcess::platformInitializeWebProcess(WebProcessCreationParameters&& par
 
     MemoryPressureHandler::ReliefLogger::setLoggingEnabled(parameters.shouldEnableMemoryPressureReliefLogging);
 
+#if PLATFORM(IOS)
+    // Track the number of vnodes we are using on iOS and make sure we only use a
+    // reasonable amount because limits are fairly low on iOS devices and we can
+    // get killed when reaching the limit.
+    VNodeTracker::singleton().setPressureHandler([] (Critical critical) {
+        MemoryPressureHandler::singleton().releaseMemory(critical);
+    });
+#endif
+
     setEnhancedAccessibility(parameters.accessibilityEnhancedUserInterfaceEnabled);
 
 #if USE(APPKIT)