Title: [203216] trunk/Source/WebCore
Revision
203216
Author
[email protected]
Date
2016-07-13 23:36:25 -0700 (Wed, 13 Jul 2016)

Log Message

[GLib] Use a GSource instead of a thread to poll memory pressure eventFD in linux implementation
https://bugs.webkit.org/show_bug.cgi?id=159346

Reviewed by Antonio Gomes.

The eventFD file descriptor is pollable, so it would be much better to use a poll instead of a blocking read in
a secondary thread and then communicate back to the main thread. This is very easy to do with GSource in GLib,
so we could use that when GLib is available and keep the current implementation as a fallback.

* platform/MemoryPressureHandler.cpp:
(WebCore::m_holdOffTimer): Use a RunLoop timer.
* platform/MemoryPressureHandler.h:
* platform/linux/MemoryPressureHandlerLinux.cpp:
(WebCore::MemoryPressureHandler::EventFDPoller::EventFDPoller): Helper class do the eventFD polling.
(WebCore::MemoryPressureHandler::logErrorAndCloseFDs): Check if file descriptors are -1 not 0.
(WebCore::MemoryPressureHandler::install): Return early also if the hold off timer is active. Use EventFDPoller
to do the polling.
(WebCore::MemoryPressureHandler::uninstall): Stop the hold off timer and clear the EventFDPoller.

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (203215 => 203216)


--- trunk/Source/WebCore/ChangeLog	2016-07-14 06:34:08 UTC (rev 203215)
+++ trunk/Source/WebCore/ChangeLog	2016-07-14 06:36:25 UTC (rev 203216)
@@ -1,3 +1,24 @@
+2016-07-13  Carlos Garcia Campos  <[email protected]>
+
+        [GLib] Use a GSource instead of a thread to poll memory pressure eventFD in linux implementation
+        https://bugs.webkit.org/show_bug.cgi?id=159346
+
+        Reviewed by Antonio Gomes.
+
+        The eventFD file descriptor is pollable, so it would be much better to use a poll instead of a blocking read in
+        a secondary thread and then communicate back to the main thread. This is very easy to do with GSource in GLib,
+        so we could use that when GLib is available and keep the current implementation as a fallback.
+
+        * platform/MemoryPressureHandler.cpp:
+        (WebCore::m_holdOffTimer): Use a RunLoop timer.
+        * platform/MemoryPressureHandler.h:
+        * platform/linux/MemoryPressureHandlerLinux.cpp:
+        (WebCore::MemoryPressureHandler::EventFDPoller::EventFDPoller): Helper class do the eventFD polling.
+        (WebCore::MemoryPressureHandler::logErrorAndCloseFDs): Check if file descriptors are -1 not 0.
+        (WebCore::MemoryPressureHandler::install): Return early also if the hold off timer is active. Use EventFDPoller
+        to do the polling.
+        (WebCore::MemoryPressureHandler::uninstall): Stop the hold off timer and clear the EventFDPoller.
+
 2016-07-13  Benjamin Poulain  <[email protected]>
 
         [CSS][ARMv7] :nth-child() do not reserve enough registers if it is in backtracking chain

Modified: trunk/Source/WebCore/platform/MemoryPressureHandler.cpp (203215 => 203216)


--- trunk/Source/WebCore/platform/MemoryPressureHandler.cpp	2016-07-14 06:34:08 UTC (rev 203215)
+++ trunk/Source/WebCore/platform/MemoryPressureHandler.cpp	2016-07-14 06:36:25 UTC (rev 203216)
@@ -68,10 +68,7 @@
     , m_releaseMemoryBlock(0)
     , m_observer(0)
 #elif OS(LINUX)
