Log number of bytes reclaimed at each step of memory pressure relief.
[WebKit-https.git] / Source / WebCore / platform / cocoa / MemoryPressureHandlerCocoa.mm
1 /*
2  * Copyright (C) 2011-2014 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 "LayerPool.h"
32 #import "Logging.h"
33 #import "WebCoreSystemInterface.h"
34 #import <mach/mach.h>
35 #import <mach/task_info.h>
36 #import <malloc/malloc.h>
37 #import <notify.h>
38 #import <wtf/CurrentTime.h>
39
40 #if PLATFORM(IOS)
41 #import "SystemMemory.h"
42 #import "WebCoreThread.h"
43 #import <dispatch/private.h>
44 #endif
45
46 namespace WebCore {
47
48 void MemoryPressureHandler::platformReleaseMemory(bool)
49 {
50 #if PLATFORM(MAC)
51     {
52         ReliefLogger log("Drain LayerPool");
53         LayerPool::sharedPool()->drain();
54     }
55 #endif
56 #if USE(IOSURFACE)
57     {
58         ReliefLogger log("Drain IOSurfacePool");
59         IOSurfacePool::sharedPool().discardAllSurfaces();
60     }
61 #endif
62 }
63
64 static dispatch_source_t _cache_event_source = 0;
65 static dispatch_source_t _timer_event_source = 0;
66 static int _notifyToken;
67
68 // Disable memory event reception for a minimum of s_minimumHoldOffTime
69 // seconds after receiving an event.  Don't let events fire any sooner than
70 // s_holdOffMultiplier times the last cleanup processing time.  Effectively 
71 // this is 1 / s_holdOffMultiplier percent of the time.
72 // These value seems reasonable and testing verifies that it throttles frequent
73 // low memory events, greatly reducing CPU usage.
74 static const unsigned s_minimumHoldOffTime = 5;
75 static const unsigned s_holdOffMultiplier = 20;
76
77 void MemoryPressureHandler::install()
78 {
79     if (m_installed || _timer_event_source)
80         return;
81
82     dispatch_async(dispatch_get_main_queue(), ^{
83 #if PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 80000
84         _cache_event_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_MEMORYSTATUS, 0, DISPATCH_MEMORYSTATUS_PRESSURE_WARN, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
85 #elif PLATFORM(MAC) && MAC_OS_X_VERSION_MIN_REQUIRED >= 1090
86         _cache_event_source = wkCreateMemoryStatusPressureCriticalDispatchOnMainQueue();
87 #else
88         _cache_event_source = wkCreateVMPressureDispatchOnMainQueue();
89 #endif
90         if (_cache_event_source) {
91             dispatch_set_context(_cache_event_source, this);
92             dispatch_source_set_event_handler(_cache_event_source, ^{
93                 memoryPressureHandler().respondToMemoryPressure();
94             });
95             dispatch_resume(_cache_event_source);
96         }
97     });
98
99     // Allow simulation of memory pressure with "notifyutil -p org.WebKit.lowMemory"
100     notify_register_dispatch("org.WebKit.lowMemory", &_notifyToken, dispatch_get_main_queue(), ^(int) {
101
102         // We only do a synchronous GC when *simulating* memory pressure.
103         // This gives us a more consistent picture of live objects at the end of testing.
104         gcController().garbageCollectNow();
105
106         memoryPressureHandler().respondToMemoryPressure();
107         malloc_zone_pressure_relief(nullptr, 0);
108     });
109
110     m_installed = true;
111 }
112
113 void MemoryPressureHandler::uninstall()
114 {
115     if (!m_installed)
116         return;
117
118     dispatch_async(dispatch_get_main_queue(), ^{
119         if (_cache_event_source) {
120             dispatch_source_cancel(_cache_event_source);
121             dispatch_release(_cache_event_source);
122             _cache_event_source = 0;
123         }
124
125         if (_timer_event_source) {
126             dispatch_source_cancel(_timer_event_source);
127             dispatch_release(_timer_event_source);
128             _timer_event_source = 0;
129         }
130     });
131
132     m_installed = false;
133     
134     notify_cancel(_notifyToken);
135 }
136
137 void MemoryPressureHandler::holdOff(unsigned seconds)
138 {
139     dispatch_async(dispatch_get_main_queue(), ^{
140         _timer_event_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
141         if (_timer_event_source) {
142             dispatch_set_context(_timer_event_source, this);
143             dispatch_source_set_timer(_timer_event_source, dispatch_time(DISPATCH_TIME_NOW, seconds * NSEC_PER_SEC), DISPATCH_TIME_FOREVER, 1 * s_minimumHoldOffTime);
144             dispatch_source_set_event_handler(_timer_event_source, ^{
145                 if (_timer_event_source) {
146                     dispatch_source_cancel(_timer_event_source);
147                     dispatch_release(_timer_event_source);
148                     _timer_event_source = 0;
149                 }
150                 memoryPressureHandler().install();
151             });
152             dispatch_resume(_timer_event_source);
153         }
154     });
155 }
156
157 void MemoryPressureHandler::respondToMemoryPressure()
158 {
159     uninstall();
160
161     double startTime = monotonicallyIncreasingTime();
162
163     m_lowMemoryHandler(false);
164
165     unsigned holdOffTime = (monotonicallyIncreasingTime() - startTime) * s_holdOffMultiplier;
166
167     holdOff(std::max(holdOffTime, s_minimumHoldOffTime));
168 }
169
170 size_t MemoryPressureHandler::ReliefLogger::platformMemoryUsage()
171 {
172     task_vm_info_data_t vmInfo;
173     mach_msg_type_number_t count = TASK_VM_INFO_COUNT;
174     kern_return_t err = task_info(mach_task_self(), TASK_VM_INFO, (task_info_t) &vmInfo, &count);
175     if (err != KERN_SUCCESS)
176         return static_cast<size_t>(-1);
177
178     return vmInfo.internal;
179 }
180
181 void MemoryPressureHandler::ReliefLogger::platformLog()
182 {
183     size_t currentMemory = platformMemoryUsage();
184     if (currentMemory == static_cast<size_t>(-1) || m_initialMemory == static_cast<size_t>(-1)) {
185         NSLog(@"%s (Unable to get dirty memory information for process)\n", m_logString);
186         return;
187     }
188
189     ssize_t memoryDiff = currentMemory - m_initialMemory;
190     if (memoryDiff < 0)
191         NSLog(@"Pressure relief: %s: -dirty %ld bytes (from %ld to %ld)\n", m_logString, (memoryDiff * -1), m_initialMemory, currentMemory);
192     else if (memoryDiff > 0)
193         NSLog(@"Pressure relief: %s: +dirty %ld bytes (from %ld to %ld)\n", m_logString, memoryDiff, m_initialMemory, currentMemory);
194     else
195         NSLog(@"Pressure relief: %s: =dirty (at %ld bytes)\n", m_logString, currentMemory);
196 }
197
198 #if PLATFORM(IOS)
199 static void respondToMemoryPressureCallback(CFRunLoopObserverRef observer, CFRunLoopActivity /*activity*/, void* /*info*/)
200 {
201     memoryPressureHandler().respondToMemoryPressureIfNeeded();
202     CFRunLoopObserverInvalidate(observer);
203     CFRelease(observer);
204 }
205
206 void MemoryPressureHandler::installMemoryReleaseBlock(void (^releaseMemoryBlock)(), bool clearPressureOnMemoryRelease)
207 {
208     if (m_installed)
209         return;
210     m_releaseMemoryBlock = Block_copy(releaseMemoryBlock);
211     m_clearPressureOnMemoryRelease = clearPressureOnMemoryRelease;
212     m_installed = true;
213 }
214
215 void MemoryPressureHandler::setReceivedMemoryPressure(MemoryPressureReason reason)
216 {
217     m_underMemoryPressure = true;
218
219     {
220         MutexLocker locker(m_observerMutex);
221         if (!m_observer) {
222             m_observer = CFRunLoopObserverCreate(NULL, kCFRunLoopBeforeWaiting | kCFRunLoopExit, NO /* don't repeat */,
223                 0, WebCore::respondToMemoryPressureCallback, NULL);
224             CFRunLoopAddObserver(WebThreadRunLoop(), m_observer, kCFRunLoopCommonModes);
225             CFRunLoopWakeUp(WebThreadRunLoop());
226         }
227         m_memoryPressureReason |= reason;
228     }
229 }
230
231 void MemoryPressureHandler::clearMemoryPressure()
232 {
233     m_underMemoryPressure = false;
234
235     {
236         MutexLocker locker(m_observerMutex);
237         m_memoryPressureReason = MemoryPressureReasonNone;
238     }
239 }
240
241 bool MemoryPressureHandler::shouldWaitForMemoryClearMessage()
242 {
243     MutexLocker locker(m_observerMutex);
244     return m_memoryPressureReason & MemoryPressureReasonVMStatus;
245 }
246
247 void MemoryPressureHandler::respondToMemoryPressureIfNeeded()
248 {
249     ASSERT(WebThreadIsLockedOrDisabled());
250
251     {
252         MutexLocker locker(m_observerMutex);
253         m_observer = 0;
254     }
255
256     if (isUnderMemoryPressure()) {
257         ASSERT(m_releaseMemoryBlock);
258         LOG(MemoryPressure, "Handle memory pressure at %s", __PRETTY_FUNCTION__);
259         m_releaseMemoryBlock();
260         if (m_clearPressureOnMemoryRelease)
261             clearMemoryPressure();
262     }
263 }
264
265 #endif
266
267 } // namespace WebCore