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

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())
sdefresne 2015/11/03 11:25:19 Please use brace as the "if" body is multi-line i
jbbegue 2015/11/03 13:37:19 Done.
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
148 [[NSNotificationCenter defaultCenter] 155 [[NSNotificationCenter defaultCenter]
149 addObserver:self 156 addObserver:self
150 selector:@selector(handleLowMemory) 157 selector:@selector(handleLowMemory)
151 name:UIApplicationDidReceiveMemoryWarningNotification 158 name:UIApplicationDidReceiveMemoryWarningNotification
152 object:nil]; 159 object:nil];
153 [[NSNotificationCenter defaultCenter] 160 [[NSNotificationCenter defaultCenter]
154 addObserver:self 161 addObserver:self
155 selector:@selector(handleEnterBackground) 162 selector:@selector(handleEnterBackground)
156 name:UIApplicationDidEnterBackgroundNotification 163 name:UIApplicationDidEnterBackgroundNotification
157 object:nil]; 164 object:nil];
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
197 204
198 - (void)retrieveImageForSessionID:(NSString*)sessionID 205 - (void)retrieveImageForSessionID:(NSString*)sessionID
199 callback:(void (^)(UIImage*))callback { 206 callback:(void (^)(UIImage*))callback {
200 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); 207 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI);
201 DCHECK(sessionID); 208 DCHECK(sessionID);
202 // iPad does not cache images, so if there is no callback we can avoid an 209 // iPad does not cache images, so if there is no callback we can avoid an
203 // expensive read from storage. 210 // expensive read from storage.
204 if (IsIPadIdiom() && !callback) 211 if (IsIPadIdiom() && !callback)
205 return; 212 return;
206 213
207 UIImage* img = [imageDictionary_ objectForKey:sessionID]; 214 UIImage* img = nil;
215 if (experimental_flags::IsLRUSnapshotCacheEnabled())
sdefresne 2015/11/03 11:25:19 This may be quite expensive (string comparison, ..
jbbegue 2015/11/03 13:37:19 Done.
216 img = [lruCache_ objectForKey:sessionID];
217 else
218 img = [imageDictionary_ objectForKey:sessionID];
219
208 if (img) { 220 if (img) {
209 if (callback) 221 if (callback)
210 callback(img); 222 callback(img);
211 return; 223 return;
212 } 224 }
213 225
214 base::PostTaskAndReplyWithResult( 226 base::PostTaskAndReplyWithResult(
215 web::WebThread::GetTaskRunnerForThread(web::WebThread::FILE_USER_BLOCKING) 227 web::WebThread::GetTaskRunnerForThread(web::WebThread::FILE_USER_BLOCKING)
216 .get(), 228 .get(),
217 FROM_HERE, base::BindBlock(^base::scoped_nsobject<UIImage>() { 229 FROM_HERE, base::BindBlock(^base::scoped_nsobject<UIImage>() {
218 // Retrieve the image on a high priority thread. 230 // Retrieve the image on a high priority thread.
219 return base::scoped_nsobject<UIImage>([ReadImageFromDisk( 231 return base::scoped_nsobject<UIImage>([ReadImageFromDisk(
220 [SnapshotCache imagePathForSessionID:sessionID]) retain]); 232 [SnapshotCache imagePathForSessionID:sessionID]) retain]);
221 }), 233 }),
222 base::BindBlock(^(base::scoped_nsobject<UIImage> image) { 234 base::BindBlock(^(base::scoped_nsobject<UIImage> image) {
223 // The iPad tab switcher is currently using its own memory cache so the 235 // 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. 236 // image is not stored in memory here if running on iPad.
225 // The same logic is used on image writes (code below). 237 // The same logic is used on image writes (code below).
226 if (!IsIPadIdiom() && image) 238 if (!IsIPadIdiom() && image) {
227 [imageDictionary_ setObject:image forKey:sessionID]; 239 if (experimental_flags::IsLRUSnapshotCacheEnabled())
240 [lruCache_ setObject:image forKey:sessionID];
241 else
242 [imageDictionary_ setObject:image forKey:sessionID];
243 }
228 if (callback) 244 if (callback)
229 callback(image); 245 callback(image);
230 })); 246 }));
231 } 247 }
232 248
233 - (void)setImage:(UIImage*)img withSessionID:(NSString*)sessionID { 249 - (void)setImage:(UIImage*)img withSessionID:(NSString*)sessionID {
234 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); 250 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI);
235 if (!img || !sessionID) 251 if (!img || !sessionID)
236 return; 252 return;
237 253
238 // The iPad tab switcher is currently using its own memory cache so the image 254 // 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. 255 // is not stored in memory here if running on iPad.
240 // The same logic is used on image reads (code above). 256 // The same logic is used on image reads (code above).
241 if (!IsIPadIdiom()) { 257 if (!IsIPadIdiom()) {
242 [imageDictionary_ setObject:img forKey:sessionID]; 258 if (experimental_flags::IsLRUSnapshotCacheEnabled())
259 [lruCache_ setObject:img forKey:sessionID];
260 else
261 [imageDictionary_ setObject:img forKey:sessionID];
243 } 262 }
244 // Save the image to disk. 263 // Save the image to disk.
245 web::WebThread::PostBlockingPoolSequencedTask( 264 web::WebThread::PostBlockingPoolSequencedTask(
246 kSequenceToken, FROM_HERE, 265 kSequenceToken, FROM_HERE,
247 base::BindBlock(^{ 266 base::BindBlock(^{
248 base::scoped_nsobject<UIImage> image([img retain]); 267 base::scoped_nsobject<UIImage> image([img retain]);
249 WriteImageToDisk(image, 268 WriteImageToDisk(image,
250 [SnapshotCache imagePathForSessionID:sessionID]); 269 [SnapshotCache imagePathForSessionID:sessionID]);
251 })); 270 }));
252 } 271 }
253 272
254 - (void)removeImageWithSessionID:(NSString*)sessionID { 273 - (void)removeImageWithSessionID:(NSString*)sessionID {
255 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); 274 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI);
256 [imageDictionary_ removeObjectForKey:sessionID]; 275 if (experimental_flags::IsLRUSnapshotCacheEnabled())
276 [lruCache_ removeObjectForKey:sessionID];
277 else
278 [imageDictionary_ removeObjectForKey:sessionID];
279
257 web::WebThread::PostBlockingPoolSequencedTask( 280 web::WebThread::PostBlockingPoolSequencedTask(
258 kSequenceToken, FROM_HERE, 281 kSequenceToken, FROM_HERE,
259 base::BindBlock(^{ 282 base::BindBlock(^{
260 base::FilePath imagePath = 283 base::FilePath imagePath =
261 [SnapshotCache imagePathForSessionID:sessionID]; 284 [SnapshotCache imagePathForSessionID:sessionID];
262 base::DeleteFile(imagePath, false); 285 base::DeleteFile(imagePath, false);
263 base::DeleteFile([SnapshotCache greyImagePathForSessionID:sessionID], 286 base::DeleteFile([SnapshotCache greyImagePathForSessionID:sessionID],
264 false); 287 false);
265 })); 288 }));
266 } 289 }
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after
347 base::DeleteFile(cur_file, false); 370 base::DeleteFile(cur_file, false);
348 } 371 }
349 })); 372 }));
350 } 373 }
351 374
352 - (void)willBeSavedGreyWhenBackgrounding:(NSString*)sessionID { 375 - (void)willBeSavedGreyWhenBackgrounding:(NSString*)sessionID {
353 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); 376 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI);
354 if (!sessionID) 377 if (!sessionID)
355 return; 378 return;
356 backgroundingImageSessionId_.reset([sessionID copy]); 379 backgroundingImageSessionId_.reset([sessionID copy]);
357 backgroundingColorImage_.reset( 380 if (experimental_flags::IsLRUSnapshotCacheEnabled())
358 [[imageDictionary_ objectForKey:sessionID] retain]); 381 backgroundingColorImage_.reset([[lruCache_ objectForKey:sessionID] retain]);
382 else
383 backgroundingColorImage_.reset(
384 [[imageDictionary_ objectForKey:sessionID] retain]);
359 } 385 }
360 386
361 - (void)handleLowMemory { 387 - (void)handleLowMemory {
362 DCHECK(!IsIPadIdiom()); 388 DCHECK(!IsIPadIdiom());
363 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); 389 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI);
364 NSMutableDictionary* dictionary = 390 NSMutableDictionary* dictionary =
365 [[NSMutableDictionary alloc] initWithCapacity:2]; 391 [[NSMutableDictionary alloc] initWithCapacity:2];
366 for (NSString* sessionID in pinnedIDs_) { 392 for (NSString* sessionID in pinnedIDs_) {
367 UIImage* image = [imageDictionary_ objectForKey:sessionID]; 393 UIImage* image = nil;
394 if (experimental_flags::IsLRUSnapshotCacheEnabled())
395 image = [lruCache_ objectForKey:sessionID];
396 else
397 image = [imageDictionary_ objectForKey:sessionID];
368 if (image) 398 if (image)
369 [dictionary setObject:image forKey:sessionID]; 399 [dictionary setObject:image forKey:sessionID];
370 } 400 }
371 imageDictionary_.reset(dictionary); 401 if (experimental_flags::IsLRUSnapshotCacheEnabled()) {
402 [lruCache_ removeAllObjects];
403 for (NSString* sessionID in pinnedIDs_)
404 [lruCache_ setObject:dictionary[sessionID] forKey:sessionID];
405 } else {
406 imageDictionary_.reset(dictionary);
407 }
372 } 408 }
373 409
374 - (void)handleEnterBackground { 410 - (void)handleEnterBackground {
375 DCHECK(!IsIPadIdiom()); 411 DCHECK(!IsIPadIdiom());
376 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); 412 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI);
377 [imageDictionary_ removeAllObjects]; 413 [imageDictionary_ removeAllObjects];
414 [lruCache_ removeAllObjects];
378 } 415 }
379 416
380 - (void)handleBecomeActive { 417 - (void)handleBecomeActive {
381 DCHECK(!IsIPadIdiom()); 418 DCHECK(!IsIPadIdiom());
382 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); 419 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI);
383 for (NSString* sessionID in pinnedIDs_) 420 for (NSString* sessionID in pinnedIDs_)
384 [self retrieveImageForSessionID:sessionID callback:nil]; 421 [self retrieveImageForSessionID:sessionID callback:nil];
385 } 422 }
386 423
387 - (void)saveGreyImage:(UIImage*)greyImage forKey:(NSString*)sessionID { 424 - (void)saveGreyImage:(UIImage*)greyImage forKey:(NSString*)sessionID {
388 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); 425 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI);
389 if (greyImage) 426 if (greyImage)
390 [greyImageDictionary_ setObject:greyImage forKey:sessionID]; 427 [greyImageDictionary_ setObject:greyImage forKey:sessionID];
391 if ([sessionID isEqualToString:mostRecentGreySessionId_]) { 428 if ([sessionID isEqualToString:mostRecentGreySessionId_]) {
392 mostRecentGreyBlock_.get()(greyImage); 429 mostRecentGreyBlock_.get()(greyImage);
393 [self clearGreySessionInfo]; 430 [self clearGreySessionInfo];
394 } 431 }
395 } 432 }
396 433
397 - (void)loadGreyImageAsync:(NSString*)sessionID { 434 - (void)loadGreyImageAsync:(NSString*)sessionID {
398 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); 435 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI);
399 // Don't call -retrieveImageForSessionID here because it caches the colored 436 // 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 437 // image, which we don't need for the grey image cache. But if the image is
401 // already in the cache, use it. 438 // already in the cache, use it.
402 UIImage* img = [imageDictionary_ objectForKey:sessionID]; 439 UIImage* img = nil;
440 if (experimental_flags::IsLRUSnapshotCacheEnabled())
441 img = [lruCache_ objectForKey:sessionID];
442 else
443 img = [imageDictionary_ objectForKey:sessionID];
444
403 base::PostTaskAndReplyWithResult( 445 base::PostTaskAndReplyWithResult(
404 web::WebThread::GetTaskRunnerForThread(web::WebThread::FILE_USER_BLOCKING) 446 web::WebThread::GetTaskRunnerForThread(web::WebThread::FILE_USER_BLOCKING)
405 .get(), 447 .get(),
406 FROM_HERE, base::BindBlock(^base::scoped_nsobject<UIImage>() { 448 FROM_HERE, base::BindBlock(^base::scoped_nsobject<UIImage>() {
407 base::scoped_nsobject<UIImage> result([img retain]); 449 base::scoped_nsobject<UIImage> result([img retain]);
408 // If the image is not in the cache, load it from disk. 450 // If the image is not in the cache, load it from disk.
409 if (!result) 451 if (!result)
410 result.reset([ReadImageFromDisk( 452 result.reset([ReadImageFromDisk(
411 [SnapshotCache imagePathForSessionID:sessionID]) retain]); 453 [SnapshotCache imagePathForSessionID:sessionID]) retain]);
412 if (result) 454 if (result)
(...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after
513 backgroundingImageSessionId_.reset(); 555 backgroundingImageSessionId_.reset();
514 } 556 }
515 } 557 }
516 558
517 web::WebThread::PostBlockingPoolTask( 559 web::WebThread::PostBlockingPoolTask(
518 FROM_HERE, base::Bind(&ConvertAndSaveGreyImage, colorImagePath, 560 FROM_HERE, base::Bind(&ConvertAndSaveGreyImage, colorImagePath,
519 greyImagePath, backgroundingColorImage_)); 561 greyImagePath, backgroundingColorImage_));
520 } 562 }
521 563
522 @end 564 @end
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698