[WTF] Add makeUnique<T>, which ensures T is fast-allocated, makeUnique / makeUniqueWi...
[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 <wtf/MemoryPressureHandler.h>
28
29 #include <wtf/MemoryFootprint.h>
30 #include <wtf/NeverDestroyed.h>
31 #include <wtf/RAMSize.h>
32
33 #define LOG_CHANNEL_PREFIX Log
34
35 namespace WTF {
36
37 #if RELEASE_LOG_DISABLED
38 WTFLogChannel LogMemoryPressure = { WTFLogChannelState::On, "MemoryPressure", WTFLogLevel::Error };
39 #else
40 WTFLogChannel LogMemoryPressure = { WTFLogChannelState::On, "MemoryPressure", WTFLogLevel::Error, LOG_CHANNEL_WEBKIT_SUBSYSTEM, OS_LOG_DEFAULT };
41 #endif
42
43 WTF_EXPORT_PRIVATE bool MemoryPressureHandler::ReliefLogger::s_loggingEnabled = false;
44
45 MemoryPressureHandler& MemoryPressureHandler::singleton()
46 {
47     static NeverDestroyed<MemoryPressureHandler> memoryPressureHandler;
48     return memoryPressureHandler;
49 }
50
51 MemoryPressureHandler::MemoryPressureHandler()
52 #if OS(LINUX)
53     : m_holdOffTimer(RunLoop::main(), this, &MemoryPressureHandler::holdOffTimerFired)
54 #elif OS(WINDOWS)
55     : m_windowsMeasurementTimer(RunLoop::main(), this, &MemoryPressureHandler::windowsMeasurementTimerFired)
56 #endif
57 {
58 #if PLATFORM(COCOA)
59     setDispatchQueue(dispatch_get_main_queue());
60 #endif
61 }
62
63 void MemoryPressureHandler::setShouldUsePeriodicMemoryMonitor(bool use)
64 {
65     if (!isFastMallocEnabled()) {
66         // If we're running with FastMalloc disabled, some kind of testing or debugging is probably happening.
67         // Let's be nice and not enable the memory kill mechanism.
68         return;
69     }
70
71     if (use) {
72         m_measurementTimer = makeUnique<RunLoop::Timer<MemoryPressureHandler>>(RunLoop::main(), this, &MemoryPressureHandler::measurementTimerFired);
73         m_measurementTimer->startRepeating(30_s);
74     } else
75         m_measurementTimer = nullptr;
76 }
77
78 #if !RELEASE_LOG_DISABLED
79 static const char* toString(MemoryUsagePolicy policy)
80 {
81     switch (policy) {
82     case MemoryUsagePolicy::Unrestricted: return "Unrestricted";
83     case MemoryUsagePolicy::Conservative: return "Conservative";
84     case MemoryUsagePolicy::Strict: return "Strict";
85     }
86 }
87 #endif
88
89 static size_t thresholdForMemoryKillWithProcessState(WebsamProcessState processState, unsigned tabCount)
90 {
91     size_t baseThreshold = 2 * GB;
92 #if CPU(X86_64) || CPU(ARM64)
93     if (processState == WebsamProcessState::Active)
94         baseThreshold = 4 * GB;
95     if (tabCount > 1)
96         baseThreshold += std::min(tabCount - 1, 4u) * 1 * GB;
97 #else
98     if ((tabCount > 1) || (processState == WebsamProcessState::Active))
99         baseThreshold = 3 * GB;
100 #endif
101     return std::min(baseThreshold, static_cast<size_t>(ramSize() * 0.9));
102 }
103
104 void MemoryPressureHandler::setPageCount(unsigned pageCount)
105 {
106     if (singleton().m_pageCount == pageCount)
107         return;
108     singleton().m_pageCount = pageCount;
109 }
110
111 size_t MemoryPressureHandler::thresholdForMemoryKill()
112 {
113     return thresholdForMemoryKillWithProcessState(m_processState, m_pageCount);
114 }
115
116 static size_t thresholdForPolicy(MemoryUsagePolicy policy)
117 {
118     const size_t baseThresholdForPolicy = std::min(3 * GB, ramSize());
119
120 #if PLATFORM(IOS_FAMILY)
121     const double conservativeThresholdFraction = 0.5;
122     const double strictThresholdFraction = 0.65;
123 #else
124     const double conservativeThresholdFraction = 0.33;
125     const double strictThresholdFraction = 0.5;
126 #endif
127
128     switch (policy) {
129     case MemoryUsagePolicy::Unrestricted:
130         return 0;
131     case MemoryUsagePolicy::Conservative:
132         return baseThresholdForPolicy * conservativeThresholdFraction;
133     case MemoryUsagePolicy::Strict:
134         return baseThresholdForPolicy * strictThresholdFraction;
135     default:
136         ASSERT_NOT_REACHED();
137         return 0;
138     }
139 }
140
141 static MemoryUsagePolicy policyForFootprint(size_t footprint)
142 {
143     if (footprint >= thresholdForPolicy(MemoryUsagePolicy::Strict))
144         return MemoryUsagePolicy::Strict;
145     if (footprint >= thresholdForPolicy(MemoryUsagePolicy::Conservative))
146         return MemoryUsagePolicy::Conservative;
147     return MemoryUsagePolicy::Unrestricted;
148 }
149
150 MemoryUsagePolicy MemoryPressureHandler::currentMemoryUsagePolicy()
151 {
152     return policyForFootprint(memoryFootprint());
153 }
154
155 void MemoryPressureHandler::shrinkOrDie()
156 {
157     RELEASE_LOG(MemoryPressure, "Process is above the memory kill threshold. Trying to shrink down.");
158     releaseMemory(Critical::Yes, Synchronous::Yes);
159
160     size_t footprint = memoryFootprint();
161     RELEASE_LOG(MemoryPressure, "New memory footprint: %zu MB", footprint / MB);
162
163     if (footprint < thresholdForMemoryKill()) {
164         RELEASE_LOG(MemoryPressure, "Shrank below memory kill threshold. Process gets to live.");
165         setMemoryUsagePolicyBasedOnFootprint(footprint);
166         return;
167     }
168
169     WTFLogAlways("Unable to shrink memory footprint of process (%zu MB) below the kill thresold (%zu MB). Killed\n", footprint / MB, thresholdForMemoryKill() / MB);
170     RELEASE_ASSERT(m_memoryKillCallback);
171     m_memoryKillCallback();
172 }
173
174 void MemoryPressureHandler::setMemoryUsagePolicyBasedOnFootprint(size_t footprint)
175 {
176     auto newPolicy = policyForFootprint(footprint);
177     if (newPolicy == m_memoryUsagePolicy)
178         return;
179
180     RELEASE_LOG(MemoryPressure, "Memory usage policy changed: %s -> %s", toString(m_memoryUsagePolicy), toString(newPolicy));
181     m_memoryUsagePolicy = newPolicy;
182     memoryPressureStatusChanged();
183 }
184
185 void MemoryPressureHandler::measurementTimerFired()
186 {
187     size_t footprint = memoryFootprint();
188     RELEASE_LOG(MemoryPressure, "Current memory footprint: %zu MB", footprint / MB);
189     if (footprint >= thresholdForMemoryKill()) {
190         shrinkOrDie();
191         return;
192     }
193
194     setMemoryUsagePolicyBasedOnFootprint(footprint);
195
196     switch (m_memoryUsagePolicy) {
197     case MemoryUsagePolicy::Unrestricted:
198         break;
199     case MemoryUsagePolicy::Conservative:
200         releaseMemory(Critical::No, Synchronous::No);
201         break;
202     case MemoryUsagePolicy::Strict:
203         releaseMemory(Critical::Yes, Synchronous::No);
204         break;
205     }
206
207     if (processState() == WebsamProcessState::Active && footprint > thresholdForMemoryKillWithProcessState(WebsamProcessState::Inactive, m_pageCount))
208         doesExceedInactiveLimitWhileActive();
209     else
210         doesNotExceedInactiveLimitWhileActive();
211 }
212
213 void MemoryPressureHandler::doesExceedInactiveLimitWhileActive()
214 {
215     if (m_hasInvokedDidExceedInactiveLimitWhileActiveCallback)
216         return;
217     if (m_didExceedInactiveLimitWhileActiveCallback)
218         m_didExceedInactiveLimitWhileActiveCallback();
219     m_hasInvokedDidExceedInactiveLimitWhileActiveCallback = true;
220 }
221
222 void MemoryPressureHandler::doesNotExceedInactiveLimitWhileActive()
223 {
224     m_hasInvokedDidExceedInactiveLimitWhileActiveCallback = false;
225 }
226
227 void MemoryPressureHandler::setProcessState(WebsamProcessState state)
228 {
229     if (m_processState == state)
230         return;
231     m_processState = state;
232 }
233
234 void MemoryPressureHandler::beginSimulatedMemoryPressure()
235 {
236     if (m_isSimulatingMemoryPressure)
237         return;
238     m_isSimulatingMemoryPressure = true;
239     memoryPressureStatusChanged();
240     respondToMemoryPressure(Critical::Yes, Synchronous::Yes);
241 }
242
243 void MemoryPressureHandler::endSimulatedMemoryPressure()
244 {
245     if (!m_isSimulatingMemoryPressure)
246         return;
247     m_isSimulatingMemoryPressure = false;
248     memoryPressureStatusChanged();
249 }
250
251 void MemoryPressureHandler::releaseMemory(Critical critical, Synchronous synchronous)
252 {
253     if (!m_lowMemoryHandler)
254         return;
255
256     ReliefLogger log("Total");
257     m_lowMemoryHandler(critical, synchronous);
258     platformReleaseMemory(critical);
259 }
260
261 void MemoryPressureHandler::setUnderMemoryPressure(bool underMemoryPressure)
262 {
263     if (m_underMemoryPressure == underMemoryPressure)
264         return;
265     m_underMemoryPressure = underMemoryPressure;
266     memoryPressureStatusChanged();
267 }
268
269 void MemoryPressureHandler::memoryPressureStatusChanged()
270 {
271     if (m_memoryPressureStatusChangedCallback)
272         m_memoryPressureStatusChangedCallback(isUnderMemoryPressure());
273 }
274
275 void MemoryPressureHandler::ReliefLogger::logMemoryUsageChange()
276 {
277 #if !RELEASE_LOG_DISABLED
278 #define STRING_SPECIFICATION "%{public}s"
279 #define MEMORYPRESSURE_LOG(...) RELEASE_LOG(MemoryPressure, __VA_ARGS__)
280 #else
281 #define STRING_SPECIFICATION "%s"
282 #define MEMORYPRESSURE_LOG(...) WTFLogAlways(__VA_ARGS__)
283 #endif
284
285     auto currentMemory = platformMemoryUsage();
286     if (!currentMemory || !m_initialMemory) {
287         MEMORYPRESSURE_LOG("Memory pressure relief: " STRING_SPECIFICATION ": (Unable to get dirty memory information for process)", m_logString);
288         return;
289     }
290
291     long residentDiff = currentMemory->resident - m_initialMemory->resident;
292     long physicalDiff = currentMemory->physical - m_initialMemory->physical;
293
294     MEMORYPRESSURE_LOG("Memory pressure relief: " STRING_SPECIFICATION ": res = %zu/%zu/%ld, res+swap = %zu/%zu/%ld",
295         m_logString,
296         m_initialMemory->resident, currentMemory->resident, residentDiff,
297         m_initialMemory->physical, currentMemory->physical, physicalDiff);
298 }
299
300 #if !OS(WINDOWS)
301 void MemoryPressureHandler::platformInitialize() { }
302 #endif
303
304 #if PLATFORM(COCOA)
305 void MemoryPressureHandler::setDispatchQueue(dispatch_queue_t queue)
306 {
307     RELEASE_ASSERT(!m_installed);
308     dispatch_retain(queue);
309     if (m_dispatchQueue)
310         dispatch_release(m_dispatchQueue);
311     m_dispatchQueue = queue;
312 }
313 #endif
314
315 } // namespace WebCore