Improve use of NeverDestroyed
[WebKit-https.git] / Source / WTF / wtf / MemoryPressureHandler.cpp
1 /*
2  * Copyright (C) 2011-2017 Apple Inc. All Rights Reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "MemoryPressureHandler.h"
28
29 #include <wtf/MemoryFootprint.h>
30 #include <wtf/NeverDestroyed.h>
31
32 #define LOG_CHANNEL_PREFIX Log
33
34 namespace WTF {
35
36 #if RELEASE_LOG_DISABLED
37 WTFLogChannel LogMemoryPressure = { WTFLogChannelOn, "MemoryPressure" };
38 #else
39 WTFLogChannel LogMemoryPressure = { WTFLogChannelOn, "MemoryPressure", LOG_CHANNEL_WEBKIT_SUBSYSTEM, OS_LOG_DEFAULT };
40 #endif
41
42 WTF_EXPORT_PRIVATE bool MemoryPressureHandler::ReliefLogger::s_loggingEnabled = false;
43
44 MemoryPressureHandler& MemoryPressureHandler::singleton()
45 {
46     static NeverDestroyed<MemoryPressureHandler> memoryPressureHandler;
47     return memoryPressureHandler;
48 }
49
50 MemoryPressureHandler::MemoryPressureHandler()
51 #if OS(LINUX)
52     : m_holdOffTimer(RunLoop::main(), this, &MemoryPressureHandler::holdOffTimerFired)
53 #elif OS(WINDOWS)
54     : m_windowsMeasurementTimer(RunLoop::main(), this, &MemoryPressureHandler::windowsMeasurementTimerFired)
55 #endif
56 {
57 }
58
59 void MemoryPressureHandler::setShouldUsePeriodicMemoryMonitor(bool use)
60 {
61     if (!isFastMallocEnabled()) {
62         // If we're running with FastMalloc disabled, some kind of testing or debugging is probably happening.
63         // Let's be nice and not enable the memory kill mechanism.
64         return;
65     }
66
67     if (use) {
68         m_measurementTimer = std::make_unique<RunLoop::Timer<MemoryPressureHandler>>(RunLoop::main(), this, &MemoryPressureHandler::measurementTimerFired);
69         m_measurementTimer->startRepeating(30);
70     } else
71         m_measurementTimer = nullptr;
72 }
73
74 #if !RELEASE_LOG_DISABLED
75 static const char* toString(MemoryUsagePolicy policy)
76 {
77     switch (policy) {
78     case MemoryUsagePolicy::Unrestricted: return "Unrestricted";
79     case MemoryUsagePolicy::Conservative: return "Conservative";
80     case MemoryUsagePolicy::Strict: return "Strict";
81     }
82 }
83 #endif
84
85 static size_t thresholdForMemoryKillWithProcessState(WebsamProcessState processState, unsigned tabCount)
86 {
87 #if CPU(X86_64) || CPU(ARM64)
88     size_t baseThreshold;
89     if (processState == WebsamProcessState::Active)
90         baseThreshold = 4 * GB;
91     else
92         baseThreshold = 2 * GB;
93     if (tabCount <= 1)
94         return baseThreshold;
95     return baseThreshold + (std::min(tabCount - 1, 4u) * 1 * GB);
96 #else
97     UNUSED_PARAM(processState);
98     UNUSED_PARAM(tabCount);
99     return 3 * GB;
100 #endif
101 }
102
103 void MemoryPressureHandler::setPageCount(unsigned pageCount)
104 {
105     if (singleton().m_pageCount == pageCount)
106         return;
107     singleton().m_pageCount = pageCount;
108 }
109
110 size_t MemoryPressureHandler::thresholdForMemoryKill()
111 {
112     return thresholdForMemoryKillWithProcessState(m_processState, m_pageCount);
113 }
114
115 static size_t thresholdForPolicy(MemoryUsagePolicy policy)
116 {
117     switch (policy) {
118     case MemoryUsagePolicy::Conservative:
119         return 1 * GB;
120     case MemoryUsagePolicy::Strict:
121         return 1.5 * GB;
122     case MemoryUsagePolicy::Unrestricted:
123     default:
124         ASSERT_NOT_REACHED();
125         return 0;
126     }
127 }
128
129 static MemoryUsagePolicy policyForFootprint(size_t footprint)
130 {
131     if (footprint >= thresholdForPolicy(MemoryUsagePolicy::Strict))
132         return MemoryUsagePolicy::Strict;
133     if (footprint >= thresholdForPolicy(MemoryUsagePolicy::Conservative))
134         return MemoryUsagePolicy::Conservative;
135     return MemoryUsagePolicy::Unrestricted;
136 }
137
138 void MemoryPressureHandler::shrinkOrDie()
139 {
140     RELEASE_LOG(MemoryPressure, "Process is above the memory kill threshold. Trying to shrink down.");
141     releaseMemory(Critical::Yes, Synchronous::Yes);
142
143     auto footprint = memoryFootprint();
144     RELEASE_ASSERT(footprint);
145     RELEASE_LOG(MemoryPressure, "New memory footprint: %lu MB", footprint.value() / MB);
146
147     if (footprint.value() < thresholdForMemoryKill()) {
148         RELEASE_LOG(MemoryPressure, "Shrank below memory kill threshold. Process gets to live.");
149         setMemoryUsagePolicyBasedOnFootprint(footprint.value());
150         return;
151     }
152
153     RELEASE_ASSERT(m_memoryKillCallback);
154     m_memoryKillCallback();
155 }
156
157 void MemoryPressureHandler::setMemoryUsagePolicyBasedOnFootprint(size_t footprint)
158 {
159     auto newPolicy = policyForFootprint(footprint);
160     if (newPolicy == m_memoryUsagePolicy)
161         return;
162
163     RELEASE_LOG(MemoryPressure, "Memory usage policy changed: %s -> %s", toString(m_memoryUsagePolicy), toString(newPolicy));
164     m_memoryUsagePolicy = newPolicy;
165     memoryPressureStatusChanged();
166 }
167
168 void MemoryPressureHandler::measurementTimerFired()
169 {
170     auto footprint = memoryFootprint();
171     if (!footprint)
172         return;
173
174     RELEASE_LOG(MemoryPressure, "Current memory footprint: %lu MB", footprint.value() / MB);
175     if (footprint.value() >= thresholdForMemoryKill()) {
176         shrinkOrDie();
177         return;
178     }
179
180     setMemoryUsagePolicyBasedOnFootprint(footprint.value());
181
182     switch (m_memoryUsagePolicy) {
183     case MemoryUsagePolicy::Unrestricted:
184         break;
185     case MemoryUsagePolicy::Conservative:
186         releaseMemory(Critical::No, Synchronous::No);
187         break;
188     case MemoryUsagePolicy::Strict:
189         releaseMemory(Critical::Yes, Synchronous::No);
190         break;
191     }
192
193     if (processState() == WebsamProcessState::Active && footprint.value() > thresholdForMemoryKillWithProcessState(WebsamProcessState::Inactive, m_pageCount))
194         doesExceedInactiveLimitWhileActive();
195     else
196         doesNotExceedInactiveLimitWhileActive();
197 }
198
199 void MemoryPressureHandler::doesExceedInactiveLimitWhileActive()
200 {
201     if (m_hasInvokedDidExceedInactiveLimitWhileActiveCallback)
202         return;
203     if (m_didExceedInactiveLimitWhileActiveCallback)
204         m_didExceedInactiveLimitWhileActiveCallback();
205     m_hasInvokedDidExceedInactiveLimitWhileActiveCallback = true;
206 }
207
208 void MemoryPressureHandler::doesNotExceedInactiveLimitWhileActive()
209 {
210     m_hasInvokedDidExceedInactiveLimitWhileActiveCallback = false;
211 }
212
213 void MemoryPressureHandler::setProcessState(WebsamProcessState state)
214 {
215     if (m_processState == state)
216         return;
217     m_processState = state;
218 }
219
220 void MemoryPressureHandler::beginSimulatedMemoryPressure()
221 {
222     if (m_isSimulatingMemoryPressure)
223         return;
224     m_isSimulatingMemoryPressure = true;
225     memoryPressureStatusChanged();
226     respondToMemoryPressure(Critical::Yes, Synchronous::Yes);
227 }
228
229 void MemoryPressureHandler::endSimulatedMemoryPressure()
230 {
231     if (!m_isSimulatingMemoryPressure)
232         return;
233     m_isSimulatingMemoryPressure = false;
234     memoryPressureStatusChanged();
235 }
236
237 void MemoryPressureHandler::releaseMemory(Critical critical, Synchronous synchronous)
238 {
239     if (!m_lowMemoryHandler)
240         return;
241
242     ReliefLogger log("Total");
243     m_lowMemoryHandler(critical, synchronous);
244     platformReleaseMemory(critical);
245 }
246
247 void MemoryPressureHandler::setUnderMemoryPressure(bool underMemoryPressure)
248 {
249     if (m_underMemoryPressure == underMemoryPressure)
250         return;
251     m_underMemoryPressure = underMemoryPressure;
252     memoryPressureStatusChanged();
253 }
254
255 void MemoryPressureHandler::memoryPressureStatusChanged()
256 {
257     if (m_memoryPressureStatusChangedCallback)
258         m_memoryPressureStatusChangedCallback(isUnderMemoryPressure());
259 }
260
261 void MemoryPressureHandler::ReliefLogger::logMemoryUsageChange()
262 {
263 #if !RELEASE_LOG_DISABLED
264 #define STRING_SPECIFICATION "%{public}s"
265 #define MEMORYPRESSURE_LOG(...) RELEASE_LOG(MemoryPressure, __VA_ARGS__)
266 #else
267 #define STRING_SPECIFICATION "%s"
268 #define MEMORYPRESSURE_LOG(...) WTFLogAlways(__VA_ARGS__)
269 #endif
270
271     auto currentMemory = platformMemoryUsage();
272     if (!currentMemory || !m_initialMemory) {
273         MEMORYPRESSURE_LOG("Memory pressure relief: " STRING_SPECIFICATION ": (Unable to get dirty memory information for process)", m_logString);
274         return;
275     }
276
277     long residentDiff = currentMemory->resident - m_initialMemory->resident;
278     long physicalDiff = currentMemory->physical - m_initialMemory->physical;
279
280     MEMORYPRESSURE_LOG("Memory pressure relief: " STRING_SPECIFICATION ": res = %zu/%zu/%ld, res+swap = %zu/%zu/%ld",
281         m_logString,
282         m_initialMemory->resident, currentMemory->resident, residentDiff,
283         m_initialMemory->physical, currentMemory->physical, physicalDiff);
284 }
285
286 #if !PLATFORM(COCOA) && !OS(LINUX) && !OS(WINDOWS)
287 void MemoryPressureHandler::install() { }
288 void MemoryPressureHandler::uninstall() { }
289 void MemoryPressureHandler::holdOff(unsigned) { }
290 void MemoryPressureHandler::respondToMemoryPressure(Critical, Synchronous) { }
291 void MemoryPressureHandler::platformReleaseMemory(Critical) { }
292 std::optional<MemoryPressureHandler::ReliefLogger::MemoryUsage> MemoryPressureHandler::ReliefLogger::platformMemoryUsage() { return std::nullopt; }
293 #endif
294
295 #if !OS(WINDOWS)
296 void MemoryPressureHandler::platformInitialize() { }
297 #endif
298
299 } // namespace WebCore