Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1033)

Side by Side Diff: ios/chrome/browser/snapshots/snapshot_cache.mm

Issue 1410973008: Added an experiment for an LRU snapshot cache. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2012 The Chromium Authors. All rights reserved. 1 // Copyright 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 #import "ios/chrome/browser/snapshots/snapshot_cache.h" 5 #import "ios/chrome/browser/snapshots/snapshot_cache.h"
6 6
7 #import <UIKit/UIKit.h> 7 #import <UIKit/UIKit.h>
8 8
9 #include "base/critical_closure.h" 9 #include "base/critical_closure.h"
10 #include "base/files/file_enumerator.h" 10 #include "base/files/file_enumerator.h"
11 #include "base/files/file_path.h" 11 #include "base/files/file_path.h"
12 #include "base/files/file_util.h" 12 #include "base/files/file_util.h"
13 #include "base/location.h" 13 #include "base/location.h"
14 #include "base/logging.h" 14 #include "base/logging.h"
15 #include "base/mac/bind_objc_block.h" 15 #include "base/mac/bind_objc_block.h"
16 #include "base/mac/scoped_cftyperef.h" 16 #include "base/mac/scoped_cftyperef.h"
17 #include "base/strings/sys_string_conversions.h" 17 #include "base/strings/sys_string_conversions.h"
18 #include "base/task_runner_util.h" 18 #include "base/task_runner_util.h"
19 #include "base/threading/thread_restrictions.h" 19 #include "base/threading/thread_restrictions.h"
20 #include "ios/chrome/browser/experimental_flags.h"
20 #include "ios/chrome/browser/ui/ui_util.h" 21 #include "ios/chrome/browser/ui/ui_util.h"
21 #import "ios/chrome/browser/ui/uikit_ui_util.h" 22 #import "ios/chrome/browser/ui/uikit_ui_util.h"
22 #include "ios/web/public/web_thread.h" 23 #include "ios/web/public/web_thread.h"
23 24
24 @interface SnapshotCache () 25 @interface SnapshotCache ()
25 + (base::FilePath)imagePathForSessionID:(NSString*)sessionID; 26 + (base::FilePath)imagePathForSessionID:(NSString*)sessionID;
26 + (base::FilePath)greyImagePathForSessionID:(NSString*)sessionID; 27 + (base::FilePath)greyImagePathForSessionID:(NSString*)sessionID;
27 // Returns the directory where the thumbnails are saved. 28 // Returns the directory where the thumbnails are saved.
28 + (base::FilePath)cacheDirectory; 29 + (base::FilePath)cacheDirectory;
29 // Returns the directory where the thumbnails were stored in M28 and earlier. 30 // Returns the directory where the thumbnails were stored in M28 and earlier.
(...skipping 14 matching lines...) Expand all
44 @end 45 @end
45 46
46 namespace { 47 namespace {
47 static NSArray* const kSnapshotCacheDirectory = @[ @"Chromium", @"Snapshots" ]; 48 static NSArray* const kSnapshotCacheDirectory = @[ @"Chromium", @"Snapshots" ];
48 49
49 const NSUInteger kCacheInitialCapacity = 100; 50 const NSUInteger kCacheInitialCapacity = 100;
50 const NSUInteger kGreyInitialCapacity = 8; 51 const NSUInteger kGreyInitialCapacity = 8;
51 const CGFloat kJPEGImageQuality = 1.0; // Highest quality. No compression. 52 const CGFloat kJPEGImageQuality = 1.0; // Highest quality. No compression.
52 // Sequence token to make sure creation/deletion of snapshots don't overlap. 53 // Sequence token to make sure creation/deletion of snapshots don't overlap.
53 const char kSequenceToken[] = "SnapshotCacheSequenceToken"; 54 const char kSequenceToken[] = "SnapshotCacheSequenceToken";
55 // Maximum size in number of elements that the LRU cache can hold before
56 // starting to evict elements.
57 const NSUInteger kLRUCacheMaxCapacity = 6;
54 58
55 // The paths of the images saved to disk, given a cache directory. 59 // The paths of the images saved to disk, given a cache directory.
56 base::FilePath FilePathForSessionID(NSString* sessionID, 60 base::FilePath FilePathForSessionID(NSString* sessionID,
57 const base::FilePath& directory) { 61 const base::FilePath& directory) {
58 base::FilePath path = directory.Append(base::SysNSStringToUTF8(sessionID)) 62 base::FilePath path = directory.Append(base::SysNSStringToUTF8(sessionID))
59 .ReplaceExtension(".jpg"); 63 .ReplaceExtension(".jpg");
60 if ([SnapshotCache snapshotScaleForDevice] == 2.0) { 64 if ([SnapshotCache snapshotScaleForDevice] == 2.0) {
61 path = path.InsertBeforeExtension("@2x"); 65 path = path.InsertBeforeExtension("@2x");
62 } else if ([SnapshotCache snapshotScaleForDevice] == 3.0) { 66 } else if ([SnapshotCache snapshotScaleForDevice] == 3.0) {
63 path = path.InsertBeforeExtension("@3x"); 67 path = path.InsertBeforeExtension("@3x");
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after
134 static SnapshotCache* instance = [[SnapshotCache alloc] init]; 138 static SnapshotCache* instance = [[SnapshotCache alloc] init];
135 return instance; 139 return instance;
136 } 140 }
137 141
138 - (id)init { 142 - (id)init {
139 if ((self = [super init])) { 143 if ((self = [super init])) {
140 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); 144 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI);
141 propertyReleaser_SnapshotCache_.Init(self, [SnapshotCache class]); 145 propertyReleaser_SnapshotCache_.Init(self, [SnapshotCache class]);
142 146
143 if (!IsIPadIdiom()) { 147 if (!IsIPadIdiom()) {
144 // TODO(jbbegue): In the case where the cache grows, it is expensive. 148 if (experimental_flags::IsLRUSnapshotCacheEnabled()) {
145 // Make sure this doesn't suck when there are more than ten tabs. 149 lruCache_.reset(
146 imageDictionary_.reset( 150 [[LRUCache alloc] initWithCacheSize:kLRUCacheMaxCapacity]);
147 [[NSMutableDictionary alloc] initWithCapacity:kCacheInitialCapacity]); 151 } else {
152 imageDictionary_.reset([[NSMutableDictionary alloc]
153 initWithCapacity:kCacheInitialCapacity]);
154 }
155
148 [[NSNotificationCenter defaultCenter] 156 [[NSNotificationCenter defaultCenter]
149 addObserver:self 157 addObserver:self
150 selector:@selector(handleLowMemory) 158 selector:@selector(handleLowMemory)
151 name:UIApplicationDidReceiveMemoryWarningNotification 159 name:UIApplicationDidReceiveMemoryWarningNotification
152 object:nil]; 160 object:nil];
153 [[NSNotificationCenter defaultCenter] 161 [[NSNotificationCenter defaultCenter]
154 addObserver:self 162 addObserver:self
155 selector:@selector(handleEnterBackground) 163 selector:@selector(handleEnterBackground)
156 name:UIApplicationDidEnterBackgroundNotification 164 name:UIApplicationDidEnterBackgroundNotification
157 object:nil]; 165 object:nil];
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
197 205
198 - (void)retrieveImageForSessionID:(NSString*)sessionID 206 - (void)retrieveImageForSessionID:(NSString*)sessionID
199 callback:(void (^)(UIImage*))callback { 207 callback:(void (^)(UIImage*))callback {
200 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); 208 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI);
201 DCHECK(sessionID); 209 DCHECK(sessionID);
202 // iPad does not cache images, so if there is no callback we can avoid an 210 // iPad does not cache images, so if there is no callback we can avoid an
203 // expensive read from storage. 211 // expensive read from storage.
204 if (IsIPadIdiom() && !callback) 212 if (IsIPadIdiom() && !callback)
205 return; 213 return;
206 214
207 UIImage* img = [imageDictionary_ objectForKey:sessionID]; 215 UIImage* img = nil;
216 if (lruCache_)
217 img = [lruCache_ objectForKey:sessionID];
218 else
219 img = [imageDictionary_ objectForKey:sessionID];
220
208 if (img) { 221 if (img) {
209 if (callback) 222 if (callback)
210 callback(img); 223 callback(img);
211 return; 224 return;
212 } 225 }
213 226
214 base::PostTaskAndReplyWithResult( 227 base::PostTaskAndReplyWithResult(
215 web::WebThread::GetTaskRunnerForThread(web::WebThread::FILE_USER_BLOCKING) 228 web::WebThread::GetTaskRunnerForThread(web::WebThread::FILE_USER_BLOCKING)
216 .get(), 229 .get(),
217 FROM_HERE, base::BindBlock(^base::scoped_nsobject<UIImage>() { 230 FROM_HERE, base::BindBlock(^base::scoped_nsobject<UIImage>() {
218 // Retrieve the image on a high priority thread. 231 // Retrieve the image on a high priority thread.
219 return base::scoped_nsobject<UIImage>([ReadImageFromDisk( 232 return base::scoped_nsobject<UIImage>([ReadImageFromDisk(
220 [SnapshotCache imagePathForSessionID:sessionID]) retain]); 233 [SnapshotCache imagePathForSessionID:sessionID]) retain]);
221 }), 234 }),
222 base::BindBlock(^(base::scoped_nsobject<UIImage> image) { 235 base::BindBlock(^(base::scoped_nsobject<UIImage> image) {
223 // The iPad tab switcher is currently using its own memory cache so the 236 // The iPad tab switcher is currently using its own memory cache so the
224 // image is not stored in memory here if running on iPad. 237 // image is not stored in memory here if running on iPad.
225 // The same logic is used on image writes (code below). 238 // The same logic is used on image writes (code below).
226 if (!IsIPadIdiom() && image) 239 if (!IsIPadIdiom() && image) {
227 [imageDictionary_ setObject:image forKey:sessionID]; 240 if (lruCache_)
241 [lruCache_ setObject:image forKey:sessionID];
242 else
243 [imageDictionary_ setObject:image forKey:sessionID];
244 }
228 if (callback) 245 if (callback)
229 callback(image); 246 callback(image);
230 })); 247 }));
231 } 248 }
232 249
233 - (void)setImage:(UIImage*)img withSessionID:(NSString*)sessionID { 250 - (void)setImage:(UIImage*)img withSessionID:(NSString*)sessionID {
234 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); 251 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI);
235 if (!img || !sessionID) 252 if (!img || !sessionID)
236 return; 253 return;
237 254
238 // The iPad tab switcher is currently using its own memory cache so the image 255 // The iPad tab switcher is currently using its own memory cache so the image
239 // is not stored in memory here if running on iPad. 256 // is not stored in memory here if running on iPad.
240 // The same logic is used on image reads (code above). 257 // The same logic is used on image reads (code above).
241 if (!IsIPadIdiom()) { 258 if (!IsIPadIdiom()) {
242 [imageDictionary_ setObject:img forKey:sessionID]; 259 if (lruCache_)
260 [lruCache_ setObject:img forKey:sessionID];
261 else
262 [imageDictionary_ setObject:img forKey:sessionID];
243 } 263 }
244 // Save the image to disk. 264 // Save the image to disk.
245 web::WebThread::PostBlockingPoolSequencedTask( 265 web::WebThread::PostBlockingPoolSequencedTask(
246 kSequenceToken, FROM_HERE, 266 kSequenceToken, FROM_HERE,
247 base::BindBlock(^{ 267 base::BindBlock(^{
248 base::scoped_nsobject<UIImage> image([img retain]); 268 base::scoped_nsobject<UIImage> image([img retain]);
249 WriteImageToDisk(image, 269 WriteImageToDisk(image,
250 [SnapshotCache imagePathForSessionID:sessionID]); 270 [SnapshotCache imagePathForSessionID:sessionID]);
251 })); 271 }));
252 } 272 }
253 273
254 - (void)removeImageWithSessionID:(NSString*)sessionID { 274 - (void)removeImageWithSessionID:(NSString*)sessionID {
255 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); 275 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI);
256 [imageDictionary_ removeObjectForKey:sessionID]; 276 if (lruCache_)
277 [lruCache_ removeObjectForKey:sessionID];
278 else
279 [imageDictionary_ removeObjectForKey:sessionID];
280
257 web::WebThread::PostBlockingPoolSequencedTask( 281 web::WebThread::PostBlockingPoolSequencedTask(
258 kSequenceToken, FROM_HERE, 282 kSequenceToken, FROM_HERE,
259 base::BindBlock(^{ 283 base::BindBlock(^{
260 base::FilePath imagePath = 284 base::FilePath imagePath =
261 [SnapshotCache imagePathForSessionID:sessionID]; 285 [SnapshotCache imagePathForSessionID:sessionID];
262 base::DeleteFile(imagePath, false); 286 base::DeleteFile(imagePath, false);
263 base::DeleteFile([SnapshotCache greyImagePathForSessionID:sessionID], 287 base::DeleteFile([SnapshotCache greyImagePathForSessionID:sessionID],
264 false); 288 false);
265 })); 289 }));
266 } 290 }
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after
347 base::DeleteFile(cur_file, false); 371 base::DeleteFile(cur_file, false);
348 } 372 }
349 })); 373 }));
350 } 374 }
351 375
352 - (void)willBeSavedGreyWhenBackgrounding:(NSString*)sessionID { 376 - (void)willBeSavedGreyWhenBackgrounding:(NSString*)sessionID {
353 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); 377 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI);
354 if (!sessionID) 378 if (!sessionID)
355 return; 379 return;
356 backgroundingImageSessionId_.reset([sessionID copy]); 380 backgroundingImageSessionId_.reset([sessionID copy]);
357 backgroundingColorImage_.reset( 381 if (lruCache_) {
358 [[imageDictionary_ objectForKey:sessionID] retain]); 382 backgroundingColorImage_.reset([[lruCache_ objectForKey:sessionID] retain]);
383 } else {
384 backgroundingColorImage_.reset(
385 [[imageDictionary_ objectForKey:sessionID] retain]);
386 }
359 } 387 }
360 388
361 - (void)handleLowMemory { 389 - (void)handleLowMemory {
362 DCHECK(!IsIPadIdiom()); 390 DCHECK(!IsIPadIdiom());
363 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); 391 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI);
364 NSMutableDictionary* dictionary = 392 NSMutableDictionary* dictionary =
365 [[NSMutableDictionary alloc] initWithCapacity:2]; 393 [[NSMutableDictionary alloc] initWithCapacity:2];
366 for (NSString* sessionID in pinnedIDs_) { 394 for (NSString* sessionID in pinnedIDs_) {
367 UIImage* image = [imageDictionary_ objectForKey:sessionID]; 395 UIImage* image = nil;
396 if (lruCache_)
397 image = [lruCache_ objectForKey:sessionID];
398 else
399 image = [imageDictionary_ objectForKey:sessionID];
368 if (image) 400 if (image)
369 [dictionary setObject:image forKey:sessionID]; 401 [dictionary setObject:image forKey:sessionID];
370 } 402 }
371 imageDictionary_.reset(dictionary); 403 if (lruCache_) {
404 [lruCache_ removeAllObjects];
405 for (NSString* sessionID in pinnedIDs_)
406 [lruCache_ setObject:dictionary[sessionID] forKey:sessionID];
407 } else {
408 imageDictionary_.reset(dictionary);
409 }
372 } 410 }
373 411
374 - (void)handleEnterBackground { 412 - (void)handleEnterBackground {
375 DCHECK(!IsIPadIdiom()); 413 DCHECK(!IsIPadIdiom());
376 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); 414 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI);
377 [imageDictionary_ removeAllObjects]; 415 [imageDictionary_ removeAllObjects];
416 [lruCache_ removeAllObjects];
378 } 417 }
379 418
380 - (void)handleBecomeActive { 419 - (void)handleBecomeActive {
381 DCHECK(!IsIPadIdiom()); 420 DCHECK(!IsIPadIdiom());
382 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); 421 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI);
383 for (NSString* sessionID in pinnedIDs_) 422 for (NSString* sessionID in pinnedIDs_)
384 [self retrieveImageForSessionID:sessionID callback:nil]; 423 [self retrieveImageForSessionID:sessionID callback:nil];
385 } 424 }
386 425
387 - (void)saveGreyImage:(UIImage*)greyImage forKey:(NSString*)sessionID { 426 - (void)saveGreyImage:(UIImage*)greyImage forKey:(NSString*)sessionID {
388 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); 427 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI);
389 if (greyImage) 428 if (greyImage)
390 [greyImageDictionary_ setObject:greyImage forKey:sessionID]; 429 [greyImageDictionary_ setObject:greyImage forKey:sessionID];
391 if ([sessionID isEqualToString:mostRecentGreySessionId_]) { 430 if ([sessionID isEqualToString:mostRecentGreySessionId_]) {
392 mostRecentGreyBlock_.get()(greyImage); 431 mostRecentGreyBlock_.get()(greyImage);
393 [self clearGreySessionInfo]; 432 [self clearGreySessionInfo];
394 } 433 }
395 } 434 }
396 435
397 - (void)loadGreyImageAsync:(NSString*)sessionID { 436 - (void)loadGreyImageAsync:(NSString*)sessionID {
398 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); 437 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI);
399 // Don't call -retrieveImageForSessionID here because it caches the colored 438 // Don't call -retrieveImageForSessionID here because it caches the colored
400 // image, which we don't need for the grey image cache. But if the image is 439 // image, which we don't need for the grey image cache. But if the image is
401 // already in the cache, use it. 440 // already in the cache, use it.
402 UIImage* img = [imageDictionary_ objectForKey:sessionID]; 441 UIImage* img = nil;
442 if (lruCache_)
443 img = [lruCache_ objectForKey:sessionID];
444 else
445 img = [imageDictionary_ objectForKey:sessionID];
446
403 base::PostTaskAndReplyWithResult( 447 base::PostTaskAndReplyWithResult(
404 web::WebThread::GetTaskRunnerForThread(web::WebThread::FILE_USER_BLOCKING) 448 web::WebThread::GetTaskRunnerForThread(web::WebThread::FILE_USER_BLOCKING)
405 .get(), 449 .get(),
406 FROM_HERE, base::BindBlock(^base::scoped_nsobject<UIImage>() { 450 FROM_HERE, base::BindBlock(^base::scoped_nsobject<UIImage>() {
407 base::scoped_nsobject<UIImage> result([img retain]); 451 base::scoped_nsobject<UIImage> result([img retain]);
408 // If the image is not in the cache, load it from disk. 452 // If the image is not in the cache, load it from disk.
409 if (!result) 453 if (!result)
410 result.reset([ReadImageFromDisk( 454 result.reset([ReadImageFromDisk(
411 [SnapshotCache imagePathForSessionID:sessionID]) retain]); 455 [SnapshotCache imagePathForSessionID:sessionID]) retain]);
412 if (result) 456 if (result)
(...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after
513 backgroundingImageSessionId_.reset(); 557 backgroundingImageSessionId_.reset();
514 } 558 }
515 } 559 }
516 560
517 web::WebThread::PostBlockingPoolTask( 561 web::WebThread::PostBlockingPoolTask(
518 FROM_HERE, base::Bind(&ConvertAndSaveGreyImage, colorImagePath, 562 FROM_HERE, base::Bind(&ConvertAndSaveGreyImage, colorImagePath,
519 greyImagePath, backgroundingColorImage_)); 563 greyImagePath, backgroundingColorImage_));
520 } 564 }
521 565
522 @end 566 @end
567
568 @implementation SnapshotCache (TestingAdditions)
569
570 - (BOOL)hasImageInMemory:(NSString*)sessionID {
571 if (experimental_flags::IsLRUSnapshotCacheEnabled())
572 return [lruCache_ objectForKey:sessionID] != nil;
573 else
574 return [imageDictionary_ objectForKey:sessionID] != nil;
575 }
576 - (BOOL)hasGreyImageInMemory:(NSString*)sessionID {
577 return [greyImageDictionary_ objectForKey:sessionID] != nil;
578 }
579
580 - (NSUInteger)lruCacheMaxSize {
581 return [lruCache_ maxCacheSize];
582 }
583
584 @end
OLDNEW
« no previous file with comments | « ios/chrome/browser/snapshots/snapshot_cache.h ('k') | ios/chrome/browser/snapshots/snapshot_cache_unittest.mm » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698