| /* |
| * 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 <atomic> |
| #include <ctime> |
| #include <wtf/FastMalloc.h> |
| #include <wtf/Forward.h> |
| #include <wtf/Function.h> |
| #include <wtf/RunLoop.h> |
| |
| #if OS(WINDOWS) |
| #include <wtf/win/Win32Handle.h> |
| #endif |
| |
| #if PLATFORM(COCOA) |
| #include <wtf/OSObjectPtr.h> |
| #endif |
| |
| namespace WTF { |
| |
| enum class MemoryPressureStatus : uint8_t { |
| Normal, |
| |
| // The entire system is at a warning or critical pressure level. |
| SystemWarning, |
| SystemCritical, |
| |
| // This specific process crossed a warning or critical memory usage limit. |
| ProcessLimitWarning, |
| ProcessLimitCritical |
| }; |
| |
| 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<void(Critical, Synchronous)> LowMemoryHandler; |
| |
| class MemoryPressureHandler { |
| WTF_MAKE_FAST_ALLOCATED; |
| friend class WTF::LazyNeverDestroyed<MemoryPressureHandler>; |
| public: |
| WTF_EXPORT_PRIVATE static MemoryPressureHandler& singleton(); |
| |
| WTF_EXPORT_PRIVATE void install(); |
| |
| WTF_EXPORT_PRIVATE void setShouldUsePeriodicMemoryMonitor(bool); |
| |
| #if OS(LINUX) || OS(FREEBSD) |
| WTF_EXPORT_PRIVATE void triggerMemoryPressureEvent(bool isCritical); |
| #endif |
| |
| void setMemoryKillCallback(WTF::Function<void()>&& function) { m_memoryKillCallback = WTFMove(function); } |
| void setMemoryPressureStatusChangedCallback(WTF::Function<void(MemoryPressureStatus)>&& function) { m_memoryPressureStatusChangedCallback = WTFMove(function); } |
| void setDidExceedInactiveLimitWhileActiveCallback(WTF::Function<void()>&& function) { m_didExceedInactiveLimitWhileActiveCallback = WTFMove(function); } |
| |
| void setLowMemoryHandler(LowMemoryHandler&& handler) |
| { |
| m_lowMemoryHandler = WTFMove(handler); |
| } |
| |
| bool isUnderMemoryPressure() const |
| { |
| auto memoryPressureStatus = m_memoryPressureStatus.load(); |
| return memoryPressureStatus == MemoryPressureStatus::SystemCritical |
| || memoryPressureStatus == MemoryPressureStatus::ProcessLimitCritical |
| #if PLATFORM(MAC) |
| || m_memoryUsagePolicy >= MemoryUsagePolicy::Strict |
| #endif |
| || m_isSimulatingMemoryPressure; |
| } |
| bool isSimulatingMemoryPressure() const { return m_isSimulatingMemoryPressure; } |
| void setMemoryPressureStatus(MemoryPressureStatus); |
| |
| WTF_EXPORT_PRIVATE MemoryUsagePolicy currentMemoryUsagePolicy(); |
| |
| #if PLATFORM(COCOA) |
| WTF_EXPORT_PRIVATE void setDispatchQueue(OSObjectPtr<dispatch_queue_t>&&); |
| #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<MemoryUsage> platformMemoryUsage(); |
| void logMemoryUsageChange(); |
| |
| const char* m_logString; |
| std::optional<MemoryUsage> 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<double>, Seconds); |
| |
| template<class Encoder> void encode(Encoder& encoder) const |
| { |
| encoder << baseThreshold; |
| encoder << conservativeThresholdFraction; |
| encoder << strictThresholdFraction; |
| encoder << killThresholdFraction; |
| encoder << pollInterval; |
| } |
| |
| template<class Decoder> |
| static std::optional<Configuration> decode(Decoder& decoder) |
| { |
| std::optional<size_t> baseThreshold; |
| decoder >> baseThreshold; |
| if (!baseThreshold) |
| return std::nullopt; |
| |
| std::optional<double> conservativeThresholdFraction; |
| decoder >> conservativeThresholdFraction; |
| if (!conservativeThresholdFraction) |
| return std::nullopt; |
| |
| std::optional<double> strictThresholdFraction; |
| decoder >> strictThresholdFraction; |
| if (!strictThresholdFraction) |
| return std::nullopt; |
| |
| std::optional<std::optional<double>> killThresholdFraction; |
| decoder >> killThresholdFraction; |
| if (!killThresholdFraction) |
| return std::nullopt; |
| |
| std::optional<Seconds> pollInterval; |
| decoder >> pollInterval; |
| if (!pollInterval) |
| return std::nullopt; |
| |
| return {{ *baseThreshold, *conservativeThresholdFraction, *strictThresholdFraction, *killThresholdFraction, *pollInterval }}; |
| } |
| |
| size_t baseThreshold; |
| double conservativeThresholdFraction; |
| double strictThresholdFraction; |
| std::optional<double> 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<size_t> 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<MemoryPressureStatus> m_memoryPressureStatus { MemoryPressureStatus::Normal }; |
| 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<RunLoop::Timer<MemoryPressureHandler>> m_measurementTimer; |
| WTF::Function<void()> m_memoryKillCallback; |
| WTF::Function<void(MemoryPressureStatus)> m_memoryPressureStatusChangedCallback; |
| WTF::Function<void()> m_didExceedInactiveLimitWhileActiveCallback; |
| LowMemoryHandler m_lowMemoryHandler; |
| |
| Configuration m_configuration; |
| |
| #if OS(WINDOWS) |
| void windowsMeasurementTimerFired(); |
| RunLoop::Timer<MemoryPressureHandler> m_windowsMeasurementTimer; |
| Win32Handle m_lowMemoryHandle; |
| #endif |
| |
| #if OS(LINUX) || OS(FREEBSD) |
| RunLoop::Timer<MemoryPressureHandler> m_holdOffTimer; |
| void holdOffTimerFired(); |
| #endif |
| |
| #if PLATFORM(COCOA) |
| OSObjectPtr<dispatch_queue_t> m_dispatchQueue; |
| #endif |
| }; |
| |
| } // namespace WTF |
| |
| using WTF::Critical; |
| using WTF::MemoryPressureHandler; |
| using WTF::Synchronous; |
| using WTF::WebsamProcessState; |