46c47aba1a8018d2a9af2cf80a389282fdd86aa4
[WebKit-https.git] / Source / WebCore / platform / cocoa / MemoryPressureHandlerCocoa.mm
1 /*
2  * Copyright (C) 2011-2015 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 #import "config.h"
27 #import "MemoryPressureHandler.h"
28
29 #import "IOSurfacePool.h"
30 #import "GCController.h"
31 #import "JSDOMWindow.h"
32 #import "JSDOMWindowBase.h"
33 #import "LayerPool.h"
34 #import "Logging.h"
35 #import "WebCoreSystemInterface.h"
36 #import <mach/mach.h>
37 #import <mach/task_info.h>
38 #import <malloc/malloc.h>
39 #import <notify.h>
40 #import <wtf/CurrentTime.h>
41
42 #if PLATFORM(IOS)
43 #import "SystemMemory.h"
44 #import "WebCoreThread.h"
45 #endif
46
47 extern "C" void cache_simulate_memory_warning_event(uint64_t);
48 extern "C" void _sqlite3_purgeEligiblePagerCacheMemory(void);
49
50 namespace WebCore {
51
52 void MemoryPressureHandler::platformReleaseMemory(Critical critical)
53 {
54     {
55         ReliefLogger log("Purging SQLite caches");
56         _sqlite3_purgeEligiblePagerCacheMemory();
57     }
58
59     {
60         ReliefLogger log("Drain LayerPools");
61         for (auto& pool : LayerPool::allLayerPools())
62             pool->drain();
63     }
64 #if USE(IOSURFACE)
65     {
66         ReliefLogger log("Drain IOSurfacePool");
67         IOSurfacePool::sharedPool().discardAllSurfaces();
68     }
69 #endif
70
71 #if PLATFORM(IOS) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
72     if (critical == Critical::Yes && !isUnderMemoryPressure()) {
73         // libcache listens to OS memory notifications, but for process suspension
74         // or memory pressure simulation, we need to prod it manually:
75         ReliefLogger log("Purging libcache caches");
76         cache_simulate_memory_warning_event(DISPATCH_MEMORYPRESSURE_CRITICAL);
77     }
78 #else
79     UNUSED_PARAM(critical);
80 #endif
81 }
82
83 static dispatch_source_t _cache_event_source = 0;
84 static dispatch_source_t _timer_event_source = 0;
85 static int _notifyToken;
86
87 // Disable memory event reception for a minimum of s_minimumHoldOffTime
88 // seconds after receiving an event.  Don't let events fire any sooner than
89 // s_holdOffMultiplier times the last cleanup processing time.  Effectively 
90 // this is 1 / s_holdOffMultiplier percent of the time.
91 // These value seems reasonable and testing verifies that it throttles frequent
92 // low memory events, greatly reducing CPU usage.
93 static const unsigned s_minimumHoldOffTime = 5;
94 #if !PLATFORM(IOS)
95 static const unsigned s_holdOffMultiplier = 20;
96 #endif
97
98 void MemoryPressureHandler::install()
99 {
100     if (m_installed || _timer_event_source)
101         return;
102
103     dispatch_async(dispatch_get_main_queue(), ^{
104 #if PLATFORM(IOS)
105         _cache_event_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_MEMORYPRESSURE, 0, DISPATCH_MEMORYPRESSURE_NORMAL | DISPATCH_MEMORYPRESSURE_WARN | DISPATCH_MEMORYPRESSURE_CRITICAL, dispatch_get_main_queue());
106 #elif PLATFORM(MAC)
107         _cache_event_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_MEMORYPRESSURE, 0, DISPATCH_MEMORYPRESSURE_CRITICAL, dispatch_get_main_queue());
108 #endif
109
110         dispatch_set_context(_cache_event_source, this);
111         dispatch_source_set_event_handler(_cache_event_source, ^{
112             bool critical = true;
113 #if PLATFORM(IOS)
114             unsigned long status = dispatch_source_get_data(_cache_event_source);
115             critical = status == DISPATCH_MEMORYPRESSURE_CRITICAL;
116             auto& memoryPressureHandler = MemoryPressureHandler::singleton();
117             bool wasCritical = memoryPressureHandler.isUnderMemoryPressure();
118             memoryPressureHandler.setUnderMemoryPressure(critical);
119             if (status == DISPATCH_MEMORYPRESSURE_NORMAL) {
120                 if (ReliefLogger::loggingEnabled())
121                     NSLog(@"System is no longer under (%s) memory pressure.", wasCritical ? "critical" : "non-critical");
122                 return;
123             }
124
125             if (ReliefLogger::loggingEnabled())
126                 NSLog(@"Got memory pressure notification (%s)", critical ? "critical" : "non-critical");
127 #endif
128             MemoryPressureHandler::singleton().respondToMemoryPressure(critical ? Critical::Yes : Critical::No);
129         });
130         dispatch_resume(_cache_event_source);
131     });
132
133     // Allow simulation of memory pressure with "notifyutil -p org.WebKit.lowMemory"
134     notify_register_dispatch("org.WebKit.lowMemory", &_notifyToken, dispatch_get_main_queue(), ^(int) {
135         MemoryPressureHandler::singleton().respondToMemoryPressure(Critical::Yes, Synchronous::Yes);
136
137         WTF::releaseFastMallocFreeMemory();
138
139         malloc_zone_pressure_relief(nullptr, 0);
140     });
141
142     m_installed = true;
143 }
144
145 void MemoryPressureHandler::uninstall()
146 {
147     if (!m_installed)
148         return;
149
150     dispatch_async(dispatch_get_main_queue(), ^{
151         if (_cache_event_source) {
152             dispatch_source_cancel(_cache_event_source);
153             dispatch_release(_cache_event_source);
154             _cache_event_source = 0;
155         }
156
157         if (_timer_event_source) {
158             dispatch_source_cancel(_timer_event_source);
159             dispatch_release(_timer_event_source);
160             _timer_event_source = 0;
161         }
162     });
163
164     m_installed = false;
165     
166     notify_cancel(_notifyToken);
167 }
168
169 void MemoryPressureHandler::holdOff(unsigned seconds)
170 {
171     dispatch_async(dispatch_get_main_queue(), ^{
172         _timer_event_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
173         if (_timer_event_source) {
174             dispatch_set_context(_timer_event_source, this);
175             dispatch_source_set_timer(_timer_event_source, dispatch_time(DISPATCH_TIME_NOW, seconds * NSEC_PER_SEC), DISPATCH_TIME_FOREVER, 1 * s_minimumHoldOffTime);
176             dispatch_source_set_event_handler(_timer_event_source, ^{
177                 if (_timer_event_source) {
178                     dispatch_source_cancel(_timer_event_source);
179                     dispatch_release(_timer_event_source);
180                     _timer_event_source = 0;
181                 }
182                 MemoryPressureHandler::singleton().install();
183             });
184             dispatch_resume(_timer_event_source);
185         }
186     });
187 }
188
189 void MemoryPressureHandler::respondToMemoryPressure(Critical critical, Synchronous synchronous)
190 {
191 #if !PLATFORM(IOS)
192     uninstall();
193     double startTime = monotonicallyIncreasingTime();
194 #endif
195
196     m_lowMemoryHandler(critical, synchronous);
197
198 #if !PLATFORM(IOS)
199     unsigned holdOffTime = (monotonicallyIncreasingTime() - startTime) * s_holdOffMultiplier;
200     holdOff(std::max(holdOffTime, s_minimumHoldOffTime));
201 #endif
202 }
203
204 size_t MemoryPressureHandler::ReliefLogger::platformMemoryUsage()
205 {
206     // Flush free memory back to the OS before every measurement.
207     // Note that this code only runs when detailed pressure relief logging is enabled.
208     WTF::releaseFastMallocFreeMemory();
209     malloc_zone_pressure_relief(nullptr, 0);
210
211     task_vm_info_data_t vmInfo;
212     mach_msg_type_number_t count = TASK_VM_INFO_COUNT;
213     kern_return_t err = task_info(mach_task_self(), TASK_VM_INFO, (task_info_t) &vmInfo, &count);
214     if (err != KERN_SUCCESS)
215         return static_cast<size_t>(-1);
216
217     return static_cast<size_t>(vmInfo.internal);
218 }
219
220 void MemoryPressureHandler::ReliefLogger::platformLog()
221 {
222     size_t currentMemory = platformMemoryUsage();
223     if (currentMemory == static_cast<size_t>(-1) || m_initialMemory == static_cast<size_t>(-1)) {
224         NSLog(@"%s (Unable to get dirty memory information for process)\n", m_logString);
225         return;
226     }
227
228     ssize_t memoryDiff = currentMemory - m_initialMemory;
229     if (memoryDiff < 0)
230         NSLog(@"Pressure relief: %s: -dirty %ld bytes (from %ld to %ld)\n", m_logString, (memoryDiff * -1), m_initialMemory, currentMemory);
231     else if (memoryDiff > 0)
232         NSLog(@"Pressure relief: %s: +dirty %ld bytes (from %ld to %ld)\n", m_logString, memoryDiff, m_initialMemory, currentMemory);
233     else
234         NSLog(@"Pressure relief: %s: =dirty (at %ld bytes)\n", m_logString, currentMemory);
235 }
236
237 #if PLATFORM(IOS)
238 static void respondToMemoryPressureCallback(CFRunLoopObserverRef observer, CFRunLoopActivity /*activity*/, void* /*info*/)
239 {
240     MemoryPressureHandler::singleton().respondToMemoryPressureIfNeeded();
241     CFRunLoopObserverInvalidate(observer);
242     CFRelease(observer);
243 }
244
245 void MemoryPressureHandler::installMemoryReleaseBlock(void (^releaseMemoryBlock)(), bool clearPressureOnMemoryRelease)
246 {
247     if (m_installed)
248         return;
249     m_releaseMemoryBlock = Block_copy(releaseMemoryBlock);
250     m_clearPressureOnMemoryRelease = clearPressureOnMemoryRelease;
251     m_installed = true;
252 }
253
254 void MemoryPressureHandler::setReceivedMemoryPressure(MemoryPressureReason reason)
255 {
256     m_underMemoryPressure = true;
257
258     {
259         DeprecatedMutexLocker locker(m_observerMutex);
260         if (!m_observer) {
261             m_observer = CFRunLoopObserverCreate(NULL, kCFRunLoopBeforeWaiting | kCFRunLoopExit, NO /* don't repeat */,
262                 0, WebCore::respondToMemoryPressureCallback, NULL);
263             CFRunLoopAddObserver(WebThreadRunLoop(), m_observer, kCFRunLoopCommonModes);
264             CFRunLoopWakeUp(WebThreadRunLoop());
265         }
266         m_memoryPressureReason |= reason;
267     }
268 }
269
270 void MemoryPressureHandler::clearMemoryPressure()
271 {
272     m_underMemoryPressure = false;
273
274     {
275         DeprecatedMutexLocker locker(m_observerMutex);
276         m_memoryPressureReason = MemoryPressureReasonNone;
277     }
278 }
279
280 bool MemoryPressureHandler::shouldWaitForMemoryClearMessage()
281 {
282     DeprecatedMutexLocker locker(m_observerMutex);
283     return m_memoryPressureReason & MemoryPressureReasonVMStatus;
284 }
285
286 void MemoryPressureHandler::respondToMemoryPressureIfNeeded()
287 {
288     ASSERT(WebThreadIsLockedOrDisabled());
289
290     {
291         DeprecatedMutexLocker locker(m_observerMutex);
292         m_observer = 0;
293     }
294
295     if (isUnderMemoryPressure()) {
296         ASSERT(m_releaseMemoryBlock);
297         LOG(MemoryPressure, "Handle memory pressure at %s", __PRETTY_FUNCTION__);
298         m_releaseMemoryBlock();
299         if (m_clearPressureOnMemoryRelease)
300             clearMemoryPressure();
301     }
302 }
303
304 #endif
305
306 } // namespace WebCore