[GTK][WPE] Memory pressure monitor doesn't reliable notify all the subprocesses
authorclopez@igalia.com <clopez@igalia.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 25 May 2018 00:52:13 +0000 (00:52 +0000)
committerclopez@igalia.com <clopez@igalia.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 25 May 2018 00:52:13 +0000 (00:52 +0000)
https://bugs.webkit.org/show_bug.cgi?id=184261

Reviewed by Carlos Garcia Campos.

Source/WebKit:

On Linux we had two implementations for getting notifications about memory pressure events:
- The memory cgroup (called systemd here).
- The UIProcess memory monitor (which delivered events via a shared eventfd)

The problem with the first is that it was usually not working on a standard machine due to
the special permissions or configurations required for memory cgroups, so the second one
(eventfd) was used as a fall-back in that case.
But this eventfd method is racy with more than one WebKit child process and it wasn't
reliably delivering the notifications.

This patch removes the memory cgroup implementation and modifies the UIProcess memory monitor
to deliver the events via WebKit IPC. This simplifies the code a lot and allows us to have
only one implementation that should work in any Linux machine.

The implementation now also triggers the event with information about the criticalness of it.

Previously a critical event was triggered always at a 95% of pressure.
Now a non-critical one is triggered at 90% and critical remains at a 95%.

Start triggering events early should compensate the fact than triggering the event via WebKit IPC is
a bit slower than doing that via an eventfd (or than listening on the memory cgroup event controller).

The events are delivered to all WebKit childs: WebProcess, NetworkProcess, StorageProcess, PluginProcess.

In the case of the StorageProcess a dummy controller is installed, which currently does nothing,
but leaves a note for a future implementation and at least allows to trigger platformReleaseMemory()
that on Linux/glibc should end calling malloc_trim()

* NetworkProcess/NetworkProcess.cpp:
(WebKit::NetworkProcess::initializeNetworkProcess):
* NetworkProcess/NetworkProcessCreationParameters.cpp:
(WebKit::NetworkProcessCreationParameters::encode const):
(WebKit::NetworkProcessCreationParameters::decode):
* NetworkProcess/NetworkProcessCreationParameters.h:
* PluginProcess/PluginProcess.cpp:
(WebKit::PluginProcess::didReceiveMessage):
(WebKit::PluginProcess::initializePluginProcess):
* Shared/ChildProcess.cpp:
(WebKit::ChildProcess::didReceiveMemoryPressureEvent):
* Shared/ChildProcess.h:
* Shared/ChildProcess.messages.in:
* Shared/Plugins/PluginProcessCreationParameters.cpp:
(WebKit::PluginProcessCreationParameters::encode const):
(WebKit::PluginProcessCreationParameters::decode):
* Shared/Plugins/PluginProcessCreationParameters.h:
* Shared/WebProcessCreationParameters.cpp:
(WebKit::WebProcessCreationParameters::encode const):
(WebKit::WebProcessCreationParameters::decode):
* Shared/WebProcessCreationParameters.h:
* StorageProcess/StorageProcess.cpp:
(WebKit::StorageProcess::initializeProcess):
* UIProcess/Plugins/PluginProcessManager.cpp:
(WebKit::PluginProcessManager::sendMemoryPressureEvent):
* UIProcess/Plugins/PluginProcessManager.h:
* UIProcess/Plugins/PluginProcessProxy.cpp:
(WebKit::PluginProcessProxy::sendMemoryPressureEvent):
(WebKit::PluginProcessProxy::didFinishLaunching):
* UIProcess/Plugins/PluginProcessProxy.h:
* UIProcess/WebProcessPool.cpp:
(WebKit::WebProcessPool::sendMemoryPressureEvent):
(WebKit::WebProcessPool::ensureNetworkProcess):
(WebKit::WebProcessPool::initializeNewWebProcess):
* UIProcess/WebProcessPool.h:
(WebKit::WebProcessPool::sendToStorageProcess):
* UIProcess/linux/MemoryPressureMonitor.cpp:
(WebKit::pollIntervalForUsedMemoryPercentage): Fix equation for calculating the interval percentage.
(WebKit::MemoryPressureMonitor::singleton):
(WebKit::MemoryPressureMonitor::start):
* UIProcess/linux/MemoryPressureMonitor.h:
* WebProcess/WebProcess.cpp:
(WebKit::WebProcess::initializeWebProcess):

Source/WTF:

Receive the memory pressure notifications from the UIProcess memory monitor via WebKit IPC.

* wtf/MemoryPressureHandler.h:
* wtf/linux/MemoryPressureHandlerLinux.cpp:
(WTF::MemoryPressureHandler::triggerMemoryPressureEvent):
(WTF::MemoryPressureHandler::install):
(WTF::MemoryPressureHandler::uninstall):

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

25 files changed:
Source/WTF/ChangeLog
Source/WTF/wtf/MemoryPressureHandler.h
Source/WTF/wtf/linux/MemoryPressureHandlerLinux.cpp
Source/WebKit/ChangeLog
Source/WebKit/NetworkProcess/NetworkProcess.cpp
Source/WebKit/NetworkProcess/NetworkProcessCreationParameters.cpp
Source/WebKit/NetworkProcess/NetworkProcessCreationParameters.h
Source/WebKit/PluginProcess/PluginProcess.cpp
Source/WebKit/Shared/ChildProcess.cpp
Source/WebKit/Shared/ChildProcess.h
Source/WebKit/Shared/ChildProcess.messages.in
Source/WebKit/Shared/Plugins/PluginProcessCreationParameters.cpp
Source/WebKit/Shared/Plugins/PluginProcessCreationParameters.h
Source/WebKit/Shared/WebProcessCreationParameters.cpp
Source/WebKit/Shared/WebProcessCreationParameters.h
Source/WebKit/StorageProcess/StorageProcess.cpp
Source/WebKit/UIProcess/Plugins/PluginProcessManager.cpp
Source/WebKit/UIProcess/Plugins/PluginProcessManager.h
Source/WebKit/UIProcess/Plugins/PluginProcessProxy.cpp
Source/WebKit/UIProcess/Plugins/PluginProcessProxy.h
Source/WebKit/UIProcess/WebProcessPool.cpp
Source/WebKit/UIProcess/WebProcessPool.h
Source/WebKit/UIProcess/linux/MemoryPressureMonitor.cpp
Source/WebKit/UIProcess/linux/MemoryPressureMonitor.h
Source/WebKit/WebProcess/WebProcess.cpp

