1/* 2 * Copyright (C) 2011, 2012 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 <WebCore/CSSValuePool.h> 30#import <WebCore/GCController.h> 31#import <WebCore/FontCache.h> 32#import <WebCore/MemoryCache.h> 33#import <WebCore/PageCache.h> 34#import <WebCore/LayerPool.h> 35#import <WebCore/ScrollingThread.h> 36#import <WebCore/StorageThread.h> 37#import <WebCore/WorkerThread.h> 38#import <wtf/CurrentTime.h> 39#import <wtf/FastMalloc.h> 40#import <wtf/Functional.h> 41 42#if !PLATFORM(IOS) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070 43#import "WebCoreSystemInterface.h" 44#import <notify.h> 45#endif 46 47using std::max; 48 49namespace WebCore { 50 51#if PLATFORM(IOS) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070 52 53#if !PLATFORM(IOS) 54static dispatch_source_t _cache_event_source = 0; 55static dispatch_source_t _timer_event_source = 0; 56static int _notifyToken; 57 58// Disable memory event reception for a minimum of s_minimumHoldOffTime 59// seconds after receiving an event. Don't let events fire any sooner than 60// s_holdOffMultiplier times the last cleanup processing time. Effectively 61// this is 1 / s_holdOffMultiplier percent of the time. 62// These value seems reasonable and testing verifies that it throttles frequent 63// low memory events, greatly reducing CPU usage. 64static const unsigned s_minimumHoldOffTime = 5; 65static const unsigned s_holdOffMultiplier = 20; 66 67void MemoryPressureHandler::install() 68{ 69 if (m_installed || _timer_event_source) 70 return; 71 72 dispatch_async(dispatch_get_main_queue(), ^{ 73#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1090 74 _cache_event_source = wkCreateMemoryStatusPressureCriticalDispatchOnMainQueue(); 75#else 76 _cache_event_source = wkCreateVMPressureDispatchOnMainQueue(); 77#endif 78 if (_cache_event_source) { 79 dispatch_set_context(_cache_event_source, this); 80 dispatch_source_set_event_handler(_cache_event_source, ^{ memoryPressureHandler().respondToMemoryPressure();}); 81 dispatch_resume(_cache_event_source); 82 } 83 }); 84 85 notify_register_dispatch("org.WebKit.lowMemory", &_notifyToken, 86 dispatch_get_main_queue(), ^(int) { memoryPressureHandler().respondToMemoryPressure();}); 87 88 m_installed = true; 89} 90 91void MemoryPressureHandler::uninstall() 92{ 93 if (!m_installed) 94 return; 95 96 dispatch_async(dispatch_get_main_queue(), ^{ 97 if (_cache_event_source) { 98 dispatch_source_cancel(_cache_event_source); 99 dispatch_release(_cache_event_source); 100 _cache_event_source = 0; 101 } 102 103 if (_timer_event_source) { 104 dispatch_source_cancel(_timer_event_source); 105 dispatch_release(_timer_event_source); 106 _timer_event_source = 0; 107 } 108 }); 109 110 m_installed = false; 111 112 notify_cancel(_notifyToken); 113} 114 115void MemoryPressureHandler::holdOff(unsigned seconds) 116{ 117 dispatch_async(dispatch_get_main_queue(), ^{ 118 _timer_event_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue()); 119 if (_timer_event_source) { 120 dispatch_set_context(_timer_event_source, this); 121 dispatch_source_set_timer(_timer_event_source, dispatch_time(DISPATCH_TIME_NOW, seconds * NSEC_PER_SEC), DISPATCH_TIME_FOREVER, 1 * s_minimumHoldOffTime); 122 dispatch_source_set_event_handler(_timer_event_source, ^{ 123 if (_timer_event_source) { 124 dispatch_source_cancel(_timer_event_source); 125 dispatch_release(_timer_event_source); 126 _timer_event_source = 0; 127 } 128 memoryPressureHandler().install(); 129 }); 130 dispatch_resume(_timer_event_source); 131 } 132 }); 133} 134 135void MemoryPressureHandler::respondToMemoryPressure() 136{ 137 uninstall(); 138 139 double startTime = monotonicallyIncreasingTime(); 140 141 m_lowMemoryHandler(false); 142 143 unsigned holdOffTime = (monotonicallyIncreasingTime() - startTime) * s_holdOffMultiplier; 144 145 holdOff(max(holdOffTime, s_minimumHoldOffTime)); 146} 147#endif // !PLATFORM(IOS) 148 149void MemoryPressureHandler::releaseMemory(bool) 150{ 151 int savedPageCacheCapacity = pageCache()->capacity(); 152 pageCache()->setCapacity(0); 153 pageCache()->setCapacity(savedPageCacheCapacity); 154 155 NSURLCache *nsurlCache = [NSURLCache sharedURLCache]; 156 NSUInteger savedNsurlCacheMemoryCapacity = [nsurlCache memoryCapacity]; 157 [nsurlCache setMemoryCapacity:0]; 158 [nsurlCache setMemoryCapacity:savedNsurlCacheMemoryCapacity]; 159 160 fontCache()->purgeInactiveFontData(); 161 162 memoryCache()->pruneToPercentage(0); 163 164 LayerPool::sharedPool()->drain(); 165 166 cssValuePool().drain(); 167 168 gcController().discardAllCompiledCode(); 169 170 // FastMalloc has lock-free thread specific caches that can only be cleared from the thread itself. 171 StorageThread::releaseFastMallocFreeMemoryInAllThreads(); 172#if ENABLE(WORKERS) 173 WorkerThread::releaseFastMallocFreeMemoryInAllThreads(); 174#endif 175#if ENABLE(THREADED_SCROLLING) 176 ScrollingThread::dispatch(bind(WTF::releaseFastMallocFreeMemory)); 177#endif 178 WTF::releaseFastMallocFreeMemory(); 179} 180#endif 181 182} // namespace WebCore 183