OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "net/disk_cache/backend_impl.h" | 5 #include "net/disk_cache/backend_impl.h" |
6 | 6 |
7 #include "base/bind.h" | 7 #include "base/bind.h" |
8 #include "base/bind_helpers.h" | 8 #include "base/bind_helpers.h" |
9 #include "base/file_util.h" | 9 #include "base/file_util.h" |
10 #include "base/files/file_path.h" | 10 #include "base/files/file_path.h" |
11 #include "base/hash.h" | 11 #include "base/hash.h" |
12 #include "base/message_loop.h" | 12 #include "base/message_loop.h" |
13 #include "base/metrics/field_trial.h" | 13 #include "base/metrics/field_trial.h" |
14 #include "base/metrics/histogram.h" | 14 #include "base/metrics/histogram.h" |
15 #include "base/metrics/stats_counters.h" | 15 #include "base/metrics/stats_counters.h" |
16 #include "base/rand_util.h" | 16 #include "base/rand_util.h" |
17 #include "base/string_util.h" | 17 #include "base/string_util.h" |
18 #include "base/stringprintf.h" | 18 #include "base/stringprintf.h" |
19 #include "base/sys_info.h" | 19 #include "base/sys_info.h" |
20 #include "base/threading/thread_restrictions.h" | 20 #include "base/threading/thread_restrictions.h" |
21 #include "base/threading/worker_pool.h" | |
22 #include "base/time.h" | 21 #include "base/time.h" |
23 #include "base/timer.h" | 22 #include "base/timer.h" |
24 #include "net/base/net_errors.h" | 23 #include "net/base/net_errors.h" |
25 #include "net/disk_cache/cache_util.h" | 24 #include "net/disk_cache/cache_util.h" |
26 #include "net/disk_cache/entry_impl.h" | 25 #include "net/disk_cache/entry_impl.h" |
27 #include "net/disk_cache/errors.h" | 26 #include "net/disk_cache/errors.h" |
28 #include "net/disk_cache/experiments.h" | 27 #include "net/disk_cache/experiments.h" |
29 #include "net/disk_cache/file.h" | 28 #include "net/disk_cache/file.h" |
30 #include "net/disk_cache/mem_backend_impl.h" | |
31 #include "net/disk_cache/simple/simple_backend_impl.h" | |
32 | 29 |
33 // This has to be defined before including histogram_macros.h from this file. | 30 // This has to be defined before including histogram_macros.h from this file. |
34 #define NET_DISK_CACHE_BACKEND_IMPL_CC_ | 31 #define NET_DISK_CACHE_BACKEND_IMPL_CC_ |
35 #include "net/disk_cache/histogram_macros.h" | 32 #include "net/disk_cache/histogram_macros.h" |
36 | 33 |
37 using base::Time; | 34 using base::Time; |
38 using base::TimeDelta; | 35 using base::TimeDelta; |
39 using base::TimeTicks; | 36 using base::TimeTicks; |
40 | 37 |
41 namespace { | 38 namespace { |
42 | 39 |
43 const char* kIndexName = "index"; | 40 const char* kIndexName = "index"; |
44 const int kMaxOldFolders = 100; | |
45 | 41 |
46 // Seems like ~240 MB correspond to less than 50k entries for 99% of the people. | 42 // Seems like ~240 MB correspond to less than 50k entries for 99% of the people. |
47 // Note that the actual target is to keep the index table load factor under 55% | 43 // Note that the actual target is to keep the index table load factor under 55% |
48 // for most users. | 44 // for most users. |
49 const int k64kEntriesStore = 240 * 1000 * 1000; | 45 const int k64kEntriesStore = 240 * 1000 * 1000; |
50 const int kBaseTableLen = 64 * 1024; | 46 const int kBaseTableLen = 64 * 1024; |
51 const int kDefaultCacheSize = 80 * 1024 * 1024; | 47 const int kDefaultCacheSize = 80 * 1024 * 1024; |
52 | 48 |
53 // Avoid trimming the cache for the first 5 minutes (10 timer ticks). | 49 // Avoid trimming the cache for the first 5 minutes (10 timer ticks). |
54 const int kTrimDelay = 10; | 50 const int kTrimDelay = 10; |
(...skipping 16 matching lines...) Expand all Loading... |
71 return table_len * (k64kEntriesStore / kBaseTableLen); | 67 return table_len * (k64kEntriesStore / kBaseTableLen); |
72 } | 68 } |
73 | 69 |
74 size_t GetIndexSize(int table_len) { | 70 size_t GetIndexSize(int table_len) { |
75 size_t table_size = sizeof(disk_cache::CacheAddr) * table_len; | 71 size_t table_size = sizeof(disk_cache::CacheAddr) * table_len; |
76 return sizeof(disk_cache::IndexHeader) + table_size; | 72 return sizeof(disk_cache::IndexHeader) + table_size; |
77 } | 73 } |
78 | 74 |
79 // ------------------------------------------------------------------------ | 75 // ------------------------------------------------------------------------ |
80 | 76 |
81 // Returns a fully qualified name from path and name, using a given name prefix | |
82 // and index number. For instance, if the arguments are "/foo", "bar" and 5, it | |
83 // will return "/foo/old_bar_005". | |
84 base::FilePath GetPrefixedName(const base::FilePath& path, | |
85 const std::string& name, | |
86 int index) { | |
87 std::string tmp = base::StringPrintf("%s%s_%03d", "old_", | |
88 name.c_str(), index); | |
89 return path.AppendASCII(tmp); | |
90 } | |
91 | |
92 // This is a simple callback to cleanup old caches. | |
93 void CleanupCallback(const base::FilePath& path, const std::string& name) { | |
94 for (int i = 0; i < kMaxOldFolders; i++) { | |
95 base::FilePath to_delete = GetPrefixedName(path, name, i); | |
96 disk_cache::DeleteCache(to_delete, true); | |
97 } | |
98 } | |
99 | |
100 // Returns a full path to rename the current cache, in order to delete it. path | |
101 // is the current folder location, and name is the current folder name. | |
102 base::FilePath GetTempCacheName(const base::FilePath& path, | |
103 const std::string& name) { | |
104 // We'll attempt to have up to kMaxOldFolders folders for deletion. | |
105 for (int i = 0; i < kMaxOldFolders; i++) { | |
106 base::FilePath to_delete = GetPrefixedName(path, name, i); | |
107 if (!file_util::PathExists(to_delete)) | |
108 return to_delete; | |
109 } | |
110 return base::FilePath(); | |
111 } | |
112 | |
113 // Moves the cache files to a new folder and creates a task to delete them. | |
114 bool DelayedCacheCleanup(const base::FilePath& full_path) { | |
115 // GetTempCacheName() and MoveCache() use synchronous file | |
116 // operations. | |
117 base::ThreadRestrictions::ScopedAllowIO allow_io; | |
118 | |
119 base::FilePath current_path = full_path.StripTrailingSeparators(); | |
120 | |
121 base::FilePath path = current_path.DirName(); | |
122 base::FilePath name = current_path.BaseName(); | |
123 #if defined(OS_POSIX) | |
124 std::string name_str = name.value(); | |
125 #elif defined(OS_WIN) | |
126 // We created this file so it should only contain ASCII. | |
127 std::string name_str = WideToASCII(name.value()); | |
128 #endif | |
129 | |
130 base::FilePath to_delete = GetTempCacheName(path, name_str); | |
131 if (to_delete.empty()) { | |
132 LOG(ERROR) << "Unable to get another cache folder"; | |
133 return false; | |
134 } | |
135 | |
136 if (!disk_cache::MoveCache(full_path, to_delete)) { | |
137 LOG(ERROR) << "Unable to move cache folder " << full_path.value() << " to " | |
138 << to_delete.value(); | |
139 return false; | |
140 } | |
141 | |
142 base::WorkerPool::PostTask( | |
143 FROM_HERE, base::Bind(&CleanupCallback, path, name_str), true); | |
144 return true; | |
145 } | |
146 | |
147 // Sets group for the current experiment. Returns false if the files should be | 77 // Sets group for the current experiment. Returns false if the files should be |
148 // discarded. | 78 // discarded. |
149 bool InitExperiment(disk_cache::IndexHeader* header) { | 79 bool InitExperiment(disk_cache::IndexHeader* header) { |
150 if (header->experiment == disk_cache::EXPERIMENT_OLD_FILE1 || | 80 if (header->experiment == disk_cache::EXPERIMENT_OLD_FILE1 || |
151 header->experiment == disk_cache::EXPERIMENT_OLD_FILE2) { | 81 header->experiment == disk_cache::EXPERIMENT_OLD_FILE2) { |
152 // Discard current cache. | 82 // Discard current cache. |
153 return false; | 83 return false; |
154 } | 84 } |
155 | 85 |
156 header->experiment = disk_cache::NO_EXPERIMENT; | 86 header->experiment = disk_cache::NO_EXPERIMENT; |
157 return true; | 87 return true; |
158 } | 88 } |
159 | 89 |
160 // ------------------------------------------------------------------------ | |
161 | |
162 // This class takes care of building an instance of the backend. | |
163 class CacheCreator { | |
164 public: | |
165 CacheCreator(const base::FilePath& path, bool force, int max_bytes, | |
166 net::CacheType type, uint32 flags, | |
167 base::MessageLoopProxy* thread, net::NetLog* net_log, | |
168 disk_cache::Backend** backend, | |
169 const net::CompletionCallback& callback) | |
170 : path_(path), | |
171 force_(force), | |
172 retry_(false), | |
173 max_bytes_(max_bytes), | |
174 type_(type), | |
175 flags_(flags), | |
176 thread_(thread), | |
177 backend_(backend), | |
178 callback_(callback), | |
179 cache_(NULL), | |
180 net_log_(net_log) { | |
181 } | |
182 ~CacheCreator() {} | |
183 | |
184 // Creates the backend. | |
185 int Run(); | |
186 | |
187 private: | |
188 void DoCallback(int result); | |
189 | |
190 // Callback implementation. | |
191 void OnIOComplete(int result); | |
192 | |
193 const base::FilePath& path_; | |
194 bool force_; | |
195 bool retry_; | |
196 int max_bytes_; | |
197 net::CacheType type_; | |
198 uint32 flags_; | |
199 scoped_refptr<base::MessageLoopProxy> thread_; | |
200 disk_cache::Backend** backend_; | |
201 net::CompletionCallback callback_; | |
202 disk_cache::BackendImpl* cache_; | |
203 net::NetLog* net_log_; | |
204 | |
205 DISALLOW_COPY_AND_ASSIGN(CacheCreator); | |
206 }; | |
207 | |
208 int CacheCreator::Run() { | |
209 cache_ = new disk_cache::BackendImpl(path_, thread_, net_log_); | |
210 cache_->SetMaxSize(max_bytes_); | |
211 cache_->SetType(type_); | |
212 cache_->SetFlags(flags_); | |
213 int rv = cache_->Init( | |
214 base::Bind(&CacheCreator::OnIOComplete, base::Unretained(this))); | |
215 DCHECK_EQ(net::ERR_IO_PENDING, rv); | |
216 return rv; | |
217 } | |
218 | |
219 void CacheCreator::DoCallback(int result) { | |
220 DCHECK_NE(net::ERR_IO_PENDING, result); | |
221 if (result == net::OK) { | |
222 *backend_ = cache_; | |
223 } else { | |
224 LOG(ERROR) << "Unable to create cache"; | |
225 *backend_ = NULL; | |
226 delete cache_; | |
227 } | |
228 callback_.Run(result); | |
229 delete this; | |
230 } | |
231 | |
232 void CacheCreator::OnIOComplete(int result) { | |
233 if (result == net::OK || !force_ || retry_) | |
234 return DoCallback(result); | |
235 | |
236 // This is a failure and we are supposed to try again, so delete the object, | |
237 // delete all the files, and try again. | |
238 retry_ = true; | |
239 delete cache_; | |
240 cache_ = NULL; | |
241 if (!DelayedCacheCleanup(path_)) | |
242 return DoCallback(result); | |
243 | |
244 // The worker thread will start deleting files soon, but the original folder | |
245 // is not there anymore... let's create a new set of files. | |
246 int rv = Run(); | |
247 DCHECK_EQ(net::ERR_IO_PENDING, rv); | |
248 } | |
249 | |
250 // A callback to perform final cleanup on the background thread. | 90 // A callback to perform final cleanup on the background thread. |
251 void FinalCleanupCallback(disk_cache::BackendImpl* backend) { | 91 void FinalCleanupCallback(disk_cache::BackendImpl* backend) { |
252 backend->CleanupCache(); | 92 backend->CleanupCache(); |
253 } | 93 } |
254 | 94 |
255 } // namespace | 95 } // namespace |
256 | 96 |
257 // ------------------------------------------------------------------------ | 97 // ------------------------------------------------------------------------ |
258 | 98 |
259 namespace disk_cache { | 99 namespace disk_cache { |
260 | 100 |
261 int CreateCacheBackend(net::CacheType type, const base::FilePath& path, | |
262 int max_bytes, | |
263 bool force, base::MessageLoopProxy* thread, | |
264 net::NetLog* net_log, Backend** backend, | |
265 const net::CompletionCallback& callback) { | |
266 // TODO(pasko): Separate out cache creation when landing cache tracer. | |
267 DCHECK(!callback.is_null()); | |
268 if (type == net::MEMORY_CACHE) { | |
269 *backend = MemBackendImpl::CreateBackend(max_bytes, net_log); | |
270 return *backend ? net::OK : net::ERR_FAILED; | |
271 } | |
272 DCHECK(thread); | |
273 | |
274 #if defined(USE_SIMPLE_CACHE_BACKEND) | |
275 // TODO(gavinp,pasko): While simple backend development proceeds, we're only | |
276 // testing it against net::DISK_CACHE. Turn it on for more cache types as | |
277 // appropriate. | |
278 if (type == net::DISK_CACHE) { | |
279 return SimpleBackendImpl::CreateBackend(path, force, max_bytes, type, kNone, | |
280 thread, net_log, backend, callback); | |
281 } | |
282 #endif | |
283 return BackendImpl::CreateBackend(path, force, max_bytes, type, kNone, | |
284 thread, net_log, backend, callback); | |
285 } | |
286 | |
287 // Returns the preferred maximum number of bytes for the cache given the | 101 // Returns the preferred maximum number of bytes for the cache given the |
288 // number of available bytes. | 102 // number of available bytes. |
289 int PreferedCacheSize(int64 available) { | 103 int PreferedCacheSize(int64 available) { |
290 // Return 80% of the available space if there is not enough space to use | 104 // Return 80% of the available space if there is not enough space to use |
291 // kDefaultCacheSize. | 105 // kDefaultCacheSize. |
292 if (available < kDefaultCacheSize * 10 / 8) | 106 if (available < kDefaultCacheSize * 10 / 8) |
293 return static_cast<int32>(available * 8 / 10); | 107 return static_cast<int32>(available * 8 / 10); |
294 | 108 |
295 // Return kDefaultCacheSize if it uses 80% to 10% of the available space. | 109 // Return kDefaultCacheSize if it uses 80% to 10% of the available space. |
296 if (available < kDefaultCacheSize * 10) | 110 if (available < kDefaultCacheSize * 10) |
(...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
382 CleanupCache(); | 196 CleanupCache(); |
383 } else { | 197 } else { |
384 background_queue_.background_thread()->PostTask( | 198 background_queue_.background_thread()->PostTask( |
385 FROM_HERE, base::Bind(&FinalCleanupCallback, base::Unretained(this))); | 199 FROM_HERE, base::Bind(&FinalCleanupCallback, base::Unretained(this))); |
386 // http://crbug.com/74623 | 200 // http://crbug.com/74623 |
387 base::ThreadRestrictions::ScopedAllowWait allow_wait; | 201 base::ThreadRestrictions::ScopedAllowWait allow_wait; |
388 done_.Wait(); | 202 done_.Wait(); |
389 } | 203 } |
390 } | 204 } |
391 | 205 |
392 // If the initialization of the cache fails, and force is true, we will discard | |
393 // the whole cache and create a new one. In order to process a potentially large | |
394 // number of files, we'll rename the cache folder to old_ + original_name + | |
395 // number, (located on the same parent folder), and spawn a worker thread to | |
396 // delete all the files on all the stale cache folders. The whole process can | |
397 // still fail if we are not able to rename the cache folder (for instance due to | |
398 // a sharing violation), and in that case a cache for this profile (on the | |
399 // desired path) cannot be created. | |
400 // | |
401 // Static. | |
402 int BackendImpl::CreateBackend(const base::FilePath& full_path, bool force, | |
403 int max_bytes, net::CacheType type, | |
404 uint32 flags, base::MessageLoopProxy* thread, | |
405 net::NetLog* net_log, Backend** backend, | |
406 const CompletionCallback& callback) { | |
407 DCHECK(!callback.is_null()); | |
408 CacheCreator* creator = | |
409 new CacheCreator(full_path, force, max_bytes, type, flags, thread, | |
410 net_log, backend, callback); | |
411 // This object will self-destroy when finished. | |
412 return creator->Run(); | |
413 } | |
414 | |
415 int BackendImpl::Init(const CompletionCallback& callback) { | 206 int BackendImpl::Init(const CompletionCallback& callback) { |
416 background_queue_.Init(callback); | 207 background_queue_.Init(callback); |
417 return net::ERR_IO_PENDING; | 208 return net::ERR_IO_PENDING; |
418 } | 209 } |
419 | 210 |
420 int BackendImpl::SyncInit() { | 211 int BackendImpl::SyncInit() { |
421 #if defined(NET_BUILD_STRESS_CACHE) | 212 #if defined(NET_BUILD_STRESS_CACHE) |
422 // Start evictions right away. | 213 // Start evictions right away. |
423 up_ticks_ = kTrimDelay * 2; | 214 up_ticks_ = kTrimDelay * 2; |
424 #endif | 215 #endif |
(...skipping 1827 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2252 if (total_memory > kMaxBuffersSize || total_memory <= 0) | 2043 if (total_memory > kMaxBuffersSize || total_memory <= 0) |
2253 total_memory = kMaxBuffersSize; | 2044 total_memory = kMaxBuffersSize; |
2254 | 2045 |
2255 done = true; | 2046 done = true; |
2256 } | 2047 } |
2257 | 2048 |
2258 return static_cast<int>(total_memory); | 2049 return static_cast<int>(total_memory); |
2259 } | 2050 } |
2260 | 2051 |
2261 } // namespace disk_cache | 2052 } // namespace disk_cache |
OLD | NEW |