index d71e917..7c8ddfc 100644 (file)
@@ -1,3 +1,18 @@
+2018-05-24  Carlos Alberto Lopez Perez  <clopez@igalia.com>
+
+        [GTK][WPE] Memory pressure monitor doesn't reliable notify all the subprocesses
+        https://bugs.webkit.org/show_bug.cgi?id=184261
+
+        Reviewed by Carlos Garcia Campos.
+
+        Receive the memory pressure notifications from the UIProcess memory monitor via WebKit IPC.
+
+        * wtf/MemoryPressureHandler.h:
+        * wtf/linux/MemoryPressureHandlerLinux.cpp:
+        (WTF::MemoryPressureHandler::triggerMemoryPressureEvent):
+        (WTF::MemoryPressureHandler::install):
+        (WTF::MemoryPressureHandler::uninstall):
+
 2018-05-24  Jiewen Tan  <jiewen_tan@apple.com>
 
         Adopt SecKeyProxy SPI in certificate based challenge response code
index 5d26356..0039d05 100644 (file)
@@ -69,6 +69,10 @@ public:
 
     WTF_EXPORT_PRIVATE void setShouldUsePeriodicMemoryMonitor(bool);
 
+#if OS(LINUX)
+    WTF_EXPORT_PRIVATE void triggerMemoryPressureEvent(bool isCritical);
+#endif
+
     void setMemoryKillCallback(WTF::Function<void()>&& function) { m_memoryKillCallback = WTFMove(function); }
     void setMemoryPressureStatusChangedCallback(WTF::Function<void(bool)>&& function) { m_memoryPressureStatusChangedCallback = WTFMove(function); }
     void setDidExceedInactiveLimitWhileActiveCallback(WTF::Function<void()>&& function) { m_didExceedInactiveLimitWhileActiveCallback = WTFMove(function); }
@@ -88,10 +92,6 @@ public:
     }
     void setUnderMemoryPressure(bool);
 
-#if OS(LINUX)
-    void setMemoryPressureMonitorHandle(int fd);
-#endif
-
     class ReliefLogger {
     public:
         explicit ReliefLogger(const char *log)
@@ -169,26 +169,6 @@ private:
     void doesExceedInactiveLimitWhileActive();
     void doesNotExceedInactiveLimitWhileActive();
 
-#if OS(LINUX)
-    class EventFDPoller {
-        WTF_MAKE_NONCOPYABLE(EventFDPoller); WTF_MAKE_FAST_ALLOCATED;
-    public:
-        EventFDPoller(int fd, WTF::Function<void ()>&& notifyHandler);
-        ~EventFDPoller();
-
-    private:
-        void readAndNotify() const;
-
-        std::optional<int> m_fd;
-        WTF::Function<void ()> m_notifyHandler;
-#if USE(GLIB)
-        GRefPtr<GSource> m_source;
-#else
-        RefPtr<Thread> m_thread;
-#endif
-    };
-#endif
-
     WebsamProcessState m_processState { WebsamProcessState::Inactive };
 
     unsigned m_pageCount { 0 };
@@ -213,13 +193,8 @@ private:
 #endif
 
 #if OS(LINUX)
-    std::optional<int> m_eventFD;
-    std::optional<int> m_pressureLevelFD;
-    std::unique_ptr<EventFDPoller> m_eventFDPoller;
     RunLoop::Timer<MemoryPressureHandler> m_holdOffTimer;
     void holdOffTimerFired();
-    void logErrorAndCloseFDs(const char* error);
-    bool tryEnsureEventFD();
 #endif
 };
 
index 8e85f58..14f4998 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * Copyright (C) 2011, 2012 Apple Inc. All Rights Reserved.
  * Copyright (C) 2014 Raspberry Pi Foundation. All Rights Reserved.
+ * Copyright (C) 2018 Igalia S.L.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
 
 #if OS(LINUX)
 
-#include <errno.h>
-#include <fcntl.h>
 #include <malloc.h>
-#include <sys/eventfd.h>
-#include <sys/stat.h>
-#include <sys/types.h>
 #include <unistd.h>
 #include <wtf/MainThread.h>
 #include <wtf/MemoryFootprint.h>
 #include <wtf/linux/CurrentProcessMemoryStatus.h>
 #include <wtf/text/WTFString.h>
 
