/* * Copyright (C) 2011-2017 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. */ #pragma once #include #include #include #include #include #include #if OS(WINDOWS) #include #endif #if PLATFORM(COCOA) #include #endif namespace WTF { enum class MemoryUsagePolicy : uint8_t { Unrestricted, // Allocate as much as you want Conservative, // Maybe you don't cache every single thing Strict, // Time to start pinching pennies for real }; enum class WebsamProcessState : uint8_t { Active, Inactive, }; enum class Critical : uint8_t { No, Yes }; enum class Synchronous : uint8_t { No, Yes }; typedef WTF::Function LowMemoryHandler; class MemoryPressureHandler { WTF_MAKE_FAST_ALLOCATED; friend class WTF::LazyNeverDestroyed; public: WTF_EXPORT_PRIVATE static MemoryPressureHandler& singleton(); WTF_EXPORT_PRIVATE void install(); WTF_EXPORT_PRIVATE void setShouldUsePeriodicMemoryMonitor(bool); #if OS(LINUX) || OS(FREEBSD) || OS(HAIKU) WTF_EXPORT_PRIVATE void triggerMemoryPressureEvent(bool isCritical); #endif void setMemoryKillCallback(WTF::Function&& function) { m_memoryKillCallback = WTFMove(function); } void setMemoryPressureStatusChangedCallback(WTF::Function&& function) { m_memoryPressureStatusChangedCallback = WTFMove(function); } void setDidExceedInactiveLimitWhileActiveCallback(WTF::Function&& function) { m_didExceedInactiveLimitWhileActiveCallback = WTFMove(function); } void setLowMemoryHandler(LowMemoryHandler&& handler) { m_lowMemoryHandler = WTFMove(handler); } bool isUnderMemoryPressure() const { return m_underMemoryPressure #if PLATFORM(MAC) || m_memoryUsagePolicy >= MemoryUsagePolicy::Strict #endif || m_isSimulatingMemoryPressure; } bool isSimulatingMemoryPressure() const { return m_isSimulatingMemoryPressure; } void setUnderMemoryPressure(bool); WTF_EXPORT_PRIVATE MemoryUsagePolicy currentMemoryUsagePolicy(); #if PLATFORM(COCOA) WTF_EXPORT_PRIVATE void setDispatchQueue(OSObjectPtr&&); #endif class ReliefLogger { WTF_MAKE_FAST_ALLOCATED; public: explicit ReliefLogger(const char *log) : m_logString(log) , m_initialMemory(loggingEnabled() ? platformMemoryUsage() : MemoryUsage { }) { } ~ReliefLogger() { if (loggingEnabled()) logMemoryUsageChange(); } const char* logString() const { return m_logString; } static void setLoggingEnabled(bool enabled) { s_loggingEnabled = enabled; } static bool loggingEnabled() { #if RELEASE_LOG_DISABLED return s_loggingEnabled; #else return true; #endif } private: struct MemoryUsage { WTF_MAKE_STRUCT_FAST_ALLOCATED; MemoryUsage() = default; MemoryUsage(size_t resident, size_t physical) : resident(resident) , physical(physical) { } size_t resident { 0 }; size_t physical { 0 }; }; std::optional platformMemoryUsage(); void logMemoryUsageChange(); const char* m_logString; std::optional m_initialMemory; WTF_EXPORT_PRIVATE static bool s_loggingEnabled; }; struct Configuration { WTF_MAKE_STRUCT_FAST_ALLOCATED; WTF_EXPORT_PRIVATE Configuration(); WTF_EXPORT_PRIVATE Configuration(size_t, double, double, std::optional, Seconds); template void encode(Encoder& encoder) const { encoder << baseThreshold; encoder << conservativeThresholdFraction; encoder << strictThresholdFraction; encoder << killThresholdFraction; encoder << pollInterval; } template static std::optional decode(Decoder& decoder) { std::optional baseThreshold; decoder >> baseThreshold; if (!baseThreshold) return std::nullopt; std::optional conservativeThresholdFraction; decoder >> conservativeThresholdFraction; if (!conservativeThresholdFraction) return std::nullopt; std::optional strictThresholdFraction; decoder >> strictThresholdFraction; if (!strictThresholdFraction) return std::nullopt; std::optional> killThresholdFraction; decoder >> killThresholdFraction; if (!killThresholdFraction) return std::nullopt; std::optional pollInterval; decoder >> pollInterval; if (!pollInterval) return std::nullopt; return {{ *baseThreshold, *conservativeThresholdFraction, *strictThresholdFraction, *killThresholdFraction, *pollInterval }}; } size_t baseThreshold; double conservativeThresholdFraction; double strictThresholdFraction; std::optional killThresholdFraction; Seconds pollInterval; }; void setConfiguration(Configuration&& configuration) { m_configuration = WTFMove(configuration); } void setConfiguration(const Configuration& configuration) { m_configuration = configuration; } WTF_EXPORT_PRIVATE void releaseMemory(Critical, Synchronous = Synchronous::No); WTF_EXPORT_PRIVATE void beginSimulatedMemoryPressure(); WTF_EXPORT_PRIVATE void endSimulatedMemoryPressure(); WTF_EXPORT_PRIVATE void setProcessState(WebsamProcessState); WebsamProcessState processState() const { return m_processState; } WTF_EXPORT_PRIVATE static void setPageCount(unsigned); void setShouldLogMemoryMemoryPressureEvents(bool shouldLog) { m_shouldLogMemoryMemoryPressureEvents = shouldLog; } private: std::optional thresholdForMemoryKill(); size_t thresholdForPolicy(MemoryUsagePolicy); MemoryUsagePolicy policyForFootprint(size_t); void memoryPressureStatusChanged(); void uninstall(); void holdOff(Seconds); MemoryPressureHandler(); ~MemoryPressureHandler() = delete; void respondToMemoryPressure(Critical, Synchronous = Synchronous::No); void platformReleaseMemory(Critical); void platformInitialize(); void measurementTimerFired(); void shrinkOrDie(size_t killThreshold); void setMemoryUsagePolicyBasedOnFootprint(size_t); void doesExceedInactiveLimitWhileActive(); void doesNotExceedInactiveLimitWhileActive(); unsigned m_pageCount { 0 }; std::atomic m_underMemoryPressure { false }; bool m_installed { false }; bool m_isSimulatingMemoryPressure { false }; bool m_shouldLogMemoryMemoryPressureEvents { true }; bool m_hasInvokedDidExceedInactiveLimitWhileActiveCallback { false }; WebsamProcessState m_processState { WebsamProcessState::Inactive }; MemoryUsagePolicy m_memoryUsagePolicy { MemoryUsagePolicy::Unrestricted }; std::unique_ptr> m_measurementTimer; WTF::Function m_memoryKillCallback; WTF::Function m_memoryPressureStatusChangedCallback; WTF::Function m_didExceedInactiveLimitWhileActiveCallback; LowMemoryHandler m_lowMemoryHandler; Configuration m_configuration; #if OS(WINDOWS) void windowsMeasurementTimerFired(); RunLoop::Timer m_windowsMeasurementTimer; Win32Handle m_lowMemoryHandle; #endif #if OS(LINUX) || OS(FREEBSD) || OS(HAIKU) RunLoop::Timer m_holdOffTimer; void holdOffTimerFired(); #endif #if PLATFORM(COCOA) OSObjectPtr m_dispatchQueue; #endif }; } // namespace WTF using WTF::Critical; using WTF::MemoryPressureHandler; using WTF::Synchronous; using WTF::WebsamProcessState;