-    , m_eventFD(0)
-    , m_pressureLevelFD(0)
-    , m_threadID(0)
-    , m_holdOffTimer(*this, &MemoryPressureHandler::holdOffTimerFired)
+    , m_holdOffTimer(RunLoop::main(), this, &MemoryPressureHandler::holdOffTimerFired)
 #endif
 {
     platformInitialize();

Modified: trunk/Source/WebCore/platform/MemoryPressureHandler.h (203215 => 203216)


--- trunk/Source/WebCore/platform/MemoryPressureHandler.h	2016-07-14 06:34:08 UTC (rev 203215)
+++ trunk/Source/WebCore/platform/MemoryPressureHandler.h	2016-07-14 06:36:25 UTC (rev 203216)
@@ -37,8 +37,11 @@
 #include <wtf/Lock.h>
 #include <wtf/ThreadingPrimitives.h>
 #elif OS(LINUX)
-#include "Timer.h"
+#include <wtf/RunLoop.h>
+#if USE(GLIB)
+#include <wtf/glib/GRefPtr.h>
 #endif
+#endif
 
 namespace WebCore {
 
@@ -81,8 +84,6 @@
     WEBCORE_EXPORT void clearMemoryPressure();
     WEBCORE_EXPORT bool shouldWaitForMemoryClearMessage();
     void respondToMemoryPressureIfNeeded();
-#elif OS(LINUX)
-    static void waitForMemoryPressureEvent(void*);
 #endif
 
     class ReliefLogger {
@@ -138,6 +139,26 @@
     void respondToMemoryPressure(Critical, Synchronous = Synchronous::No);
     void platformReleaseMemory(Critical);
 
+#if OS(LINUX)
+    class EventFDPoller {
+        WTF_MAKE_NONCOPYABLE(EventFDPoller); WTF_MAKE_FAST_ALLOCATED;
+    public:
+        EventFDPoller(int fd, std::function<void ()>&& notifyHandler);
+        ~EventFDPoller();
+
+    private:
+        void readAndNotify() const;
+
+        Optional<int> m_fd;
+        std::function<void ()> m_notifyHandler;
+#if USE(GLIB)
+        GRefPtr<GSource> m_source;
+#else
+        ThreadIdentifier m_threadID;
+#endif
+    };
+#endif
+
     bool m_installed;
     time_t m_lastRespondTime;
     LowMemoryHandler m_lowMemoryHandler;
@@ -152,10 +173,10 @@
     CFRunLoopObserverRef m_observer;
     Lock m_observerMutex;
 #elif OS(LINUX)
-    int m_eventFD;
-    int m_pressureLevelFD;
-    WTF::ThreadIdentifier m_threadID;
-    Timer m_holdOffTimer;
+    Optional<int> m_eventFD;
+    Optional<int> m_pressureLevelFD;
+    std::unique_ptr<EventFDPoller> m_eventFDPoller;
+    RunLoop::Timer<MemoryPressureHandler> m_holdOffTimer;
     void holdOffTimerFired();
     void logErrorAndCloseFDs(const char* error);
 #endif

Modified: trunk/Source/WebCore/platform/linux/MemoryPressureHandlerLinux.cpp (203215 => 203216)


--- trunk/Source/WebCore/platform/linux/MemoryPressureHandlerLinux.cpp	2016-07-14 06:34:08 UTC (rev 203215)
+++ trunk/Source/WebCore/platform/linux/MemoryPressureHandlerLinux.cpp	2016-07-14 06:36:25 UTC (rev 203216)
@@ -31,6 +31,7 @@
 
 #include "Logging.h"
 
+#include <errno.h>
 #include <fcntl.h>
 #include <malloc.h>
 #include <sys/eventfd.h>
@@ -41,6 +42,10 @@
 #include <wtf/MainThread.h>
 #include <wtf/text/WTFString.h>
 
+#if USE(GLIB)
+#include <glib-unix.h>
+#endif
+
 namespace WebCore {
 
 // Disable memory event reception for a minimum of s_minimumHoldOffTime
@@ -56,6 +61,102 @@
 static const char* s_cgroupEventControl = "/sys/fs/cgroup/memory/cgroup.event_control";
 static const char* s_processStatus = "/proc/self/status";
 
+#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, std::function<void ()>&& notifyHandler)
+    : m_fd(fd)
+    , m_notifyHandler(WTFMove(notifyHandler))
+{
+#if USE(GLIB)
+    m_source = adoptGRef(g_source_new(&eventFDSourceFunctions, sizeof(EventFDSource)));
+    g_source_set_name(m_source.get(), "WebCore: 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_threadID = createThread("WebCore: MemoryPressureHandler", [this] { readAndNotify(); }
+#endif
+}
+
+MemoryPressureHandler::EventFDPoller::~EventFDPoller()
+{
+    m_fd = Nullopt;
+#if USE(GLIB)
+    g_source_destroy(m_source.get());
+#else
+    detachThread(m_threadID);
+#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.");
+        return;
+    }
+
+    uint64_t buffer;
+    if (read(m_fd.value(), &buffer, sizeof(buffer)) == -1) {
+        if (isFatalReadError(errno)) {
+            LOG(MemoryPressure, "Failed to read eventfd.");
+            return;
+        }
+    }
+
+    m_notifyHandler();
+}
+
 static inline String nextToken(FILE* file)
 {
     if (!file)
@@ -77,67 +178,41 @@
     return String(buffer);
 }
 
-void MemoryPressureHandler::waitForMemoryPressureEvent(void*)
-{
-    ASSERT(!isMainThread());
-    int eventFD = MemoryPressureHandler::singleton().m_eventFD;
-    if (!eventFD) {
-        LOG(MemoryPressure, "Invalidate eventfd.");
-        return;
-    }
-
-    uint64_t buffer;
-    if (read(eventFD, &buffer, sizeof(buffer)) <= 0) {
-        LOG(MemoryPressure, "Failed to read eventfd.");
-        return;
-    }
-
-    // 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");
-
-    MemoryPressureHandler::singleton().setUnderMemoryPressure(critical);
-    callOnMainThread([critical] {
-        MemoryPressureHandler::singleton().respondToMemoryPressure(critical ? Critical::Yes : Critical::No);
-    });
-}
-
 inline void MemoryPressureHandler::logErrorAndCloseFDs(const char* log)
 {
     if (log)
         LOG(MemoryPressure, "%s, error : %m", log);
 
-    if (m_eventFD) {
-        close(m_eventFD);
-        m_eventFD = 0;
+    if (!m_eventFD) {
+        close(m_eventFD.value());
+        m_eventFD = Nullopt;
     }
-    if (m_pressureLevelFD) {
-        close(m_pressureLevelFD);
-        m_pressureLevelFD = 0;
+    if (!m_pressureLevelFD) {
+        close(m_pressureLevelFD.value());
+        m_pressureLevelFD = Nullopt;
     }
 }
 
 void MemoryPressureHandler::install()
 {
-    if (m_installed)
+    if (m_installed || m_holdOffTimer.isActive())
         return;
 
-    m_eventFD = eventfd(0, EFD_CLOEXEC);
-    if (m_eventFD == -1) {
+    int fd = eventfd(0, EFD_CLOEXEC);
+    if (fd == -1) {
         LOG(MemoryPressure, "eventfd() failed: %m");
         return;
     }
+    m_eventFD = fd;
 
-    m_pressureLevelFD = open(s_cgroupMemoryPressureLevel, O_CLOEXEC | O_RDONLY);
-    if (m_pressureLevelFD == -1) {
+    fd = open(s_cgroupMemoryPressureLevel, O_CLOEXEC | O_RDONLY);
+    if (fd == -1) {
         logErrorAndCloseFDs("Failed to open memory.pressure_level");
         return;
     }
+    m_pressureLevelFD = fd;
 
-    int fd = open(s_cgroupEventControl, O_CLOEXEC | O_WRONLY);
+    fd = open(s_cgroupEventControl, O_CLOEXEC | O_WRONLY);
     if (fd == -1) {
         logErrorAndCloseFDs("Failed to open cgroup.event_control");
         return;
@@ -144,7 +219,7 @@
     }
 
     char line[128] = {0, };
-    if (snprintf(line, sizeof(line), "%d %d low", m_eventFD, m_pressureLevelFD) < 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);
@@ -152,12 +227,21 @@
     }
     close(fd);
 
-    m_threadID = createThread(waitForMemoryPressureEvent, this, "WebCore: MemoryPressureHandler");
-    if (!m_threadID) {
-        logErrorAndCloseFDs("Failed to create a thread for MemoryPressureHandler");
-        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.");
 
@@ -170,10 +254,8 @@
     if (!m_installed)
         return;
 
-    if (m_threadID) {
-        detachThread(m_threadID);
-        m_threadID = 0;
-    }
+    m_holdOffTimer.stop();
+    m_eventFDPoller = nullptr;
 
     logErrorAndCloseFDs(nullptr);
     m_installed = false;
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to