-#if USE(GLIB)
-#include <glib-unix.h>
-#include <wtf/glib/RunLoopSourcePriority.h>
-#endif
-
 #define LOG_CHANNEL_PREFIX Log
 
 namespace WTF {
@@ -63,160 +54,27 @@ static const Seconds s_maximumHoldOffTime { 30_s };
 static const size_t s_minimumBytesFreedToUseMinimumHoldOffTime = 1 * MB;
 static const unsigned s_holdOffMultiplier = 20;
 
-static const char* s_cgroupMemoryPressureLevel = "/sys/fs/cgroup/memory/memory.pressure_level";
-static const char* s_cgroupEventControl = "/sys/fs/cgroup/memory/cgroup.event_control";
-
-#if USE(GLIB)
-typedef struct {
-    GSource source;
-    gpointer fdTag;
-    GIOCondition condition;
-} EventFDSource;
-
-static const unsigned eventFDSourceCondition = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
-
-static GSourceFuncs eventFDSourceFunctions = {
-    nullptr, // prepare
-    nullptr, // check
-    // dispatch
-    [](GSource* source, GSourceFunc callback, gpointer userData) -> gboolean
-    {
-        EventFDSource* eventFDSource = reinterpret_cast<EventFDSource*>(source);
-        unsigned events = g_source_query_unix_fd(source, eventFDSource->fdTag) & eventFDSourceCondition;
-        if (events & G_IO_HUP || events & G_IO_ERR || events & G_IO_NVAL)
-            return G_SOURCE_REMOVE;
-
-        gboolean returnValue = G_SOURCE_CONTINUE;
-        if (events & G_IO_IN)
-            returnValue = callback(userData);
-        g_source_set_ready_time(source, -1);
-        return returnValue;
-    },
-    nullptr, // finalize
-    nullptr, // closure_callback
-    nullptr, // closure_marshall
-};
-#endif
-
-MemoryPressureHandler::EventFDPoller::EventFDPoller(int fd, WTF::Function<void ()>&& notifyHandler)
-    : m_fd(fd)
-    , m_notifyHandler(WTFMove(notifyHandler))
+void MemoryPressureHandler::triggerMemoryPressureEvent(bool isCritical)
 {
-#if USE(GLIB)
-    m_source = adoptGRef(g_source_new(&eventFDSourceFunctions, sizeof(EventFDSource)));
-    g_source_set_priority(m_source.get(), RunLoopSourcePriority::MemoryPressureHandlerTimer);
-    g_source_set_name(m_source.get(), "WTF: MemoryPressureHandler");
-    if (!g_unix_set_fd_nonblocking(m_fd.value(), TRUE, nullptr)) {
-        LOG(MemoryPressure, "Failed to set eventfd nonblocking");
-        return;
-    }
-
-    EventFDSource* eventFDSource = reinterpret_cast<EventFDSource*>(m_source.get());
-    eventFDSource->fdTag = g_source_add_unix_fd(m_source.get(), m_fd.value(), static_cast<GIOCondition>(eventFDSourceCondition));
-    g_source_set_callback(m_source.get(), [](gpointer userData) -> gboolean {
-        static_cast<EventFDPoller*>(userData)->readAndNotify();
-        return G_SOURCE_REMOVE;
-    }, this, nullptr);
-    g_source_attach(m_source.get(), nullptr);
-#else
-    m_thread = Thread::create("WTF: MemoryPressureHandler",
-        [this] {
-            readAndNotify();
-        });
-#endif
-}
-
-MemoryPressureHandler::EventFDPoller::~EventFDPoller()
-{
-    m_fd = std::nullopt;
-#if USE(GLIB)
-    g_source_destroy(m_source.get());
-#else
-    m_thread->detach();
-#endif
-}
-
-static inline bool isFatalReadError(int error)
-{
-#if USE(GLIB)
-    // We don't really need to read the buffer contents, if the poller
-    // notified us, but read would block or is no longer available, is
-    // enough to trigger the memory pressure handler.
-    return error != EAGAIN && error != EWOULDBLOCK;
-#else
-    return true;
-#endif
-}
-
-void MemoryPressureHandler::EventFDPoller::readAndNotify() const
-{
-    if (!m_fd) {
-        LOG(MemoryPressure, "Invalidate eventfd.");
+    if (!m_installed)
         return;
-    }
 
-    uint64_t buffer;
-    if (read(m_fd.value(), &buffer, sizeof(buffer)) == -1) {
-        if (isFatalReadError(errno)) {
-            LOG(MemoryPressure, "Failed to read eventfd.");
-            return;
-        }
-    }
+    if (ReliefLogger::loggingEnabled())
+        LOG(MemoryPressure, "Got memory pressure notification (%s)", isCritical ? "critical" : "non-critical");
 
-    m_notifyHandler();
-}
+    setUnderMemoryPressure(true);
 
-inline void MemoryPressureHandler::logErrorAndCloseFDs(const char* log)
-{
-    if (log)
-        LOG(MemoryPressure, "%s, error : %m", log);
-
-    if (m_eventFD) {
-        close(m_eventFD.value());
-        m_eventFD = std::nullopt;
-    }
-    if (m_pressureLevelFD) {
-        close(m_pressureLevelFD.value());
-        m_pressureLevelFD = std::nullopt;
-    }
-}
-
-bool MemoryPressureHandler::tryEnsureEventFD()
-{
-    if (m_eventFD)
-        return true;
-
-    // Try to use cgroups instead.
-    int fd = eventfd(0, EFD_CLOEXEC);
-    if (fd == -1) {
-        LOG(MemoryPressure, "eventfd() failed: %m");
-        return false;
-    }
-    m_eventFD = fd;
-
-    fd = open(s_cgroupMemoryPressureLevel, O_CLOEXEC | O_RDONLY);
-    if (fd == -1) {
-        logErrorAndCloseFDs("Failed to open memory.pressure_level");
-        return false;
-    }
-    m_pressureLevelFD = fd;
-
-    fd = open(s_cgroupEventControl, O_CLOEXEC | O_WRONLY);
-    if (fd == -1) {
-        logErrorAndCloseFDs("Failed to open cgroup.event_control");
-        return false;
-    }
+    if (isMainThread())
+        respondToMemoryPressure(isCritical ? Critical::Yes : Critical::No);
+    else
+        RunLoop::main().dispatch([this, isCritical] {
+            respondToMemoryPressure(isCritical ? Critical::Yes : Critical::No);
+        });
 
-    char line[128] = {0, };
-    if (snprintf(line, sizeof(line), "%d %d low", m_eventFD.value(), m_pressureLevelFD.value()) < 0
-        || write(fd, line, strlen(line) + 1) < 0) {
-        logErrorAndCloseFDs("Failed to write cgroup.event_control");
-        close(fd);
-        return false;
-    }
-    close(fd);
+    if (ReliefLogger::loggingEnabled() && isUnderMemoryPressure())
+        LOG(MemoryPressure, "System is no longer under memory pressure.");
 
-    return true;
+    setUnderMemoryPressure(false);
 }
 
 void MemoryPressureHandler::install()
@@ -224,28 +82,6 @@ void MemoryPressureHandler::install()
     if (m_installed || m_holdOffTimer.isActive())
         return;
 
-    if (!tryEnsureEventFD())
-        return;
-
-    m_eventFDPoller = std::make_unique<EventFDPoller>(m_eventFD.value(), [this] {
-        // FIXME: Current memcg does not provide any way for users to know how serious the memory pressure is.
-        // So we assume all notifications from memcg are critical for now. If memcg had better inferfaces
-        // to get a detailed memory pressure level in the future, we should update here accordingly.
-        bool critical = true;
-        if (ReliefLogger::loggingEnabled())
-            LOG(MemoryPressure, "Got memory pressure notification (%s)", critical ? "critical" : "non-critical");
-
-        setUnderMemoryPressure(critical);
-        if (isMainThread())
-            respondToMemoryPressure(critical ? Critical::Yes : Critical::No);
-        else
-            RunLoop::main().dispatch([this, critical] { respondToMemoryPressure(critical ? Critical::Yes : Critical::No); });
-    });
-
-    if (ReliefLogger::loggingEnabled() && isUnderMemoryPressure())
-        LOG(MemoryPressure, "System is no longer under memory pressure.");
-
-    setUnderMemoryPressure(false);
     m_installed = true;
 }
 
