• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2007 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 COMPUTER, 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 COMPUTER, 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 "PageCache.h"
28 
29 #include "Cache.h"
30 #include "CachedPage.h"
31 #include "FrameLoader.h"
32 #include "HistoryItem.h"
33 #include "Logging.h"
34 #include "SystemTime.h"
35 #include <wtf/CurrentTime.h>
36 
37 using namespace std;
38 
39 namespace WebCore {
40 
41 static const double autoreleaseInterval = 3;
42 
pageCache()43 PageCache* pageCache()
44 {
45     static PageCache* staticPageCache = new PageCache;
46     return staticPageCache;
47 }
48 
PageCache()49 PageCache::PageCache()
50     : m_capacity(0)
51     , m_size(0)
52     , m_head(0)
53     , m_tail(0)
54     , m_autoreleaseTimer(this, &PageCache::releaseAutoreleasedPagesNowOrReschedule)
55 {
56 }
57 
setCapacity(int capacity)58 void PageCache::setCapacity(int capacity)
59 {
60     ASSERT(capacity >= 0);
61     m_capacity = max(capacity, 0);
62 
63     prune();
64 }
65 
frameCount() const66 int PageCache::frameCount() const
67 {
68     int frameCount = 0;
69     for (HistoryItem* current = m_head; current; current = current->m_next) {
70         ++frameCount;
71         ASSERT(current->m_cachedPage);
72         frameCount += current->m_cachedPage ? current->m_cachedPage->cachedMainFrame()->descendantFrameCount() : 0;
73     }
74 
75     return frameCount;
76 }
77 
autoreleasedPageCount() const78 int PageCache::autoreleasedPageCount() const
79 {
80     return m_autoreleaseSet.size();
81 }
82 
add(PassRefPtr<HistoryItem> prpItem,PassRefPtr<CachedPage> cachedPage)83 void PageCache::add(PassRefPtr<HistoryItem> prpItem, PassRefPtr<CachedPage> cachedPage)
84 {
85     ASSERT(prpItem);
86     ASSERT(cachedPage);
87 
88     HistoryItem* item = prpItem.releaseRef(); // Balanced in remove().
89 
90     // Remove stale cache entry if necessary.
91     if (item->m_cachedPage)
92         remove(item);
93 
94     item->m_cachedPage = cachedPage;
95     addToLRUList(item);
96     ++m_size;
97 
98     prune();
99 }
100 
remove(HistoryItem * item)101 void PageCache::remove(HistoryItem* item)
102 {
103     // Safely ignore attempts to remove items not in the cache.
104     if (!item || !item->m_cachedPage)
105         return;
106 
107     autorelease(item->m_cachedPage.release());
108     removeFromLRUList(item);
109     --m_size;
110 
111     item->deref(); // Balanced in add().
112 }
113 
prune()114 void PageCache::prune()
115 {
116     while (m_size > m_capacity) {
117         ASSERT(m_tail && m_tail->m_cachedPage);
118         remove(m_tail);
119     }
120 }
121 
addToLRUList(HistoryItem * item)122 void PageCache::addToLRUList(HistoryItem* item)
123 {
124     item->m_next = m_head;
125     item->m_prev = 0;
126 
127     if (m_head) {
128         ASSERT(m_tail);
129         m_head->m_prev = item;
130     } else {
131         ASSERT(!m_tail);
132         m_tail = item;
133     }
134 
135     m_head = item;
136 }
137 
removeFromLRUList(HistoryItem * item)138 void PageCache::removeFromLRUList(HistoryItem* item)
139 {
140     if (!item->m_next) {
141         ASSERT(item == m_tail);
142         m_tail = item->m_prev;
143     } else {
144         ASSERT(item != m_tail);
145         item->m_next->m_prev = item->m_prev;
146     }
147 
148     if (!item->m_prev) {
149         ASSERT(item == m_head);
150         m_head = item->m_next;
151     } else {
152         ASSERT(item != m_head);
153         item->m_prev->m_next = item->m_next;
154     }
155 }
156 
releaseAutoreleasedPagesNowOrReschedule(Timer<PageCache> * timer)157 void PageCache::releaseAutoreleasedPagesNowOrReschedule(Timer<PageCache>* timer)
158 {
159     double loadDelta = currentTime() - FrameLoader::timeOfLastCompletedLoad();
160     float userDelta = userIdleTime();
161 
162     // FIXME: <rdar://problem/5211190> This limit of 42 risks growing the page cache far beyond its nominal capacity.
163     if ((userDelta < 0.5 || loadDelta < 1.25) && m_autoreleaseSet.size() < 42) {
164         LOG(PageCache, "WebCorePageCache: Postponing releaseAutoreleasedPagesNowOrReschedule() - %f since last load, %f since last input, %i objects pending release", loadDelta, userDelta, m_autoreleaseSet.size());
165         timer->startOneShot(autoreleaseInterval);
166         return;
167     }
168 
169     LOG(PageCache, "WebCorePageCache: Releasing page caches - %f seconds since last load, %f since last input, %i objects pending release", loadDelta, userDelta, m_autoreleaseSet.size());
170     releaseAutoreleasedPagesNow();
171 }
172 
releaseAutoreleasedPagesNow()173 void PageCache::releaseAutoreleasedPagesNow()
174 {
175     m_autoreleaseTimer.stop();
176 
177     // Postpone dead pruning until all our resources have gone dead.
178     cache()->setPruneEnabled(false);
179 
180     CachedPageSet tmp;
181     tmp.swap(m_autoreleaseSet);
182 
183     CachedPageSet::iterator end = tmp.end();
184     for (CachedPageSet::iterator it = tmp.begin(); it != end; ++it)
185         (*it)->destroy();
186 
187     // Now do the prune.
188     cache()->setPruneEnabled(true);
189     cache()->prune();
190 }
191 
autorelease(PassRefPtr<CachedPage> page)192 void PageCache::autorelease(PassRefPtr<CachedPage> page)
193 {
194     ASSERT(page);
195     ASSERT(!m_autoreleaseSet.contains(page.get()));
196     m_autoreleaseSet.add(page);
197     if (!m_autoreleaseTimer.isActive())
198         m_autoreleaseTimer.startOneShot(autoreleaseInterval);
199 }
200 
201 } // namespace WebCore
202