Modified: trunk/Source/WebCore/platform/MemoryPressureHandler.cpp (177215 => 177216)
--- trunk/Source/WebCore/platform/MemoryPressureHandler.cpp 2014-12-12 09:17:21 UTC (rev 177215)
+++ trunk/Source/WebCore/platform/MemoryPressureHandler.cpp 2014-12-12 09:21:15 UTC (rev 177216)
@@ -64,6 +64,11 @@
, m_clearPressureOnMemoryRelease(true)
, m_releaseMemoryBlock(0)
, m_observer(0)
+#elif OS(LINUX)
+ , m_eventFD(0)
+ , m_pressureLevelFD(0)
+ , m_threadID(0)
+ , m_holdOffTimer(*this, &MemoryPressureHandler::holdOffTimerFired)
#endif
{
}
@@ -143,7 +148,7 @@
}
}
-#if !PLATFORM(COCOA)
+#if !PLATFORM(COCOA) && !OS(LINUX)
void MemoryPressureHandler::install() { }
void MemoryPressureHandler::uninstall() { }
void MemoryPressureHandler::holdOff(unsigned) { }
Modified: trunk/Source/WebCore/platform/MemoryPressureHandler.h (177215 => 177216)
--- trunk/Source/WebCore/platform/MemoryPressureHandler.h 2014-12-12 09:17:21 UTC (rev 177215)
+++ trunk/Source/WebCore/platform/MemoryPressureHandler.h 2014-12-12 09:21:15 UTC (rev 177216)
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2011 Apple Inc. All Rights Reserved.
+ * Copyright (C) 2014 Raspberry Pi Foundation. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -32,6 +33,8 @@
#if PLATFORM(IOS)
#include <wtf/ThreadingPrimitives.h>
+#elif OS(LINUX)
+#include "Timer.h"
#endif
namespace WebCore {
@@ -69,6 +72,8 @@
WEBCORE_EXPORT void clearMemoryPressure();
WEBCORE_EXPORT bool shouldWaitForMemoryClearMessage();
void respondToMemoryPressureIfNeeded();
+#elif OS(LINUX)
+ static void waitForMemoryPressureEvent(void*);
#endif
class ReliefLogger {
@@ -127,9 +132,16 @@
void (^m_releaseMemoryBlock)();
CFRunLoopObserverRef m_observer;
Mutex m_observerMutex;
+#elif OS(LINUX)
+ int m_eventFD;
+ int m_pressureLevelFD;
+ WTF::ThreadIdentifier m_threadID;
+ Timer m_holdOffTimer;
+ void holdOffTimerFired();
+ void logErrorAndCloseFDs(const char* error);
#endif
};
-
+
// Function to obtain the global memory pressure object.
WEBCORE_EXPORT MemoryPressureHandler& memoryPressureHandler();
Added: trunk/Source/WebCore/platform/linux/MemoryPressureHandlerLinux.cpp (0 => 177216)
--- trunk/Source/WebCore/platform/linux/MemoryPressureHandlerLinux.cpp (rev 0)
+++ trunk/Source/WebCore/platform/linux/MemoryPressureHandlerLinux.cpp 2014-12-12 09:21:15 UTC (rev 177216)
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2011, 2012 Apple Inc. All Rights Reserved.
+ * Copyright (C) 2014 Raspberry Pi Foundation. 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 "MemoryPressureHandler.h"
+
+#if OS(LINUX)
+
+#include "Logging.h"
+
+#include <fcntl.h>
+#include <malloc.h>
+#include <sys/eventfd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <wtf/CurrentTime.h>
+#include <wtf/Functional.h>
+#include <wtf/MainThread.h>
+#include <wtf/text/WTFString.h>
+
+namespace WebCore {
+
+// Disable memory event reception for a minimum of s_minimumHoldOffTime
+// seconds after receiving an event. Don't let events fire any sooner than
+// s_holdOffMultiplier times the last cleanup processing time. Effectively
+// this is 1 / s_holdOffMultiplier percent of the time.
+// These value seems reasonable and testing verifies that it throttles frequent
+// low memory events, greatly reducing CPU usage.
+static const unsigned s_minimumHoldOffTime = 5;
+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";
+static const char* s_processStatus = "/proc/self/status";
+
+static inline String nextToken(FILE* file)
+{
+ if (!file)
+ return String();
+
+ static const unsigned bufferSize = 128;
+ char buffer[bufferSize] = {0, };
+ unsigned index = 0;
+ while (index < bufferSize) {
+ char ch = fgetc(file);
+ if (ch == EOF || (isASCIISpace(ch) && index)) // Break on non-initial ASCII space.
+ break;
+ if (!isASCIISpace(ch)) {
+ buffer[index] = ch;
+ index++;
+ }
+ }
+
+ return String(buffer);
+}
+
+MemoryPressureHandler::~MemoryPressureHandler()
+{
+ uninstall();
+}
+
+void MemoryPressureHandler::waitForMemoryPressureEvent(void*)
+{
+ ASSERT(!isMainThread());
+ int eventFD = memoryPressureHandler().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;
+ }
+
+ memoryPressureHandler().m_underMemoryPressure = true;
+ callOnMainThread(bind(&MemoryPressureHandler::respondToMemoryPressure, &memoryPressureHandler(), true));
+}
+
+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_pressureLevelFD) {
+ close(m_pressureLevelFD);
+ m_pressureLevelFD = 0;
+ }
+}
+
+void MemoryPressureHandler::install()
+{
+ if (m_installed)
+ return;
+
+ m_eventFD = eventfd(0, EFD_CLOEXEC);
+ if (m_eventFD == -1) {
+ LOG(MemoryPressure, "eventfd() failed: %m");
+ return;
+ }
+
+ m_pressureLevelFD = open(s_cgroupMemoryPressureLevel, O_CLOEXEC | O_RDONLY);
+ if (m_pressureLevelFD == -1) {
+ logErrorAndCloseFDs("Failed to open memory.pressure_level");
+ return;
+ }
+
+ int fd = open(s_cgroupEventControl, O_CLOEXEC | O_WRONLY);
+ if (fd == -1) {
+ logErrorAndCloseFDs("Failed to open cgroup.event_control");
+ return;
+ }
+
+ char line[128] = {0, };
+ if (snprintf(line, sizeof(line), "%d %d low", m_eventFD, m_pressureLevelFD) < 0
+ || write(fd, line, strlen(line) + 1) < 0) {
+ logErrorAndCloseFDs("Failed to write cgroup.event_control");
+ close(fd);
+ return;
+ }
+ close(fd);
+
+ m_threadID = createThread(waitForMemoryPressureEvent, this, "WebCore: MemoryPressureHandler");
+ if (!m_threadID) {
+ logErrorAndCloseFDs("Failed to create a thread for MemoryPressureHandler");
+ return;
+ }
+
+ m_underMemoryPressure = false;
+ m_installed = true;
+}
+
+void MemoryPressureHandler::uninstall()
+{
+ if (!m_installed)
+ return;
+
+ if (m_threadID) {
+ detachThread(m_threadID);
+ m_threadID = 0;
+ }
+
+ logErrorAndCloseFDs(nullptr);
+ m_installed = false;
+}
+
+void MemoryPressureHandler::holdOffTimerFired()
+{
+ install();
+}
+
+void MemoryPressureHandler::holdOff(unsigned seconds)
+{
+ m_holdOffTimer.startOneShot(seconds);
+}
+
+void MemoryPressureHandler::respondToMemoryPressure(bool critical)
+{
+ uninstall();
+
+ double startTime = monotonicallyIncreasingTime();
+ m_lowMemoryHandler(critical);
+ unsigned holdOffTime = (monotonicallyIncreasingTime() - startTime) * s_holdOffMultiplier;
+ holdOff(std::max(holdOffTime, s_minimumHoldOffTime));
+}
+
+void MemoryPressureHandler::platformReleaseMemory(bool)
+{
+ ReliefLogger log("Run malloc_trim");
+ malloc_trim(0);
+}
+
+void MemoryPressureHandler::ReliefLogger::platformLog()
+{
+ size_t currentMemory = platformMemoryUsage();
+ if (currentMemory == static_cast<size_t>(-1) || m_initialMemory == static_cast<size_t>(-1)) {
+ LOG(MemoryPressure, "%s (Unable to get dirty memory information for process)", m_logString);
+ return;
+ }
+
+ ssize_t memoryDiff = currentMemory - m_initialMemory;
+ if (memoryDiff < 0)
+ LOG(MemoryPressure, "Pressure relief: %s: -dirty %ld bytes (from %ld to %ld)", m_logString, (memoryDiff * -1), m_initialMemory, currentMemory);
+ else if (memoryDiff > 0)
+ LOG(MemoryPressure, "Pressure relief: %s: +dirty %ld bytes (from %ld to %ld)", m_logString, memoryDiff, m_initialMemory, currentMemory);
+ else
+ LOG(MemoryPressure, "Pressure relief: %s: =dirty (at %ld bytes)", m_logString, currentMemory);
+}
+
+size_t MemoryPressureHandler::ReliefLogger::platformMemoryUsage()
+{
+ FILE* file = fopen(s_processStatus, "r");
+ if (!file)
+ return static_cast<size_t>(-1);
+
+ size_t vmSize = static_cast<size_t>(-1); // KB
+ String token = nextToken(file);
+ while (!token.isEmpty()) {
+ if (token == "VmSize:") {
+ vmSize = nextToken(file).toInt() * KB;
+ break;
+ }
+ token = nextToken(file);
+ }
+ fclose(file);
+
+ return vmSize;
+}
+
+} // namespace WebCore
+
+#endif // OS(LINUX)