@@ -255,18 +91,6 @@ void MemoryPressureHandler::uninstall()
         return;
 
     m_holdOffTimer.stop();
-    m_eventFDPoller = nullptr;
-
-    if (m_pressureLevelFD) {
-        close(m_pressureLevelFD.value());
-        m_pressureLevelFD = std::nullopt;
-
-        // Only close the eventFD used for cgroups.
-        if (m_eventFD) {
-            close(m_eventFD.value());
-            m_eventFD = std::nullopt;
-        }
-    }
 
     m_installed = false;
 }
@@ -318,11 +142,6 @@ std::optional<MemoryPressureHandler::ReliefLogger::MemoryUsage> MemoryPressureHa
     return MemoryUsage {processMemoryUsage(), physical};
 }
 
-void MemoryPressureHandler::setMemoryPressureMonitorHandle(int fd)
-{
-    ASSERT(!m_eventFD);
-    m_eventFD = fd;
-}
 
 } // namespace WTF
 
index 7c6852f..41df37a 100644 (file)
@@ -1,3 +1,82 @@
+2018-05-24  Carlos Alberto Lopez Perez  <clopez@igalia.com>
+
+        [GTK][WPE] Memory pressure monitor doesn't reliable notify all the subprocesses
+        https://bugs.webkit.org/show_bug.cgi?id=184261
+
+        Reviewed by Carlos Garcia Campos.
+
+        On Linux we had two implementations for getting notifications about memory pressure events:
+        - The memory cgroup (called systemd here).
+        - The UIProcess memory monitor (which delivered events via a shared eventfd)
+
+        The problem with the first is that it was usually not working on a standard machine due to
+        the special permissions or configurations required for memory cgroups, so the second one
+        (eventfd) was used as a fall-back in that case.
+        But this eventfd method is racy with more than one WebKit child process and it wasn't
+        reliably delivering the notifications.
+
+        This patch removes the memory cgroup implementation and modifies the UIProcess memory monitor
+        to deliver the events via WebKit IPC. This simplifies the code a lot and allows us to have
+        only one implementation that should work in any Linux machine.
+
+        The implementation now also triggers the event with information about the criticalness of it.
+
+        Previously a critical event was triggered always at a 95% of pressure.
+        Now a non-critical one is triggered at 90% and critical remains at a 95%.
+
+        Start triggering events early should compensate the fact than triggering the event via WebKit IPC is
+        a bit slower than doing that via an eventfd (or than listening on the memory cgroup event controller).
+
+        The events are delivered to all WebKit childs: WebProcess, NetworkProcess, StorageProcess, PluginProcess.
+
+        In the case of the StorageProcess a dummy controller is installed, which currently does nothing,
+        but leaves a note for a future implementation and at least allows to trigger platformReleaseMemory()
+        that on Linux/glibc should end calling malloc_trim()
+
+        * NetworkProcess/NetworkProcess.cpp:
+        (WebKit::NetworkProcess::initializeNetworkProcess):
+        * NetworkProcess/NetworkProcessCreationParameters.cpp:
+        (WebKit::NetworkProcessCreationParameters::encode const):
+        (WebKit::NetworkProcessCreationParameters::decode):
+        * NetworkProcess/NetworkProcessCreationParameters.h:
+        * PluginProcess/PluginProcess.cpp:
+        (WebKit::PluginProcess::didReceiveMessage):
+        (WebKit::PluginProcess::initializePluginProcess):
+        * Shared/ChildProcess.cpp:
+        (WebKit::ChildProcess::didReceiveMemoryPressureEvent):
+        * Shared/ChildProcess.h:
+        * Shared/ChildProcess.messages.in:
+        * Shared/Plugins/PluginProcessCreationParameters.cpp:
+        (WebKit::PluginProcessCreationParameters::encode const):
+        (WebKit::PluginProcessCreationParameters::decode):
+        * Shared/Plugins/PluginProcessCreationParameters.h:
+        * Shared/WebProcessCreationParameters.cpp:
+        (WebKit::WebProcessCreationParameters::encode const):
+        (WebKit::WebProcessCreationParameters::decode):
+        * Shared/WebProcessCreationParameters.h:
+        * StorageProcess/StorageProcess.cpp:
+        (WebKit::StorageProcess::initializeProcess):
+        * UIProcess/Plugins/PluginProcessManager.cpp:
+        (WebKit::PluginProcessManager::sendMemoryPressureEvent):
+        * UIProcess/Plugins/PluginProcessManager.h:
+        * UIProcess/Plugins/PluginProcessProxy.cpp:
+        (WebKit::PluginProcessProxy::sendMemoryPressureEvent):
+        (WebKit::PluginProcessProxy::didFinishLaunching):
+        * UIProcess/Plugins/PluginProcessProxy.h:
+        * UIProcess/WebProcessPool.cpp:
+        (WebKit::WebProcessPool::sendMemoryPressureEvent):
+        (WebKit::WebProcessPool::ensureNetworkProcess):
+        (WebKit::WebProcessPool::initializeNewWebProcess):
+        * UIProcess/WebProcessPool.h:
+        (WebKit::WebProcessPool::sendToStorageProcess):
+        * UIProcess/linux/MemoryPressureMonitor.cpp:
+        (WebKit::pollIntervalForUsedMemoryPercentage): Fix equation for calculating the interval percentage.
+        (WebKit::MemoryPressureMonitor::singleton):
+        (WebKit::MemoryPressureMonitor::start):
+        * UIProcess/linux/MemoryPressureMonitor.h:
+        * WebProcess/WebProcess.cpp:
+        (WebKit::WebProcess::initializeWebProcess):
+
 2018-05-24  Youenn Fablet  <youenn@apple.com>
 
         Update plugin search path to look for user installed plugins
