282 lines
9.3 KiB
C++
282 lines
9.3 KiB
C++
/*
|
|
* 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 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) || OS(HAIKU)
|
|
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); }
|
|
|
|
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<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<bool> 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<RunLoop::Timer<MemoryPressureHandler>> m_measurementTimer;
|
|
WTF::Function<void()> m_memoryKillCallback;
|
|
WTF::Function<void(bool)> 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) || OS(HAIKU)
|
|
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;
|