index 7f0b5f8..a4bc45e 100644 (file)
@@ -232,10 +232,6 @@ void NetworkProcess::initializeNetworkProcess(NetworkProcessCreationParameters&&
     m_loadThrottleLatency = parameters.loadThrottleLatency;
     if (!m_suppressMemoryPressureHandler) {
         auto& memoryPressureHandler = MemoryPressureHandler::singleton();
-#if OS(LINUX)
-        if (parameters.memoryPressureMonitorHandle.fileDescriptor() != -1)
-            memoryPressureHandler.setMemoryPressureMonitorHandle(parameters.memoryPressureMonitorHandle.releaseFileDescriptor());
-#endif
         memoryPressureHandler.setLowMemoryHandler([this] (Critical critical, Synchronous) {
             lowMemoryHandler(critical);
         });
index d61cff8..f855293 100644 (file)
@@ -100,9 +100,6 @@ void NetworkProcessCreationParameters::encode(IPC::Encoder& encoder) const
 #if HAVE(CFNETWORK_STORAGE_PARTITIONING) && !RELEASE_LOG_DISABLED
     encoder << logCookieInformation;
 #endif
-#if OS(LINUX)
-    encoder << memoryPressureMonitorHandle;
-#endif
 #if ENABLE(NETWORK_CAPTURE)
     encoder << recordReplayMode;
     encoder << recordReplayCacheLocation;
@@ -254,11 +251,6 @@ bool NetworkProcessCreationParameters::decode(IPC::Decoder& decoder, NetworkProc
         return false;
 #endif
 
-#if OS(LINUX)
-    if (!decoder.decode(result.memoryPressureMonitorHandle))
-        return false;
-#endif
-
 #if ENABLE(NETWORK_CAPTURE)
     if (!decoder.decode(result.recordReplayMode))
         return false;
index 25bc08f..5d0fe13 100644 (file)
@@ -25,7 +25,6 @@
 
 #pragma once
 
-#include "Attachment.h"
 #include "CacheModel.h"
 #include "NetworkSessionCreationParameters.h"
 #include "SandboxExtension.h"
@@ -116,10 +115,6 @@ struct NetworkProcessCreationParameters {
     bool logCookieInformation { false };
 #endif
 
-#if OS(LINUX)
-    IPC::Attachment memoryPressureMonitorHandle;
-#endif
-
 #if ENABLE(NETWORK_CAPTURE)
     String recordReplayMode;
     String recordReplayCacheLocation;
index 1cd3075..5421386 100644 (file)
@@ -30,6 +30,7 @@
 
 #include "ArgumentCoders.h"
 #include "Attachment.h"
+#include "ChildProcessMessages.h"
 #include "NetscapePlugin.h"
 #include "NetscapePluginModule.h"
 #include "PluginProcessConnectionMessages.h"
@@ -116,6 +117,13 @@ bool PluginProcess::shouldTerminate()
 
 void PluginProcess::didReceiveMessage(IPC::Connection& connection, IPC::Decoder& decoder)
 {
+#if OS(LINUX)
+    if (decoder.messageReceiverName() == Messages::ChildProcess::messageReceiverName()) {
+        ChildProcess::didReceiveMessage(connection, decoder);
+        return;
+    }
+#endif
+
     didReceivePluginProcessMessage(connection, decoder);
 }
 
@@ -124,10 +132,6 @@ void PluginProcess::initializePluginProcess(PluginProcessCreationParameters&& pa
     ASSERT(!m_pluginModule);
 
     auto& memoryPressureHandler = MemoryPressureHandler::singleton();
-#if OS(LINUX)
-    if (parameters.memoryPressureMonitorHandle.fileDescriptor() != -1)
-        memoryPressureHandler.setMemoryPressureMonitorHandle(parameters.memoryPressureMonitorHandle.releaseFileDescriptor());
-#endif
     memoryPressureHandler.setLowMemoryHandler([this] (Critical, Synchronous) {
         if (shouldTerminate())
             terminate();
index 0b2cab7..4534243 100644 (file)
 #include <unistd.h>
 #endif
 
+#if OS(LINUX)
+#include <wtf/MemoryPressureHandler.h>
+#endif
+
 using namespace WebCore;
 
 namespace WebKit {
@@ -215,6 +219,14 @@ void ChildProcess::didReceiveInvalidMessage(IPC::Connection&, IPC::StringReferen
     WTFLogAlways("Received invalid message: '%s::%s'", messageReceiverName.toString().data(), messageName.toString().data());
     CRASH();
 }
+
+#if OS(LINUX)
+void ChildProcess::didReceiveMemoryPressureEvent(bool isCritical)
+{
+    MemoryPressureHandler::singleton().triggerMemoryPressureEvent(isCritical);
+}
 #endif
 
+#endif // !PLATFORM(COCOA)
+
 } // namespace WebKit
index bfd16ea..b35c9f2 100644 (file)
@@ -107,6 +107,9 @@ protected:
     void didReceiveMessage(IPC::Connection&, IPC::Decoder&) override;
 
     void registerURLSchemeServiceWorkersCanHandle(const String&) const;
+#if OS(LINUX)
+    void didReceiveMemoryPressureEvent(bool isCritical);
+#endif
 
 private:
     // IPC::MessageSender
index 1dd1b1a..18cbcd8 100644 (file)
@@ -24,4 +24,8 @@ messages -> ChildProcess {
     ShutDown()
     RegisterURLSchemeServiceWorkersCanHandle(String scheme)
     SetProcessSuppressionEnabled(bool flag)
+
+#if OS(LINUX)
+    void DidReceiveMemoryPressureEvent(bool isCritical)
+#endif
 }
index 7bd78b6..5e44035 100644 (file)
@@ -49,9 +49,6 @@ void PluginProcessCreationParameters::encode(IPC::Encoder& encoder) const
     encoder << acceleratedCompositingPort;
     IPC::encode(encoder, networkATSContext.get());
 #endif
-#if OS(LINUX)
-    encoder << memoryPressureMonitorHandle;
-#endif
 }
 
 bool PluginProcessCreationParameters::decode(IPC::Decoder& decoder, PluginProcessCreationParameters& result)
@@ -70,10 +67,6 @@ bool PluginProcessCreationParameters::decode(IPC::Decoder& decoder, PluginProces
     if (!IPC::decode(decoder, result.networkATSContext))
         return false;
 #endif
-#if OS(LINUX)
-    if (!decoder.decode(result.memoryPressureMonitorHandle))
-        return false;
-#endif
 
     return true;
 }
index 186cb3b..d326880 100644 (file)
@@ -27,7 +27,6 @@
 
 #if ENABLE(NETSCAPE_PLUGIN_API)
 
-#include "Attachment.h"
 #include "PluginProcessAttributes.h"
 #include <wtf/Seconds.h>
 
@@ -58,9 +57,6 @@ struct PluginProcessCreationParameters {
     WTF::MachSendRight acceleratedCompositingPort;
     RetainPtr<CFDataRef> networkATSContext;
 #endif
-#if OS(LINUX)
-    IPC::Attachment memoryPressureMonitorHandle;
-#endif
 };
 
 } // namespace WebKit
index 801e610..483d47c 100644 (file)
@@ -139,10 +139,6 @@ void WebProcessCreationParameters::encode(IPC::Encoder& encoder) const
     IPC::encode(encoder, networkATSContext.get());
 #endif
 
-#if OS(LINUX)
-    encoder << memoryPressureMonitorHandle;
-#endif
-
 #if PLATFORM(WAYLAND)
     encoder << waylandCompositorDisplayName;
 #endif
@@ -385,11 +381,6 @@ bool WebProcessCreationParameters::decode(IPC::Decoder& decoder, WebProcessCreat
         return false;
 #endif
 
-#if OS(LINUX)
-    if (!decoder.decode(parameters.memoryPressureMonitorHandle))
-        return false;
-#endif
-
 #if PLATFORM(WAYLAND)
     if (!decoder.decode(parameters.waylandCompositorDisplayName))
         return false;
index e250237..6e1cce9 100644 (file)
@@ -173,10 +173,6 @@ struct WebProcessCreationParameters {
     RetainPtr<CFDataRef> networkATSContext;
 #endif
 
-#if OS(LINUX)
-    IPC::Attachment memoryPressureMonitorHandle;
-#endif
-
 #if PLATFORM(WAYLAND)
     String waylandCompositorDisplayName;
 #endif
index 6a98d1a..5782a2a 100644 (file)
@@ -49,6 +49,8 @@
 #include <wtf/CallbackAggregator.h>
 #include <wtf/CrossThreadTask.h>
 #include <wtf/MainThread.h>
+#include <wtf/MemoryPressureHandler.h>
+
 
 #if ENABLE(SERVICE_WORKER)
 #include "WebSWServerToContextConnectionMessages.h"
@@ -555,6 +557,14 @@ void StorageProcess::disableServiceWorkerProcessTerminationDelay()
 #if !PLATFORM(COCOA)
 void StorageProcess::initializeProcess(const ChildProcessInitializationParameters&)
 {
+#if OS(LINUX)
+    auto& memoryPressureHandler = MemoryPressureHandler::singleton();
+    memoryPressureHandler.setLowMemoryHandler([this] (Critical, Synchronous) {
+        // FIXME: no lowMemoryHandler() implemented for StorageProcess currently.
+        // But at least define this setLowMemoryHandler() empty so platformReleaseMemory is called.
+    });
+    memoryPressureHandler.install();
+#endif
 }
 
 void StorageProcess::initializeProcessName(const ChildProcessInitializationParameters&)
@@ -564,6 +574,6 @@ void StorageProcess::initializeProcessName(const ChildProcessInitializationParam
 void StorageProcess::initializeSandbox(const ChildProcessInitializationParameters&, SandboxInitializationParameters&)
 {
 }
-#endif
+#endif // !PLATFORM(COCOA)
 
 } // namespace WebKit
index 139fa5a..57662d9 100644 (file)
@@ -131,6 +131,14 @@ PluginProcessProxy* PluginProcessManager::getPluginProcess(uint64_t pluginProces
     return nullptr;
 }
 
+#if OS(LINUX)
+void PluginProcessManager::sendMemoryPressureEvent(bool isCritical)
+{
+    for (auto& pluginProcess : m_pluginProcesses)
+        pluginProcess->sendMemoryPressureEvent(isCritical);
+}
+#endif
+
 PluginProcessProxy* PluginProcessManager::getOrCreatePluginProcess(uint64_t pluginProcessToken)
 {
     if (auto existingProcess = getPluginProcess(pluginProcessToken))
index 5dcdd2c..a5c3fbe 100644 (file)
@@ -64,6 +64,10 @@ public:
     void deleteWebsiteData(const PluginModuleInfo&, WallTime modifiedSince, WTF::Function<void ()>&& completionHandler);
     void deleteWebsiteDataForHostNames(const PluginModuleInfo&, const Vector<String>& hostNames, WTF::Function<void ()>&& completionHandler);
 
+#if OS(LINUX)
+    void sendMemoryPressureEvent(bool isCritical);
+#endif
+
 #if PLATFORM(COCOA)
     inline ProcessSuppressionDisabledToken processSuppressionDisabledToken();
     inline bool processSuppressionDisabled() const;
index 4d0ddf8..7a39f5b 100644 (file)
@@ -28,6 +28,7 @@
 
 #if ENABLE(NETSCAPE_PLUGIN_API)
 
+#include "ChildProcessMessages.h"
 #include "PluginProcessConnectionManagerMessages.h"
 #include "PluginProcessCreationParameters.h"
 #include "PluginProcessManager.h"
 #include <WebCore/NotImplemented.h>
 #include <wtf/RunLoop.h>
 
-#if OS(LINUX)
-#include "MemoryPressureMonitor.h"
-#endif
-
 using namespace WebCore;
 
 namespace WebKit {
@@ -155,6 +152,16 @@ void PluginProcessProxy::deleteWebsiteDataForHostNames(const Vector<String>& hos
     m_connection->send(Messages::PluginProcess::DeleteWebsiteDataForHostNames(hostNames, callbackID), 0);
 }
 
+#if OS(LINUX)
+void PluginProcessProxy::sendMemoryPressureEvent(bool isCritical)
+{
+    if (state() == State::Launching)
+        return;
+
+    m_connection->send(Messages::ChildProcess::DidReceiveMemoryPressureEvent(isCritical), 0);
+}
+#endif
+
 void PluginProcessProxy::pluginProcessCrashedOrFailedToLaunch()
 {
     // The plug-in process must have crashed or exited, send any pending sync replies we might have.
@@ -234,11 +241,6 @@ void PluginProcessProxy::didFinishLaunching(ProcessLauncher*, IPC::Connection::I
         parameters.terminationTimeout = shutdownTimeout;
     }
 
-#if OS(LINUX)
-    if (MemoryPressureMonitor::isEnabled())
-        parameters.memoryPressureMonitorHandle = MemoryPressureMonitor::singleton().createHandle();
-#endif
-
     platformInitializePluginProcess(parameters);
 
     // Initialize the plug-in host process.
index 69c153d..9e86b85 100644 (file)
@@ -82,6 +82,10 @@ public:
     void deleteWebsiteData(WallTime modifiedSince, WTF::Function<void ()>&& completionHandler);
     void deleteWebsiteDataForHostNames(const Vector<String>& hostNames, WTF::Function<void ()>&& completionHandler);
 
+#if OS(LINUX)
+    void sendMemoryPressureEvent(bool isCritical);
+#endif
+
     bool isValid() const { return m_connection; }
 
 #if PLUGIN_ARCHITECTURE(UNIX)
index 12a6ddf..bf3bc98 100644 (file)
@@ -49,6 +49,7 @@
 #include "NetworkProcessMessages.h"
 #include "NetworkProcessProxy.h"
 #include "PerActivityStateCPUUsageSampler.h"
+#include "PluginProcessManager.h"
 #include "SandboxExtension.h"
 #include "ServiceWorkerProcessProxy.h"
 #include "StatisticsData.h"
@@ -269,6 +270,10 @@ WebProcessPool::WebProcessPool(API::ProcessPoolConfiguration& configuration)
 
     platformInitialize();
 
+#if OS(LINUX)
+    MemoryPressureMonitor::singleton().start();
+#endif
+
     addMessageReceiver(Messages::WebProcessPool::messageReceiverName(), *this);
 
     // NOTE: These sub-objects must be initialized after m_messageReceiverMap..
@@ -413,6 +418,18 @@ void WebProcessPool::fullKeyboardAccessModeChanged(bool fullKeyboardAccessEnable
     sendToAllProcesses(Messages::WebProcess::FullKeyboardAccessModeChanged(fullKeyboardAccessEnabled));
 }
 
+#if OS(LINUX)
+void WebProcessPool::sendMemoryPressureEvent(bool isCritical)
+{
+    sendToAllProcesses(Messages::ChildProcess::DidReceiveMemoryPressureEvent(isCritical));
+    sendToNetworkingProcess(Messages::ChildProcess::DidReceiveMemoryPressureEvent(isCritical));
+    sendToStorageProcess(Messages::ChildProcess::DidReceiveMemoryPressureEvent(isCritical));
+#if ENABLE(NETSCAPE_PLUGIN_API)
+    PluginProcessManager::singleton().sendMemoryPressureEvent(isCritical);
+#endif
+}
+#endif
+
 void WebProcessPool::textCheckerStateChanged()
 {
     sendToAllProcesses(Messages::WebProcess::SetTextCheckerState(TextChecker::state()));
@@ -490,11 +507,6 @@ NetworkProcessProxy& WebProcessPool::ensureNetworkProcess(WebsiteDataStore* with
         SandboxExtension::createHandle(parentBundleDirectory, SandboxExtension::Type::ReadOnly, parameters.parentBundleDirectoryExtensionHandle);
 #endif
 
-#if OS(LINUX)
-    if (MemoryPressureMonitor::isEnabled())
-        parameters.memoryPressureMonitorHandle = MemoryPressureMonitor::singleton().createHandle();
-#endif
-
     parameters.shouldUseTestingNetworkSession = m_shouldUseTestingNetworkSession;
     parameters.presentingApplicationPID = m_configuration->presentingApplicationPID();
 
@@ -923,8 +935,6 @@ void WebProcessPool::initializeNewWebProcess(WebProcessProxy& process, WebsiteDa
 
 #if OS(LINUX)
     parameters.shouldEnableMemoryPressureReliefLogging = true;
-    if (MemoryPressureMonitor::isEnabled())
-        parameters.memoryPressureMonitorHandle = MemoryPressureMonitor::singleton().createHandle();
 #endif
 
 #if PLATFORM(WAYLAND) && USE(EGL)
index b5b5e4a..21d71f5 100644 (file)
@@ -165,6 +165,7 @@ public:
     template<typename T> void sendToNetworkingProcessRelaunchingIfNecessary(T&& message);
 
     // Sends the message to WebProcess or StorageProcess as approporiate for current process model.
+    template<typename T> void sendToStorageProcess(T&& message);
     template<typename T> void sendToStorageProcessRelaunchingIfNecessary(T&& message);
 
     void processDidFinishLaunching(WebProcessProxy*);
@@ -313,7 +314,9 @@ public:
 #endif
 
     void fullKeyboardAccessModeChanged(bool fullKeyboardAccessEnabled);
-
+#if OS(LINUX)
+    void sendMemoryPressureEvent(bool isCritical);
+#endif
     void textCheckerStateChanged();
 
     Ref<API::Dictionary> plugInAutoStartOriginHashes() const;
@@ -714,6 +717,13 @@ void WebProcessPool::sendToNetworkingProcessRelaunchingIfNecessary(T&& message)
 }
 
 template<typename T>
+void WebProcessPool::sendToStorageProcess(T&& message)
+{
+    if (m_storageProcess && m_storageProcess->canSendMessage())
+        m_storageProcess->send(std::forward<T>(message), 0);
+}
+
+template<typename T>
 void WebProcessPool::sendToStorageProcessRelaunchingIfNecessary(T&& message)
 {
     ensureStorageProcessAndWebsiteDataStore(nullptr);
index 357d993..9ed4b73 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 Igalia S.L.
+ * Copyright (C) 2016, 2018 Igalia S.L.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
 
 #if OS(LINUX)
 
-#include "Attachment.h"
-#include <errno.h>
-#include <fcntl.h>
-#include <mutex>
+#include "WebProcessPool.h"
+#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <sys/eventfd.h>
-#include <sys/stat.h>
-#include <sys/types.h>
 #include <unistd.h>
 #include <wtf/Threading.h>
 #include <wtf/UniStdExtras.h>
@@ -48,8 +43,9 @@ static const size_t notSet = static_cast<size_t>(-1);
 static const Seconds s_minPollingInterval { 1_s };
 static const Seconds s_maxPollingInterval { 5_s };
 static const double s_minUsedMemoryPercentageForPolling = 50;
-static const double s_maxUsedMemoryPercentageForPolling = 90;
-static const int s_memoryPresurePercentageThreshold = 95;
+static const double s_maxUsedMemoryPercentageForPolling = 85;
+static const int s_memoryPresurePercentageThreshold = 90;
+static const int s_memoryPresurePercentageThresholdCritical = 95;
 
 static size_t lowWatermarkPages()
 {
@@ -208,45 +204,22 @@ static inline Seconds pollIntervalForUsedMemoryPercentage(int usedPercentage)
         return s_minPollingInterval;
 
     return s_minPollingInterval + (s_maxPollingInterval - s_minPollingInterval) *
-        ((usedPercentage - s_minUsedMemoryPercentageForPolling) / (s_maxUsedMemoryPercentageForPolling - s_minUsedMemoryPercentageForPolling));
-}
-
-static bool isSystemdMemoryPressureMonitorAvailable()
-{
-    int fd = open("/sys/fs/cgroup/memory/memory.pressure_level", O_CLOEXEC | O_RDONLY);
-    if (fd == -1)
-        return false;
-    close(fd);
-
-    fd = open("/sys/fs/cgroup/memory/cgroup.event_control", O_CLOEXEC | O_WRONLY);
-    if (fd == -1)
-        return false;
-    close(fd);
-
-    return true;
-}
-
-bool MemoryPressureMonitor::isEnabled()
-{
-    static std::once_flag onceFlag;
-    static bool enabled;
-    std::call_once(onceFlag, [] { enabled = !isSystemdMemoryPressureMonitorAvailable(); });
-    return enabled;
+        ((s_maxUsedMemoryPercentageForPolling - usedPercentage) / (s_maxUsedMemoryPercentageForPolling - s_minUsedMemoryPercentageForPolling));
 }
 
 MemoryPressureMonitor& MemoryPressureMonitor::singleton()
 {
-    ASSERT(isEnabled());
     static NeverDestroyed<MemoryPressureMonitor> memoryMonitor;
     return memoryMonitor;
 }
 
-MemoryPressureMonitor::MemoryPressureMonitor()
-    : m_eventFD(eventfd(0, EFD_CLOEXEC))
+void MemoryPressureMonitor::start()
 {
-    if (m_eventFD == -1)
+    if (m_started)
         return;
 
+    m_started = true;
+
     Thread::create("MemoryPressureMonitor", [this] {
         Seconds pollInterval = s_maxPollingInterval;
         while (true) {
@@ -259,26 +232,15 @@ MemoryPressureMonitor::MemoryPressureMonitor()
             }
 
             if (usedPercentage >= s_memoryPresurePercentageThreshold) {
-                uint64_t fdEvent = 1;
-                ssize_t bytesWritten = write(m_eventFD, &fdEvent, sizeof(uint64_t));
-                if (bytesWritten != sizeof(uint64_t)) {
-                    WTFLogAlways("Error writing to MemoryPressureMonitor eventFD: %s", strerror(errno));
-                    break;
-                }
+                bool isCritical = (usedPercentage >= s_memoryPresurePercentageThresholdCritical);
+                for (auto* processPool : WebProcessPool::allProcessPools())
+                    processPool->sendMemoryPressureEvent(isCritical);
             }
             pollInterval = pollIntervalForUsedMemoryPercentage(usedPercentage);
         }
-        close(m_eventFD);
     })->detach();
 }
 
-IPC::Attachment MemoryPressureMonitor::createHandle() const
-{
-    int duplicatedHandle = dupCloseOnExec(m_eventFD);
-    if (duplicatedHandle == -1)
-        return { };
-    return IPC::Attachment(duplicatedHandle);
-}
 
 } // namespace WebKit
 
index e250c85..64c1d7d 100644 (file)
@@ -41,16 +41,13 @@ class MemoryPressureMonitor {
     friend NeverDestroyed<MemoryPressureMonitor>;
 public:
     static MemoryPressureMonitor& singleton();
-    static bool isEnabled();
+    void start();
 
     ~MemoryPressureMonitor();
 
-    IPC::Attachment createHandle() const;
-
 private:
-    MemoryPressureMonitor();
-
-    int m_eventFD { -1 };
+    MemoryPressureMonitor() = default;
+    bool m_started { false };
 };
 
 } // namespace WebKit
index 0e83704..50800d0 100644 (file)
@@ -270,8 +270,6 @@ void WebProcess::initializeWebProcess(WebProcessCreationParameters&& parameters)
     WebCore::setPresentingApplicationPID(parameters.presentingApplicationPID);
 
 #if OS(LINUX)
-    if (parameters.memoryPressureMonitorHandle.fileDescriptor() != -1)
-        MemoryPressureHandler::singleton().setMemoryPressureMonitorHandle(parameters.memoryPressureMonitorHandle.releaseFileDescriptor());
     MemoryPressureHandler::ReliefLogger::setLoggingEnabled(parameters.shouldEnableMemoryPressureReliefLogging);
 #endif