1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* vim: set ts=8 sts=4 et sw=4 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/Attributes.h"
8 #include "mozilla/DebugOnly.h"
9 #include "mozilla/Util.h"
11 #include "necko-config.h"
14 #include "nsCacheService.h"
15 #include "nsCacheRequest.h"
16 #include "nsCacheEntry.h"
17 #include "nsCacheEntryDescriptor.h"
18 #include "nsCacheDevice.h"
19 #include "nsMemoryCacheDevice.h"
20 #include "nsICacheVisitor.h"
21 #include "nsDiskCacheDevice.h"
22 #include "nsDiskCacheDeviceSQL.h"
24 #include "nsIObserverService.h"
25 #include "nsIPrefService.h"
26 #include "nsIPrefBranch.h"
28 #include "nsIOService.h"
29 #include "nsDirectoryServiceDefs.h"
30 #include "nsAppDirectoryServiceDefs.h"
31 #include "nsThreadUtils.h"
32 #include "nsProxyRelease.h"
33 #include "nsVoidArray.h"
34 #include "nsDeleteDir.h"
36 #include <math.h> // for log()
37 #include "mozilla/Services.h"
41 #include "mozilla/net/NeckoCommon.h"
44 using namespace mozilla
;
46 /******************************************************************************
47 * nsCacheProfilePrefObserver
48 *****************************************************************************/
49 #define DISK_CACHE_ENABLE_PREF "browser.cache.disk.enable"
50 #define DISK_CACHE_DIR_PREF "browser.cache.disk.parent_directory"
51 #define DISK_CACHE_SMART_SIZE_FIRST_RUN_PREF\
52 "browser.cache.disk.smart_size.first_run"
53 #define DISK_CACHE_SMART_SIZE_ENABLED_PREF \
54 "browser.cache.disk.smart_size.enabled"
55 #define DISK_CACHE_SMART_SIZE_PREF "browser.cache.disk.smart_size_cached_value"
56 #define DISK_CACHE_CAPACITY_PREF "browser.cache.disk.capacity"
57 #define DISK_CACHE_MAX_ENTRY_SIZE_PREF "browser.cache.disk.max_entry_size"
58 #define DISK_CACHE_CAPACITY 256000
60 #define DISK_CACHE_USE_OLD_MAX_SMART_SIZE_PREF \
61 "browser.cache.disk.smart_size.use_old_max"
63 #define OFFLINE_CACHE_ENABLE_PREF "browser.cache.offline.enable"
64 #define OFFLINE_CACHE_DIR_PREF "browser.cache.offline.parent_directory"
65 #define OFFLINE_CACHE_CAPACITY_PREF "browser.cache.offline.capacity"
66 #define OFFLINE_CACHE_CAPACITY 512000
68 #define MEMORY_CACHE_ENABLE_PREF "browser.cache.memory.enable"
69 #define MEMORY_CACHE_CAPACITY_PREF "browser.cache.memory.capacity"
70 #define MEMORY_CACHE_MAX_ENTRY_SIZE_PREF "browser.cache.memory.max_entry_size"
72 #define CACHE_COMPRESSION_LEVEL_PREF "browser.cache.compression_level"
73 #define CACHE_COMPRESSION_LEVEL 1
75 #define SANITIZE_ON_SHUTDOWN_PREF "privacy.sanitize.sanitizeOnShutdown"
76 #define CLEAR_ON_SHUTDOWN_PREF "privacy.clearOnShutdown.cache"
78 static const char * observerList
[] = {
79 "profile-before-change",
81 NS_XPCOM_SHUTDOWN_OBSERVER_ID
,
82 "last-pb-context-exited",
83 "suspend_process_notification",
84 "resume_process_notification"
87 static const char * prefList
[] = {
88 DISK_CACHE_ENABLE_PREF
,
89 DISK_CACHE_SMART_SIZE_ENABLED_PREF
,
90 DISK_CACHE_CAPACITY_PREF
,
92 DISK_CACHE_MAX_ENTRY_SIZE_PREF
,
93 DISK_CACHE_USE_OLD_MAX_SMART_SIZE_PREF
,
94 OFFLINE_CACHE_ENABLE_PREF
,
95 OFFLINE_CACHE_CAPACITY_PREF
,
96 OFFLINE_CACHE_DIR_PREF
,
97 MEMORY_CACHE_ENABLE_PREF
,
98 MEMORY_CACHE_CAPACITY_PREF
,
99 MEMORY_CACHE_MAX_ENTRY_SIZE_PREF
,
100 CACHE_COMPRESSION_LEVEL_PREF
,
101 SANITIZE_ON_SHUTDOWN_PREF
,
102 CLEAR_ON_SHUTDOWN_PREF
105 // Cache sizes, in KB
106 const int32_t DEFAULT_CACHE_SIZE
= 250 * 1024; // 250 MB
107 const int32_t MIN_CACHE_SIZE
= 50 * 1024; // 50 MB
109 const int32_t MAX_CACHE_SIZE
= 200 * 1024; // 200 MB
110 const int32_t OLD_MAX_CACHE_SIZE
= 200 * 1024; // 200 MB
112 const int32_t MAX_CACHE_SIZE
= 350 * 1024; // 350 MB
113 const int32_t OLD_MAX_CACHE_SIZE
= 1024 * 1024; // 1 GB
115 // Default cache size was 50 MB for many years until FF 4:
116 const int32_t PRE_GECKO_2_0_DEFAULT_CACHE_SIZE
= 50 * 1024;
118 class nsCacheProfilePrefObserver
: public nsIObserver
124 nsCacheProfilePrefObserver()
125 : mHaveProfile(false)
126 , mDiskCacheEnabled(false)
127 , mDiskCacheCapacity(0)
128 , mDiskCacheMaxEntrySize(-1) // -1 means "no limit"
129 , mSmartSizeEnabled(false)
130 , mShouldUseOldMaxSmartSize(false)
131 , mOfflineCacheEnabled(false)
132 , mOfflineCacheCapacity(0)
133 , mMemoryCacheEnabled(true)
134 , mMemoryCacheCapacity(-1)
135 , mMemoryCacheMaxEntrySize(-1) // -1 means "no limit"
136 , mCacheCompressionLevel(CACHE_COMPRESSION_LEVEL
)
137 , mSanitizeOnShutdown(false)
138 , mClearCacheOnShutdown(false)
142 virtual ~nsCacheProfilePrefObserver() {}
146 nsresult
ReadPrefs(nsIPrefBranch
* branch
);
148 bool DiskCacheEnabled();
149 int32_t DiskCacheCapacity() { return mDiskCacheCapacity
; }
150 void SetDiskCacheCapacity(int32_t);
151 int32_t DiskCacheMaxEntrySize() { return mDiskCacheMaxEntrySize
; }
152 nsIFile
* DiskCacheParentDirectory() { return mDiskCacheParentDirectory
; }
153 bool SmartSizeEnabled() { return mSmartSizeEnabled
; }
155 bool ShouldUseOldMaxSmartSize() { return mShouldUseOldMaxSmartSize
; }
156 void SetUseNewMaxSmartSize(bool useNew
) { mShouldUseOldMaxSmartSize
= !useNew
; }
158 bool OfflineCacheEnabled();
159 int32_t OfflineCacheCapacity() { return mOfflineCacheCapacity
; }
160 nsIFile
* OfflineCacheParentDirectory() { return mOfflineCacheParentDirectory
; }
162 bool MemoryCacheEnabled();
163 int32_t MemoryCacheCapacity();
164 int32_t MemoryCacheMaxEntrySize() { return mMemoryCacheMaxEntrySize
; }
166 int32_t CacheCompressionLevel();
168 bool SanitizeAtShutdown() { return mSanitizeOnShutdown
&& mClearCacheOnShutdown
; }
170 static uint32_t GetSmartCacheSize(const nsAString
& cachePath
,
171 uint32_t currentSize
,
172 bool shouldUseOldMaxSmartSize
);
174 bool PermittedToSmartSize(nsIPrefBranch
*, bool firstRun
);
179 bool mDiskCacheEnabled
;
180 int32_t mDiskCacheCapacity
; // in kilobytes
181 int32_t mDiskCacheMaxEntrySize
; // in kilobytes
182 nsCOMPtr
<nsIFile
> mDiskCacheParentDirectory
;
183 bool mSmartSizeEnabled
;
185 bool mShouldUseOldMaxSmartSize
;
187 bool mOfflineCacheEnabled
;
188 int32_t mOfflineCacheCapacity
; // in kilobytes
189 nsCOMPtr
<nsIFile
> mOfflineCacheParentDirectory
;
191 bool mMemoryCacheEnabled
;
192 int32_t mMemoryCacheCapacity
; // in kilobytes
193 int32_t mMemoryCacheMaxEntrySize
; // in kilobytes
195 int32_t mCacheCompressionLevel
;
197 bool mSanitizeOnShutdown
;
198 bool mClearCacheOnShutdown
;
201 NS_IMPL_THREADSAFE_ISUPPORTS1(nsCacheProfilePrefObserver
, nsIObserver
)
203 class nsSetDiskSmartSizeCallback MOZ_FINAL
: public nsITimerCallback
208 NS_IMETHOD
Notify(nsITimer
* aTimer
) {
209 if (nsCacheService::gService
) {
210 nsCacheServiceAutoLock
autoLock(LOCK_TELEM(NSSETDISKSMARTSIZECALLBACK_NOTIFY
));
211 nsCacheService::gService
->SetDiskSmartSize_Locked();
212 nsCacheService::gService
->mSmartSizeTimer
= nullptr;
218 NS_IMPL_THREADSAFE_ISUPPORTS1(nsSetDiskSmartSizeCallback
, nsITimerCallback
)
220 // Runnable sent to main thread after the cache IO thread calculates available
221 // disk space, so that there is no race in setting mDiskCacheCapacity.
222 class nsSetSmartSizeEvent
: public nsRunnable
225 nsSetSmartSizeEvent(int32_t smartSize
)
226 : mSmartSize(smartSize
) {}
230 NS_ASSERTION(NS_IsMainThread(),
231 "Setting smart size data off the main thread");
233 // Main thread may have already called nsCacheService::Shutdown
234 if (!nsCacheService::IsInitialized())
235 return NS_ERROR_NOT_AVAILABLE
;
237 // Ensure smart sizing wasn't switched off while event was pending.
238 // It is safe to access the observer without the lock since we are
239 // on the main thread and the value changes only on the main thread.
240 if (!nsCacheService::gService
->mObserver
->SmartSizeEnabled())
243 nsCacheService::SetDiskCacheCapacity(mSmartSize
);
245 nsCOMPtr
<nsIPrefBranch
> ps
= do_GetService(NS_PREFSERVICE_CONTRACTID
);
247 NS_FAILED(ps
->SetIntPref(DISK_CACHE_SMART_SIZE_PREF
, mSmartSize
)))
248 NS_WARNING("Failed to set smart size pref");
258 // Runnable sent from main thread to cacheIO thread
259 class nsGetSmartSizeEvent
: public nsRunnable
262 nsGetSmartSizeEvent(const nsAString
& cachePath
, uint32_t currentSize
,
263 bool shouldUseOldMaxSmartSize
)
264 : mCachePath(cachePath
)
265 , mCurrentSize(currentSize
)
266 , mShouldUseOldMaxSmartSize(shouldUseOldMaxSmartSize
)
269 // Calculates user's disk space available on a background thread and
270 // dispatches this value back to the main thread.
274 size
= nsCacheProfilePrefObserver::GetSmartCacheSize(mCachePath
,
276 mShouldUseOldMaxSmartSize
);
277 NS_DispatchToMainThread(new nsSetSmartSizeEvent(size
));
283 uint32_t mCurrentSize
;
284 bool mShouldUseOldMaxSmartSize
;
287 class nsBlockOnCacheThreadEvent
: public nsRunnable
{
289 nsBlockOnCacheThreadEvent()
294 nsCacheServiceAutoLock
autoLock(LOCK_TELEM(NSBLOCKONCACHETHREADEVENT_RUN
));
296 CACHE_LOG_DEBUG(("nsBlockOnCacheThreadEvent [%p]\n", this));
298 nsCacheService::gService
->mCondVar
.Notify();
305 nsCacheProfilePrefObserver::Install()
307 // install profile-change observer
308 nsCOMPtr
<nsIObserverService
> observerService
=
309 mozilla::services::GetObserverService();
310 if (!observerService
)
311 return NS_ERROR_FAILURE
;
313 nsresult rv
, rv2
= NS_OK
;
314 for (unsigned int i
=0; i
<ArrayLength(observerList
); i
++) {
315 rv
= observerService
->AddObserver(this, observerList
[i
], false);
320 // install preferences observer
321 nsCOMPtr
<nsIPrefBranch
> branch
= do_GetService(NS_PREFSERVICE_CONTRACTID
);
322 if (!branch
) return NS_ERROR_FAILURE
;
324 for (unsigned int i
=0; i
<ArrayLength(prefList
); i
++) {
325 rv
= branch
->AddObserver(prefList
[i
], this, false);
330 // Determine if we have a profile already
331 // Install() is called *after* the profile-after-change notification
332 // when there is only a single profile, or it is specified on the
333 // commandline at startup.
334 // In that case, we detect the presence of a profile by the existence
335 // of the NS_APP_USER_PROFILE_50_DIR directory.
337 nsCOMPtr
<nsIFile
> directory
;
338 rv
= NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR
,
339 getter_AddRefs(directory
));
340 if (NS_SUCCEEDED(rv
))
343 rv
= ReadPrefs(branch
);
344 NS_ENSURE_SUCCESS(rv
, rv
);
351 nsCacheProfilePrefObserver::Remove()
353 // remove Observer Service observers
354 nsCOMPtr
<nsIObserverService
> obs
=
355 mozilla::services::GetObserverService();
357 for (unsigned int i
=0; i
<ArrayLength(observerList
); i
++) {
358 obs
->RemoveObserver(this, observerList
[i
]);
362 // remove Pref Service observers
363 nsCOMPtr
<nsIPrefBranch
> prefs
=
364 do_GetService(NS_PREFSERVICE_CONTRACTID
);
367 for (unsigned int i
=0; i
<ArrayLength(prefList
); i
++)
368 prefs
->RemoveObserver(prefList
[i
], this); // remove cache pref observers
372 nsCacheProfilePrefObserver::SetDiskCacheCapacity(int32_t capacity
)
374 mDiskCacheCapacity
= std::max(0, capacity
);
379 nsCacheProfilePrefObserver::Observe(nsISupports
* subject
,
381 const PRUnichar
* data_unicode
)
384 NS_ConvertUTF16toUTF8
data(data_unicode
);
385 CACHE_LOG_ALWAYS(("Observe [topic=%s data=%s]\n", topic
, data
.get()));
387 if (!nsCacheService::IsInitialized()) {
388 if (!strcmp("resume_process_notification", topic
)) {
389 // A suspended process has a closed cache, so re-open it here.
390 nsCacheService::GlobalInstance()->Init();
395 if (!strcmp(NS_XPCOM_SHUTDOWN_OBSERVER_ID
, topic
)) {
396 // xpcom going away, shutdown cache service
397 nsCacheService::GlobalInstance()->Shutdown();
398 } else if (!strcmp("profile-before-change", topic
)) {
399 // profile before change
400 mHaveProfile
= false;
402 // XXX shutdown devices
403 nsCacheService::OnProfileShutdown(!strcmp("shutdown-cleanse",
406 } else if (!strcmp("suspend_process_notification", topic
)) {
407 // A suspended process may never return, so shutdown the cache to reduce
409 nsCacheService::GlobalInstance()->Shutdown();
410 } else if (!strcmp("profile-do-change", topic
)) {
411 // profile after change
413 nsCOMPtr
<nsIPrefBranch
> branch
= do_GetService(NS_PREFSERVICE_CONTRACTID
);
415 nsCacheService::OnProfileChanged();
417 } else if (!strcmp(NS_PREFBRANCH_PREFCHANGE_TOPIC_ID
, topic
)) {
419 // ignore pref changes until we're done switch profiles
423 nsCOMPtr
<nsIPrefBranch
> branch
= do_QueryInterface(subject
, &rv
);
427 // which preference changed?
428 if (!strcmp(DISK_CACHE_ENABLE_PREF
, data
.get())) {
430 rv
= branch
->GetBoolPref(DISK_CACHE_ENABLE_PREF
,
434 nsCacheService::SetDiskCacheEnabled(DiskCacheEnabled());
436 } else if (!strcmp(DISK_CACHE_CAPACITY_PREF
, data
.get())) {
438 int32_t capacity
= 0;
439 rv
= branch
->GetIntPref(DISK_CACHE_CAPACITY_PREF
, &capacity
);
442 mDiskCacheCapacity
= std::max(0, capacity
);
443 nsCacheService::SetDiskCacheCapacity(mDiskCacheCapacity
);
445 // Update the cache capacity when smart sizing is turned on/off
446 } else if (!strcmp(DISK_CACHE_SMART_SIZE_ENABLED_PREF
, data
.get())) {
447 // Is the update because smartsizing was turned on, or off?
448 rv
= branch
->GetBoolPref(DISK_CACHE_SMART_SIZE_ENABLED_PREF
,
452 int32_t newCapacity
= 0;
453 if (mSmartSizeEnabled
) {
454 nsCacheService::SetDiskSmartSize();
456 // Smart sizing switched off: use user specified size
457 rv
= branch
->GetIntPref(DISK_CACHE_CAPACITY_PREF
, &newCapacity
);
460 mDiskCacheCapacity
= std::max(0, newCapacity
);
461 nsCacheService::SetDiskCacheCapacity(mDiskCacheCapacity
);
463 } else if (!strcmp(DISK_CACHE_USE_OLD_MAX_SMART_SIZE_PREF
, data
.get())) {
464 rv
= branch
->GetBoolPref(DISK_CACHE_USE_OLD_MAX_SMART_SIZE_PREF
,
465 &mShouldUseOldMaxSmartSize
);
468 } else if (!strcmp(DISK_CACHE_MAX_ENTRY_SIZE_PREF
, data
.get())) {
470 rv
= branch
->GetIntPref(DISK_CACHE_MAX_ENTRY_SIZE_PREF
,
475 mDiskCacheMaxEntrySize
= std::max(-1, newMaxSize
);
476 nsCacheService::SetDiskCacheMaxEntrySize(mDiskCacheMaxEntrySize
);
479 } else if (!strcmp(DISK_CACHE_DIR_PREF
, data
.get())) {
480 // XXX We probaby don't want to respond to this pref except after
481 // XXX profile changes. Ideally, there should be somekind of user
482 // XXX notification that the pref change won't take effect until
483 // XXX the next time the profile changes (browser launch)
487 // which preference changed?
488 if (!strcmp(OFFLINE_CACHE_ENABLE_PREF
, data
.get())) {
490 rv
= branch
->GetBoolPref(OFFLINE_CACHE_ENABLE_PREF
,
491 &mOfflineCacheEnabled
);
492 if (NS_FAILED(rv
)) return rv
;
493 nsCacheService::SetOfflineCacheEnabled(OfflineCacheEnabled());
495 } else if (!strcmp(OFFLINE_CACHE_CAPACITY_PREF
, data
.get())) {
497 int32_t capacity
= 0;
498 rv
= branch
->GetIntPref(OFFLINE_CACHE_CAPACITY_PREF
, &capacity
);
499 if (NS_FAILED(rv
)) return rv
;
500 mOfflineCacheCapacity
= std::max(0, capacity
);
501 nsCacheService::SetOfflineCacheCapacity(mOfflineCacheCapacity
);
503 } else if (!strcmp(OFFLINE_CACHE_DIR_PREF
, data
.get())) {
504 // XXX We probaby don't want to respond to this pref except after
505 // XXX profile changes. Ideally, there should be some kind of user
506 // XXX notification that the pref change won't take effect until
507 // XXX the next time the profile changes (browser launch)
511 if (!strcmp(MEMORY_CACHE_ENABLE_PREF
, data
.get())) {
513 rv
= branch
->GetBoolPref(MEMORY_CACHE_ENABLE_PREF
,
514 &mMemoryCacheEnabled
);
517 nsCacheService::SetMemoryCache();
519 } else if (!strcmp(MEMORY_CACHE_CAPACITY_PREF
, data
.get())) {
521 mMemoryCacheCapacity
= -1;
522 (void) branch
->GetIntPref(MEMORY_CACHE_CAPACITY_PREF
,
523 &mMemoryCacheCapacity
);
524 nsCacheService::SetMemoryCache();
525 } else if (!strcmp(MEMORY_CACHE_MAX_ENTRY_SIZE_PREF
, data
.get())) {
527 rv
= branch
->GetIntPref(MEMORY_CACHE_MAX_ENTRY_SIZE_PREF
,
532 mMemoryCacheMaxEntrySize
= std::max(-1, newMaxSize
);
533 nsCacheService::SetMemoryCacheMaxEntrySize(mMemoryCacheMaxEntrySize
);
534 } else if (!strcmp(CACHE_COMPRESSION_LEVEL_PREF
, data
.get())) {
535 mCacheCompressionLevel
= CACHE_COMPRESSION_LEVEL
;
536 (void)branch
->GetIntPref(CACHE_COMPRESSION_LEVEL_PREF
,
537 &mCacheCompressionLevel
);
538 mCacheCompressionLevel
= std::max(0, mCacheCompressionLevel
);
539 mCacheCompressionLevel
= std::min(9, mCacheCompressionLevel
);
540 } else if (!strcmp(SANITIZE_ON_SHUTDOWN_PREF
, data
.get())) {
541 rv
= branch
->GetBoolPref(SANITIZE_ON_SHUTDOWN_PREF
,
542 &mSanitizeOnShutdown
);
545 nsCacheService::SetDiskCacheEnabled(DiskCacheEnabled());
546 } else if (!strcmp(CLEAR_ON_SHUTDOWN_PREF
, data
.get())) {
547 rv
= branch
->GetBoolPref(CLEAR_ON_SHUTDOWN_PREF
,
548 &mClearCacheOnShutdown
);
551 nsCacheService::SetDiskCacheEnabled(DiskCacheEnabled());
553 } else if (!strcmp("last-pb-context-exited", topic
)) {
554 nsCacheService::LeavePrivateBrowsing();
560 // Returns default ("smart") size (in KB) of cache, given available disk space
563 SmartCacheSize(const uint32_t availKB
, bool shouldUseOldMaxSmartSize
)
565 uint32_t maxSize
= shouldUseOldMaxSmartSize
? OLD_MAX_CACHE_SIZE
: MAX_CACHE_SIZE
;
567 if (availKB
> 100 * 1024 * 1024)
568 return maxSize
; // skip computing if we're over 100 GB
570 // Grow/shrink in 10 MB units, deliberately, so that in the common case we
571 // don't shrink cache and evict items every time we startup (it's important
572 // that we don't slow down startup benchmarks).
573 uint32_t sz10MBs
= 0;
574 uint32_t avail10MBs
= availKB
/ (1024*10);
576 // .5% of space above 25 GB
577 if (avail10MBs
> 2500) {
578 sz10MBs
+= static_cast<uint32_t>((avail10MBs
- 2500)*.005);
581 // 1% of space between 7GB -> 25 GB
582 if (avail10MBs
> 700) {
583 sz10MBs
+= static_cast<uint32_t>((avail10MBs
- 700)*.01);
586 // 5% of space between 500 MB -> 7 GB
587 if (avail10MBs
> 50) {
588 sz10MBs
+= static_cast<uint32_t>((avail10MBs
- 50)*.05);
593 // On Android, smaller/older devices may have very little storage and
594 // device owners may be sensitive to storage footprint: Use a smaller
595 // percentage of available space and a smaller minimum.
597 // 20% of space up to 500 MB (10 MB min)
598 sz10MBs
+= std::max
<uint32_t>(1, static_cast<uint32_t>(avail10MBs
* .2));
600 // 40% of space up to 500 MB (50 MB min)
601 sz10MBs
+= std::max
<uint32_t>(5, static_cast<uint32_t>(avail10MBs
* .4));
604 return std::min
<uint32_t>(maxSize
, sz10MBs
* 10 * 1024);
607 /* Computes our best guess for the default size of the user's disk cache,
608 * based on the amount of space they have free on their hard drive.
609 * We use a tiered scheme: the more space available,
610 * the larger the disk cache will be. However, we do not want
611 * to enable the disk cache to grow to an unbounded size, so the larger the
612 * user's available space is, the smaller of a percentage we take. We set a
613 * lower bound of 50MB and an upper bound of 1GB.
616 *@return: The size that the user's disk cache should default to, in kBytes.
619 nsCacheProfilePrefObserver::GetSmartCacheSize(const nsAString
& cachePath
,
620 uint32_t currentSize
,
621 bool shouldUseOldMaxSmartSize
)
623 // Check for free space on device where cache directory lives
626 cacheDirectory (do_CreateInstance(NS_LOCAL_FILE_CONTRACTID
, &rv
));
627 if (NS_FAILED(rv
) || !cacheDirectory
)
628 return DEFAULT_CACHE_SIZE
;
629 rv
= cacheDirectory
->InitWithPath(cachePath
);
631 return DEFAULT_CACHE_SIZE
;
632 int64_t bytesAvailable
;
633 rv
= cacheDirectory
->GetDiskSpaceAvailable(&bytesAvailable
);
635 return DEFAULT_CACHE_SIZE
;
637 return SmartCacheSize(static_cast<uint32_t>((bytesAvailable
/ 1024) +
639 shouldUseOldMaxSmartSize
);
642 /* Determine if we are permitted to dynamically size the user's disk cache based
643 * on their disk space available. We may do this so long as the pref
644 * smart_size.enabled is true.
647 nsCacheProfilePrefObserver::PermittedToSmartSize(nsIPrefBranch
* branch
, bool
652 // check if user has set cache size in the past
654 rv
= branch
->PrefHasUserValue(DISK_CACHE_CAPACITY_PREF
, &userSet
);
655 if (NS_FAILED(rv
)) userSet
= true;
658 // If user explicitly set cache size to be smaller than old default
659 // of 50 MB, then keep user's value. Otherwise use smart sizing.
660 rv
= branch
->GetIntPref(DISK_CACHE_CAPACITY_PREF
, &oldCapacity
);
661 if (oldCapacity
< PRE_GECKO_2_0_DEFAULT_CACHE_SIZE
) {
662 mSmartSizeEnabled
= false;
663 branch
->SetBoolPref(DISK_CACHE_SMART_SIZE_ENABLED_PREF
,
665 return mSmartSizeEnabled
;
668 // Set manual setting to MAX cache size as starting val for any
669 // adjustment by user: (bug 559942 comment 65)
670 int32_t maxSize
= mShouldUseOldMaxSmartSize
? OLD_MAX_CACHE_SIZE
: MAX_CACHE_SIZE
;
671 branch
->SetIntPref(DISK_CACHE_CAPACITY_PREF
, maxSize
);
674 rv
= branch
->GetBoolPref(DISK_CACHE_SMART_SIZE_ENABLED_PREF
,
677 mSmartSizeEnabled
= false;
678 return mSmartSizeEnabled
;
683 nsCacheProfilePrefObserver::ReadPrefs(nsIPrefBranch
* branch
)
687 // read disk cache device prefs
688 mDiskCacheEnabled
= true; // presume disk cache is enabled
689 (void) branch
->GetBoolPref(DISK_CACHE_ENABLE_PREF
, &mDiskCacheEnabled
);
691 mDiskCacheCapacity
= DISK_CACHE_CAPACITY
;
692 (void)branch
->GetIntPref(DISK_CACHE_CAPACITY_PREF
, &mDiskCacheCapacity
);
693 mDiskCacheCapacity
= std::max(0, mDiskCacheCapacity
);
695 (void) branch
->GetIntPref(DISK_CACHE_MAX_ENTRY_SIZE_PREF
,
696 &mDiskCacheMaxEntrySize
);
697 mDiskCacheMaxEntrySize
= std::max(-1, mDiskCacheMaxEntrySize
);
699 (void) branch
->GetComplexValue(DISK_CACHE_DIR_PREF
, // ignore error
701 getter_AddRefs(mDiskCacheParentDirectory
));
703 (void) branch
->GetBoolPref(DISK_CACHE_USE_OLD_MAX_SMART_SIZE_PREF
,
704 &mShouldUseOldMaxSmartSize
);
706 if (!mDiskCacheParentDirectory
) {
707 nsCOMPtr
<nsIFile
> directory
;
709 // try to get the disk cache parent directory
710 rv
= NS_GetSpecialDirectory(NS_APP_CACHE_PARENT_DIR
,
711 getter_AddRefs(directory
));
713 // try to get the profile directory (there may not be a profile yet)
714 nsCOMPtr
<nsIFile
> profDir
;
715 NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR
,
716 getter_AddRefs(profDir
));
717 NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR
,
718 getter_AddRefs(directory
));
722 nsCacheService::MoveOrRemoveDiskCache(profDir
, directory
,
726 // use file cache in build tree only if asked, to avoid cache dir litter
727 if (!directory
&& PR_GetEnv("NECKO_DEV_ENABLE_DISK_CACHE")) {
728 rv
= NS_GetSpecialDirectory(NS_XPCOM_CURRENT_PROCESS_DIR
,
729 getter_AddRefs(directory
));
732 mDiskCacheParentDirectory
= do_QueryInterface(directory
, &rv
);
734 if (mDiskCacheParentDirectory
) {
735 bool firstSmartSizeRun
;
736 rv
= branch
->GetBoolPref(DISK_CACHE_SMART_SIZE_FIRST_RUN_PREF
,
739 firstSmartSizeRun
= false;
740 if (PermittedToSmartSize(branch
, firstSmartSizeRun
)) {
741 // Avoid evictions: use previous cache size until smart size event
742 // updates mDiskCacheCapacity
743 rv
= branch
->GetIntPref(firstSmartSizeRun
?
744 DISK_CACHE_CAPACITY_PREF
:
745 DISK_CACHE_SMART_SIZE_PREF
,
746 &mDiskCacheCapacity
);
748 mDiskCacheCapacity
= DEFAULT_CACHE_SIZE
;
751 if (firstSmartSizeRun
) {
752 // It is no longer our first run
753 rv
= branch
->SetBoolPref(DISK_CACHE_SMART_SIZE_FIRST_RUN_PREF
,
756 NS_WARNING("Failed setting first_run pref in ReadPrefs.");
760 // read offline cache device prefs
761 mOfflineCacheEnabled
= true; // presume offline cache is enabled
762 (void) branch
->GetBoolPref(OFFLINE_CACHE_ENABLE_PREF
,
763 &mOfflineCacheEnabled
);
765 mOfflineCacheCapacity
= OFFLINE_CACHE_CAPACITY
;
766 (void)branch
->GetIntPref(OFFLINE_CACHE_CAPACITY_PREF
,
767 &mOfflineCacheCapacity
);
768 mOfflineCacheCapacity
= std::max(0, mOfflineCacheCapacity
);
770 (void) branch
->GetComplexValue(OFFLINE_CACHE_DIR_PREF
, // ignore error
772 getter_AddRefs(mOfflineCacheParentDirectory
));
774 if (!mOfflineCacheParentDirectory
) {
775 nsCOMPtr
<nsIFile
> directory
;
777 // try to get the offline cache parent directory
778 rv
= NS_GetSpecialDirectory(NS_APP_CACHE_PARENT_DIR
,
779 getter_AddRefs(directory
));
781 // try to get the profile directory (there may not be a profile yet)
782 nsCOMPtr
<nsIFile
> profDir
;
783 NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR
,
784 getter_AddRefs(profDir
));
785 NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR
,
786 getter_AddRefs(directory
));
790 nsCacheService::MoveOrRemoveDiskCache(profDir
, directory
,
796 // use current process directory during development
797 rv
= NS_GetSpecialDirectory(NS_XPCOM_CURRENT_PROCESS_DIR
,
798 getter_AddRefs(directory
));
802 mOfflineCacheParentDirectory
= do_QueryInterface(directory
, &rv
);
805 // read memory cache device prefs
806 (void) branch
->GetBoolPref(MEMORY_CACHE_ENABLE_PREF
, &mMemoryCacheEnabled
);
808 mMemoryCacheCapacity
= -1;
809 (void) branch
->GetIntPref(MEMORY_CACHE_CAPACITY_PREF
,
810 &mMemoryCacheCapacity
);
812 (void) branch
->GetIntPref(MEMORY_CACHE_MAX_ENTRY_SIZE_PREF
,
813 &mMemoryCacheMaxEntrySize
);
814 mMemoryCacheMaxEntrySize
= std::max(-1, mMemoryCacheMaxEntrySize
);
816 // read cache compression level pref
817 mCacheCompressionLevel
= CACHE_COMPRESSION_LEVEL
;
818 (void)branch
->GetIntPref(CACHE_COMPRESSION_LEVEL_PREF
,
819 &mCacheCompressionLevel
);
820 mCacheCompressionLevel
= std::max(0, mCacheCompressionLevel
);
821 mCacheCompressionLevel
= std::min(9, mCacheCompressionLevel
);
823 // read cache shutdown sanitization prefs
824 (void) branch
->GetBoolPref(SANITIZE_ON_SHUTDOWN_PREF
,
825 &mSanitizeOnShutdown
);
826 (void) branch
->GetBoolPref(CLEAR_ON_SHUTDOWN_PREF
,
827 &mClearCacheOnShutdown
);
833 nsCacheService::DispatchToCacheIOThread(nsIRunnable
* event
)
835 if (!gService
->mCacheIOThread
) return NS_ERROR_NOT_AVAILABLE
;
836 return gService
->mCacheIOThread
->Dispatch(event
, NS_DISPATCH_NORMAL
);
840 nsCacheService::SyncWithCacheIOThread()
842 gService
->mLock
.AssertCurrentThreadOwns();
843 if (!gService
->mCacheIOThread
) return NS_ERROR_NOT_AVAILABLE
;
845 nsCOMPtr
<nsIRunnable
> event
= new nsBlockOnCacheThreadEvent();
847 // dispatch event - it will notify the monitor when it's done
849 gService
->mCacheIOThread
->Dispatch(event
, NS_DISPATCH_NORMAL
);
851 NS_WARNING("Failed dispatching block-event");
852 return NS_ERROR_UNEXPECTED
;
855 // wait until notified, then return
856 rv
= gService
->mCondVar
.Wait();
863 nsCacheProfilePrefObserver::DiskCacheEnabled()
865 if ((mDiskCacheCapacity
== 0) || (!mDiskCacheParentDirectory
)) return false;
866 return mDiskCacheEnabled
&& (!mSanitizeOnShutdown
|| !mClearCacheOnShutdown
);
871 nsCacheProfilePrefObserver::OfflineCacheEnabled()
873 if ((mOfflineCacheCapacity
== 0) || (!mOfflineCacheParentDirectory
))
876 return mOfflineCacheEnabled
;
881 nsCacheProfilePrefObserver::MemoryCacheEnabled()
883 if (mMemoryCacheCapacity
== 0) return false;
884 return mMemoryCacheEnabled
;
889 * MemoryCacheCapacity
891 * If the browser.cache.memory.capacity preference is positive, we use that
892 * value for the amount of memory available for the cache.
894 * If browser.cache.memory.capacity is zero, the memory cache is disabled.
896 * If browser.cache.memory.capacity is negative or not present, we use a
897 * formula that grows less than linearly with the amount of system memory,
898 * with an upper limit on the cache size. No matter how much physical RAM is
899 * present, the default cache size would not exceed 32 MB. This maximum would
900 * apply only to systems with more than 4 GB of RAM (e.g. terminal servers)
913 * The equation for this is (for cache size C and memory size K (kbytes)):
915 * C = x^2/3 + x + 2/3 + 0.1 (0.1 for rounding)
920 nsCacheProfilePrefObserver::MemoryCacheCapacity()
922 int32_t capacity
= mMemoryCacheCapacity
;
924 CACHE_LOG_DEBUG(("Memory cache capacity forced to %d\n", capacity
));
928 static uint64_t bytes
= PR_GetPhysicalMemorySize();
929 CACHE_LOG_DEBUG(("Physical Memory size is %llu\n", bytes
));
931 // If getting the physical memory failed, arbitrarily assume
932 // 32 MB of RAM. We use a low default to have a reasonable
933 // size on all the devices we support.
935 bytes
= 32 * 1024 * 1024;
937 // Conversion from unsigned int64_t to double doesn't work on all platforms.
938 // We need to truncate the value at INT64_MAX to make sure we don't
940 if (bytes
> INT64_MAX
)
943 uint64_t kbytes
= bytes
>> 10;
945 double kBytesD
= double(kbytes
);
947 double x
= log(kBytesD
)/log(2.0) - 14;
949 capacity
= (int32_t)(x
* x
/ 3.0 + x
+ 2.0 / 3 + 0.1); // 0.1 for rounding
961 nsCacheProfilePrefObserver::CacheCompressionLevel()
963 return mCacheCompressionLevel
;
966 /******************************************************************************
967 * nsProcessRequestEvent
968 *****************************************************************************/
970 class nsProcessRequestEvent
: public nsRunnable
{
972 nsProcessRequestEvent(nsCacheRequest
*aRequest
)
981 NS_ASSERTION(mRequest
->mListener
,
982 "Sync OpenCacheEntry() posted to background thread!");
984 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSPROCESSREQUESTEVENT_RUN
));
985 rv
= nsCacheService::gService
->ProcessRequest(mRequest
,
989 // Don't delete the request if it was queued
990 if (!(mRequest
->IsBlocking() &&
991 rv
== NS_ERROR_CACHE_WAIT_FOR_VALIDATION
))
998 virtual ~nsProcessRequestEvent() {}
1001 nsCacheRequest
*mRequest
;
1004 /******************************************************************************
1006 *****************************************************************************/
1008 class nsDoomEvent
: public nsRunnable
{
1010 nsDoomEvent(nsCacheSession
*session
,
1011 const nsACString
&key
,
1012 nsICacheListener
*listener
)
1014 mKey
= *session
->ClientID();
1017 mStoragePolicy
= session
->StoragePolicy();
1018 mListener
= listener
;
1019 mThread
= do_GetCurrentThread();
1020 // We addref the listener here and release it in nsNotifyDoomListener
1021 // on the callers thread. If posting of nsNotifyDoomListener event fails
1022 // we leak the listener which is better than releasing it on a wrong
1024 NS_IF_ADDREF(mListener
);
1029 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSDOOMEVENT_RUN
));
1031 bool foundActive
= true;
1032 nsresult status
= NS_ERROR_NOT_AVAILABLE
;
1033 nsCacheEntry
*entry
;
1034 entry
= nsCacheService::gService
->mActiveEntries
.GetEntry(&mKey
);
1036 bool collision
= false;
1037 foundActive
= false;
1038 entry
= nsCacheService::gService
->SearchCacheDevices(&mKey
,
1045 nsCacheService::gService
->DoomEntry_Internal(entry
, foundActive
);
1049 mThread
->Dispatch(new nsNotifyDoomListener(mListener
, status
),
1050 NS_DISPATCH_NORMAL
);
1051 // posted event will release the reference on the correct thread
1052 mListener
= nullptr;
1060 nsCacheStoragePolicy mStoragePolicy
;
1061 nsICacheListener
*mListener
;
1062 nsCOMPtr
<nsIThread
> mThread
;
1065 /******************************************************************************
1067 *****************************************************************************/
1068 nsCacheService
* nsCacheService::gService
= nullptr;
1070 NS_IMPL_THREADSAFE_ISUPPORTS1(nsCacheService
, nsICacheService
)
1072 nsCacheService::nsCacheService()
1073 : mObserver(nullptr),
1074 mLock("nsCacheService.mLock"),
1075 mCondVar(mLock
, "nsCacheService.mCondVar"),
1076 mInitialized(false),
1077 mClearingEntries(false),
1078 mEnableMemoryDevice(true),
1079 mEnableDiskDevice(true),
1080 mMemoryDevice(nullptr),
1081 mDiskDevice(nullptr),
1082 mOfflineDevice(nullptr),
1089 mDeactivateFailures(0),
1090 mDeactivatedUnboundEntries(0)
1092 NS_ASSERTION(gService
==nullptr, "multiple nsCacheService instances!");
1095 // create list of cache devices
1096 PR_INIT_CLIST(&mDoomedEntries
);
1097 mCustomOfflineDevices
.Init();
1100 nsCacheService::~nsCacheService()
1102 if (mInitialized
) // Shutdown hasn't been called yet.
1106 mObserver
->Remove();
1107 NS_RELEASE(mObserver
);
1115 nsCacheService::Init()
1117 // Thie method must be called on the main thread because mCacheIOThread must
1118 // only be modified on the main thread.
1119 if (!NS_IsMainThread()) {
1120 NS_ERROR("nsCacheService::Init called off the main thread");
1121 return NS_ERROR_NOT_SAME_THREAD
;
1124 NS_ASSERTION(!mInitialized
, "nsCacheService already initialized.");
1126 return NS_ERROR_ALREADY_INITIALIZED
;
1128 if (mozilla::net::IsNeckoChild()) {
1129 return NS_ERROR_UNEXPECTED
;
1134 nsresult rv
= NS_NewNamedThread("Cache I/O",
1135 getter_AddRefs(mCacheIOThread
));
1136 if (NS_FAILED(rv
)) {
1137 NS_RUNTIMEABORT("Can't create cache IO thread");
1140 rv
= nsDeleteDir::Init();
1141 if (NS_FAILED(rv
)) {
1142 NS_WARNING("Can't initialize nsDeleteDir");
1145 // initialize hashtable for active cache entries
1146 rv
= mActiveEntries
.Init();
1147 if (NS_FAILED(rv
)) return rv
;
1149 // create profile/preference observer
1151 mObserver
= new nsCacheProfilePrefObserver();
1152 NS_ADDREF(mObserver
);
1153 mObserver
->Install();
1156 mEnableDiskDevice
= mObserver
->DiskCacheEnabled();
1157 mEnableOfflineDevice
= mObserver
->OfflineCacheEnabled();
1158 mEnableMemoryDevice
= mObserver
->MemoryCacheEnabled();
1160 mInitialized
= true;
1166 nsCacheService::ShutdownCustomCacheDeviceEnum(const nsAString
& aProfileDir
,
1167 nsRefPtr
<nsOfflineCacheDevice
>& aDevice
,
1170 aDevice
->Shutdown();
1171 return PL_DHASH_REMOVE
;
1175 nsCacheService::Shutdown()
1177 // This method must be called on the main thread because mCacheIOThread must
1178 // only be modified on the main thread.
1179 if (!NS_IsMainThread()) {
1180 NS_RUNTIMEABORT("nsCacheService::Shutdown called off the main thread");
1183 nsCOMPtr
<nsIThread
> cacheIOThread
;
1184 Telemetry::AutoTimer
<Telemetry::NETWORK_DISK_CACHE_SHUTDOWN
> totalTimer
;
1186 bool shouldSanitize
= false;
1187 nsCOMPtr
<nsIFile
> parentDir
;
1190 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSCACHESERVICE_SHUTDOWN
));
1191 NS_ASSERTION(mInitialized
,
1192 "can't shutdown nsCacheService unless it has been initialized.");
1196 mClearingEntries
= true;
1197 DoomActiveEntries(nullptr);
1203 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSCACHESERVICE_SHUTDOWN
));
1204 NS_ASSERTION(mInitialized
, "Bad state");
1206 mInitialized
= false;
1211 if (mSmartSizeTimer
) {
1212 mSmartSizeTimer
->Cancel();
1213 mSmartSizeTimer
= nullptr;
1216 // Make sure to wait for any pending cache-operations before
1217 // proceeding with destructive actions (bug #620660)
1218 (void) SyncWithCacheIOThread();
1220 // obtain the disk cache directory in case we need to sanitize it
1221 parentDir
= mObserver
->DiskCacheParentDirectory();
1222 shouldSanitize
= mObserver
->SanitizeAtShutdown();
1224 // deallocate memory and disk caches
1225 delete mMemoryDevice
;
1226 mMemoryDevice
= nullptr;
1229 mDiskDevice
= nullptr;
1232 mOfflineDevice
->Shutdown();
1234 NS_IF_RELEASE(mOfflineDevice
);
1236 mCustomOfflineDevices
.Enumerate(&nsCacheService::ShutdownCustomCacheDeviceEnum
, nullptr);
1239 LogCacheStatistics();
1242 mClearingEntries
= false;
1243 mCacheIOThread
.swap(cacheIOThread
);
1247 cacheIOThread
->Shutdown();
1249 if (shouldSanitize
) {
1250 nsresult rv
= parentDir
->AppendNative(NS_LITERAL_CSTRING("Cache"));
1251 if (NS_SUCCEEDED(rv
)) {
1253 if (NS_SUCCEEDED(parentDir
->Exists(&exists
)) && exists
)
1254 nsDeleteDir::DeleteDir(parentDir
, false);
1256 Telemetry::AutoTimer
<Telemetry::NETWORK_DISK_CACHE_SHUTDOWN_CLEAR_PRIVATE
> timer
;
1257 nsDeleteDir::Shutdown(shouldSanitize
);
1259 Telemetry::AutoTimer
<Telemetry::NETWORK_DISK_CACHE_DELETEDIR_SHUTDOWN
> timer
;
1260 nsDeleteDir::Shutdown(shouldSanitize
);
1266 nsCacheService::Create(nsISupports
* aOuter
, const nsIID
& aIID
, void* *aResult
)
1270 if (aOuter
!= nullptr)
1271 return NS_ERROR_NO_AGGREGATION
;
1273 nsCacheService
* cacheService
= new nsCacheService();
1274 if (cacheService
== nullptr)
1275 return NS_ERROR_OUT_OF_MEMORY
;
1277 NS_ADDREF(cacheService
);
1278 rv
= cacheService
->Init();
1279 if (NS_SUCCEEDED(rv
)) {
1280 rv
= cacheService
->QueryInterface(aIID
, aResult
);
1282 NS_RELEASE(cacheService
);
1288 nsCacheService::CreateSession(const char * clientID
,
1289 nsCacheStoragePolicy storagePolicy
,
1291 nsICacheSession
**result
)
1295 if (this == nullptr) return NS_ERROR_NOT_AVAILABLE
;
1297 nsCacheSession
* session
= new nsCacheSession(clientID
, storagePolicy
, streamBased
);
1298 if (!session
) return NS_ERROR_OUT_OF_MEMORY
;
1300 NS_ADDREF(*result
= session
);
1307 nsCacheService::EvictEntriesForSession(nsCacheSession
* session
)
1309 NS_ASSERTION(gService
, "nsCacheService::gService is null.");
1310 return gService
->EvictEntriesForClient(session
->ClientID()->get(),
1311 session
->StoragePolicy());
1316 class EvictionNotifierRunnable
: public nsRunnable
1319 EvictionNotifierRunnable(nsISupports
* aSubject
)
1320 : mSubject(aSubject
)
1326 nsCOMPtr
<nsISupports
> mSubject
;
1330 EvictionNotifierRunnable::Run()
1332 nsCOMPtr
<nsIObserverService
> obsSvc
=
1333 mozilla::services::GetObserverService();
1335 obsSvc
->NotifyObservers(mSubject
,
1336 NS_CACHESERVICE_EMPTYCACHE_TOPIC_ID
,
1342 } // anonymous namespace
1345 nsCacheService::EvictEntriesForClient(const char * clientID
,
1346 nsCacheStoragePolicy storagePolicy
)
1348 nsRefPtr
<EvictionNotifierRunnable
> r
= new EvictionNotifierRunnable(this);
1349 NS_DispatchToMainThread(r
);
1351 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSCACHESERVICE_EVICTENTRIESFORCLIENT
));
1352 nsresult res
= NS_OK
;
1354 if (storagePolicy
== nsICache::STORE_ANYWHERE
||
1355 storagePolicy
== nsICache::STORE_ON_DISK
) {
1357 if (mEnableDiskDevice
) {
1358 nsresult rv
= NS_OK
;
1360 rv
= CreateDiskDevice();
1362 rv
= mDiskDevice
->EvictEntries(clientID
);
1368 // Only clear the offline cache if it has been specifically asked for.
1369 if (storagePolicy
== nsICache::STORE_OFFLINE
) {
1370 if (mEnableOfflineDevice
) {
1371 nsresult rv
= NS_OK
;
1372 if (!mOfflineDevice
)
1373 rv
= CreateOfflineDevice();
1375 rv
= mOfflineDevice
->EvictEntries(clientID
);
1381 if (storagePolicy
== nsICache::STORE_ANYWHERE
||
1382 storagePolicy
== nsICache::STORE_IN_MEMORY
) {
1383 // If there is no memory device, there is no need to evict it...
1384 if (mMemoryDevice
) {
1385 nsresult rv
= mMemoryDevice
->EvictEntries(clientID
);
1396 nsCacheService::IsStorageEnabledForPolicy(nsCacheStoragePolicy storagePolicy
,
1399 if (gService
== nullptr) return NS_ERROR_NOT_AVAILABLE
;
1400 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSCACHESERVICE_ISSTORAGEENABLEDFORPOLICY
));
1402 *result
= gService
->IsStorageEnabledForPolicy_Locked(storagePolicy
);
1408 nsCacheService::DoomEntry(nsCacheSession
*session
,
1409 const nsACString
&key
,
1410 nsICacheListener
*listener
)
1412 CACHE_LOG_DEBUG(("Dooming entry for session %p, key %s\n",
1413 session
, PromiseFlatCString(key
).get()));
1414 NS_ASSERTION(gService
, "nsCacheService::gService is null.");
1416 if (!gService
->mInitialized
)
1417 return NS_ERROR_NOT_INITIALIZED
;
1419 return DispatchToCacheIOThread(new nsDoomEvent(session
, key
, listener
));
1424 nsCacheService::IsStorageEnabledForPolicy_Locked(nsCacheStoragePolicy storagePolicy
)
1426 if (gService
->mEnableMemoryDevice
&&
1427 (storagePolicy
== nsICache::STORE_ANYWHERE
||
1428 storagePolicy
== nsICache::STORE_IN_MEMORY
)) {
1431 if (gService
->mEnableDiskDevice
&&
1432 (storagePolicy
== nsICache::STORE_ANYWHERE
||
1433 storagePolicy
== nsICache::STORE_ON_DISK
)) {
1436 if (gService
->mEnableOfflineDevice
&&
1437 storagePolicy
== nsICache::STORE_OFFLINE
) {
1444 NS_IMETHODIMP
nsCacheService::VisitEntries(nsICacheVisitor
*visitor
)
1446 NS_ENSURE_ARG_POINTER(visitor
);
1448 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSCACHESERVICE_VISITENTRIES
));
1450 if (!(mEnableDiskDevice
|| mEnableMemoryDevice
))
1451 return NS_ERROR_NOT_AVAILABLE
;
1453 // XXX record the fact that a visitation is in progress,
1454 // XXX i.e. keep list of visitors in progress.
1456 nsresult rv
= NS_OK
;
1457 // If there is no memory device, there are then also no entries to visit...
1458 if (mMemoryDevice
) {
1459 rv
= mMemoryDevice
->Visit(visitor
);
1460 if (NS_FAILED(rv
)) return rv
;
1463 if (mEnableDiskDevice
) {
1465 rv
= CreateDiskDevice();
1466 if (NS_FAILED(rv
)) return rv
;
1468 rv
= mDiskDevice
->Visit(visitor
);
1469 if (NS_FAILED(rv
)) return rv
;
1472 if (mEnableOfflineDevice
) {
1473 if (!mOfflineDevice
) {
1474 rv
= CreateOfflineDevice();
1475 if (NS_FAILED(rv
)) return rv
;
1477 rv
= mOfflineDevice
->Visit(visitor
);
1478 if (NS_FAILED(rv
)) return rv
;
1481 // XXX notify any shutdown process that visitation is complete for THIS visitor.
1482 // XXX keep queue of visitors
1488 NS_IMETHODIMP
nsCacheService::EvictEntries(nsCacheStoragePolicy storagePolicy
)
1490 return EvictEntriesForClient(nullptr, storagePolicy
);
1493 NS_IMETHODIMP
nsCacheService::GetCacheIOTarget(nsIEventTarget
* *aCacheIOTarget
)
1495 NS_ENSURE_ARG_POINTER(aCacheIOTarget
);
1497 // Because mCacheIOThread can only be changed on the main thread, it can be
1498 // read from the main thread without the lock. This is useful to prevent
1499 // blocking the main thread on other cache operations.
1500 if (!NS_IsMainThread()) {
1501 Lock(LOCK_TELEM(NSCACHESERVICE_GETCACHEIOTARGET
));
1505 if (mCacheIOThread
) {
1506 NS_ADDREF(*aCacheIOTarget
= mCacheIOThread
);
1509 *aCacheIOTarget
= nullptr;
1510 rv
= NS_ERROR_NOT_AVAILABLE
;
1513 if (!NS_IsMainThread()) {
1524 nsCacheService::CreateDiskDevice()
1526 if (!mInitialized
) return NS_ERROR_NOT_AVAILABLE
;
1527 if (!mEnableDiskDevice
) return NS_ERROR_NOT_AVAILABLE
;
1528 if (mDiskDevice
) return NS_OK
;
1530 mDiskDevice
= new nsDiskCacheDevice
;
1531 if (!mDiskDevice
) return NS_ERROR_OUT_OF_MEMORY
;
1533 // set the preferences
1534 mDiskDevice
->SetCacheParentDirectory(mObserver
->DiskCacheParentDirectory());
1535 mDiskDevice
->SetCapacity(mObserver
->DiskCacheCapacity());
1536 mDiskDevice
->SetMaxEntrySize(mObserver
->DiskCacheMaxEntrySize());
1538 nsresult rv
= mDiskDevice
->Init();
1539 if (NS_FAILED(rv
)) {
1542 printf("### mDiskDevice->Init() failed (0x%.8x)\n",
1543 static_cast<uint32_t>(rv
));
1544 printf("### - disabling disk cache for this session.\n");
1547 mEnableDiskDevice
= false;
1549 mDiskDevice
= nullptr;
1553 Telemetry::Accumulate(Telemetry::DISK_CACHE_SMART_SIZE_USING_OLD_MAX
,
1554 mObserver
->ShouldUseOldMaxSmartSize());
1556 NS_ASSERTION(!mSmartSizeTimer
, "Smartsize timer was already fired!");
1558 // Disk device is usually created during the startup. Delay smart size
1559 // calculation to avoid possible massive IO caused by eviction of entries
1560 // in case the new smart size is smaller than current cache usage.
1561 mSmartSizeTimer
= do_CreateInstance("@mozilla.org/timer;1", &rv
);
1562 if (NS_SUCCEEDED(rv
)) {
1563 rv
= mSmartSizeTimer
->InitWithCallback(new nsSetDiskSmartSizeCallback(),
1565 nsITimer::TYPE_ONE_SHOT
);
1566 if (NS_FAILED(rv
)) {
1567 NS_WARNING("Failed to post smart size timer");
1568 mSmartSizeTimer
= nullptr;
1571 NS_WARNING("Can't create smart size timer");
1573 // Ignore state of the timer and return success since the purpose of the
1574 // method (create the disk-device) has been fulfilled
1579 // Runnable sent from cache thread to main thread
1580 class nsDisableOldMaxSmartSizePrefEvent
: public nsRunnable
1583 nsDisableOldMaxSmartSizePrefEvent() {}
1587 // Main thread may have already called nsCacheService::Shutdown
1588 if (!nsCacheService::IsInitialized())
1589 return NS_ERROR_NOT_AVAILABLE
;
1591 nsCOMPtr
<nsIPrefBranch
> branch
= do_GetService(NS_PREFSERVICE_CONTRACTID
);
1593 return NS_ERROR_NOT_AVAILABLE
;
1596 nsresult rv
= branch
->SetBoolPref(DISK_CACHE_USE_OLD_MAX_SMART_SIZE_PREF
, false);
1597 if (NS_FAILED(rv
)) {
1598 NS_WARNING("Failed to disable old max smart size");
1602 nsCacheService::SetDiskSmartSize();
1604 if (nsCacheService::gService
->mObserver
->PermittedToSmartSize(branch
, false)) {
1605 rv
= branch
->SetIntPref(DISK_CACHE_CAPACITY_PREF
, MAX_CACHE_SIZE
);
1606 if (NS_FAILED(rv
)) {
1607 NS_WARNING("Failed to set cache capacity pref");
1616 nsCacheService::MarkStartingFresh()
1618 if (!gService
->mObserver
->ShouldUseOldMaxSmartSize()) {
1619 // Already using new max, nothing to do here
1623 gService
->mObserver
->SetUseNewMaxSmartSize(true);
1625 // We always dispatch an event here because we don't want to deal with lock
1626 // reentrance issues.
1627 NS_DispatchToMainThread(new nsDisableOldMaxSmartSizePrefEvent());
1631 nsCacheService::GetOfflineDevice(nsOfflineCacheDevice
**aDevice
)
1633 if (!mOfflineDevice
) {
1634 nsresult rv
= CreateOfflineDevice();
1635 NS_ENSURE_SUCCESS(rv
, rv
);
1638 NS_ADDREF(*aDevice
= mOfflineDevice
);
1643 nsCacheService::GetCustomOfflineDevice(nsIFile
*aProfileDir
,
1645 nsOfflineCacheDevice
**aDevice
)
1649 nsAutoString profilePath
;
1650 rv
= aProfileDir
->GetPath(profilePath
);
1651 NS_ENSURE_SUCCESS(rv
, rv
);
1653 if (!mCustomOfflineDevices
.Get(profilePath
, aDevice
)) {
1654 rv
= CreateCustomOfflineDevice(aProfileDir
, aQuota
, aDevice
);
1655 NS_ENSURE_SUCCESS(rv
, rv
);
1657 (*aDevice
)->SetAutoShutdown();
1658 mCustomOfflineDevices
.Put(profilePath
, *aDevice
);
1665 nsCacheService::CreateOfflineDevice()
1667 CACHE_LOG_ALWAYS(("Creating default offline device"));
1669 if (mOfflineDevice
) return NS_OK
;
1670 if (!nsCacheService::IsInitialized()) {
1671 return NS_ERROR_NOT_AVAILABLE
;
1674 nsresult rv
= CreateCustomOfflineDevice(
1675 mObserver
->OfflineCacheParentDirectory(),
1676 mObserver
->OfflineCacheCapacity(),
1678 NS_ENSURE_SUCCESS(rv
, rv
);
1684 nsCacheService::CreateCustomOfflineDevice(nsIFile
*aProfileDir
,
1686 nsOfflineCacheDevice
**aDevice
)
1688 NS_ENSURE_ARG(aProfileDir
);
1690 #if defined(PR_LOGGING)
1691 nsAutoCString profilePath
;
1692 aProfileDir
->GetNativePath(profilePath
);
1693 CACHE_LOG_ALWAYS(("Creating custom offline device, %s, %d",
1694 profilePath
.BeginReading(), aQuota
));
1697 if (!mInitialized
) return NS_ERROR_NOT_AVAILABLE
;
1698 if (!mEnableOfflineDevice
) return NS_ERROR_NOT_AVAILABLE
;
1700 *aDevice
= new nsOfflineCacheDevice
;
1702 NS_ADDREF(*aDevice
);
1704 // set the preferences
1705 (*aDevice
)->SetCacheParentDirectory(aProfileDir
);
1706 (*aDevice
)->SetCapacity(aQuota
);
1708 nsresult rv
= (*aDevice
)->Init();
1709 if (NS_FAILED(rv
)) {
1710 CACHE_LOG_DEBUG(("OfflineDevice->Init() failed (0x%.8x)\n", rv
));
1711 CACHE_LOG_DEBUG((" - disabling offline cache for this session.\n"));
1713 NS_RELEASE(*aDevice
);
1719 nsCacheService::CreateMemoryDevice()
1721 if (!mInitialized
) return NS_ERROR_NOT_AVAILABLE
;
1722 if (!mEnableMemoryDevice
) return NS_ERROR_NOT_AVAILABLE
;
1723 if (mMemoryDevice
) return NS_OK
;
1725 mMemoryDevice
= new nsMemoryCacheDevice
;
1726 if (!mMemoryDevice
) return NS_ERROR_OUT_OF_MEMORY
;
1729 int32_t capacity
= mObserver
->MemoryCacheCapacity();
1730 CACHE_LOG_DEBUG(("Creating memory device with capacity %d\n", capacity
));
1731 mMemoryDevice
->SetCapacity(capacity
);
1732 mMemoryDevice
->SetMaxEntrySize(mObserver
->MemoryCacheMaxEntrySize());
1734 nsresult rv
= mMemoryDevice
->Init();
1735 if (NS_FAILED(rv
)) {
1736 NS_WARNING("Initialization of Memory Cache failed.");
1737 delete mMemoryDevice
;
1738 mMemoryDevice
= nullptr;
1745 nsCacheService::RemoveCustomOfflineDevice(nsOfflineCacheDevice
*aDevice
)
1747 nsCOMPtr
<nsIFile
> profileDir
= aDevice
->BaseDirectory();
1749 return NS_ERROR_UNEXPECTED
;
1751 nsAutoString profilePath
;
1752 nsresult rv
= profileDir
->GetPath(profilePath
);
1753 NS_ENSURE_SUCCESS(rv
, rv
);
1755 mCustomOfflineDevices
.Remove(profilePath
);
1760 nsCacheService::CreateRequest(nsCacheSession
* session
,
1761 const nsACString
& clientKey
,
1762 nsCacheAccessMode accessRequested
,
1764 nsICacheListener
* listener
,
1765 nsCacheRequest
** request
)
1767 NS_ASSERTION(request
, "CreateRequest: request is null");
1769 nsAutoCString
key(*session
->ClientID());
1771 key
.Append(clientKey
);
1773 if (mMaxKeyLength
< key
.Length()) mMaxKeyLength
= key
.Length();
1776 *request
= new nsCacheRequest(key
, listener
, accessRequested
,
1777 blockingMode
, session
);
1779 if (!listener
) return NS_OK
; // we're sync, we're done.
1781 // get the request's thread
1782 (*request
)->mThread
= do_GetCurrentThread();
1788 class nsCacheListenerEvent
: public nsRunnable
1791 nsCacheListenerEvent(nsICacheListener
*listener
,
1792 nsICacheEntryDescriptor
*descriptor
,
1793 nsCacheAccessMode accessGranted
,
1795 : mListener(listener
) // transfers reference
1796 , mDescriptor(descriptor
) // transfers reference (may be null)
1797 , mAccessGranted(accessGranted
)
1803 mListener
->OnCacheEntryAvailable(mDescriptor
, mAccessGranted
, mStatus
);
1805 NS_RELEASE(mListener
);
1806 NS_IF_RELEASE(mDescriptor
);
1811 // We explicitly leak mListener or mDescriptor if Run is not called
1812 // because otherwise we cannot guarantee that they are destroyed on
1813 // the right thread.
1815 nsICacheListener
*mListener
;
1816 nsICacheEntryDescriptor
*mDescriptor
;
1817 nsCacheAccessMode mAccessGranted
;
1823 nsCacheService::NotifyListener(nsCacheRequest
* request
,
1824 nsICacheEntryDescriptor
* descriptor
,
1825 nsCacheAccessMode accessGranted
,
1828 NS_ASSERTION(request
->mThread
, "no thread set in async request!");
1830 // Swap ownership, and release listener on target thread...
1831 nsICacheListener
*listener
= request
->mListener
;
1832 request
->mListener
= nullptr;
1834 nsCOMPtr
<nsIRunnable
> ev
=
1835 new nsCacheListenerEvent(listener
, descriptor
,
1836 accessGranted
, status
);
1838 // Better to leak listener and descriptor if we fail because we don't
1839 // want to destroy them inside the cache service lock or on potentially
1840 // the wrong thread.
1841 return NS_ERROR_OUT_OF_MEMORY
;
1844 return request
->mThread
->Dispatch(ev
, NS_DISPATCH_NORMAL
);
1849 nsCacheService::ProcessRequest(nsCacheRequest
* request
,
1850 bool calledFromOpenCacheEntry
,
1851 nsICacheEntryDescriptor
** result
)
1853 // !!! must be called with mLock held !!!
1855 nsCacheEntry
* entry
= nullptr;
1856 nsCacheEntry
* doomedEntry
= nullptr;
1857 nsCacheAccessMode accessGranted
= nsICache::ACCESS_NONE
;
1858 if (result
) *result
= nullptr;
1860 while(1) { // Activate entry loop
1861 rv
= ActivateEntry(request
, &entry
, &doomedEntry
); // get the entry for this request
1862 if (NS_FAILED(rv
)) break;
1864 while(1) { // Request Access loop
1865 NS_ASSERTION(entry
, "no entry in Request Access loop!");
1866 // entry->RequestAccess queues request on entry
1867 rv
= entry
->RequestAccess(request
, &accessGranted
);
1868 if (rv
!= NS_ERROR_CACHE_WAIT_FOR_VALIDATION
) break;
1870 if (request
->IsBlocking()) {
1871 if (request
->mListener
) {
1872 // async exits - validate, doom, or close will resume
1876 // XXX this is probably wrong...
1878 rv
= request
->WaitForValidation();
1879 Lock(LOCK_TELEM(NSCACHESERVICE_PROCESSREQUEST
));
1882 PR_REMOVE_AND_INIT_LINK(request
);
1883 if (NS_FAILED(rv
)) break; // non-blocking mode returns WAIT_FOR_VALIDATION error
1884 // okay, we're ready to process this request, request access again
1886 if (rv
!= NS_ERROR_CACHE_ENTRY_DOOMED
) break;
1888 if (entry
->IsNotInUse()) {
1889 // this request was the last one keeping it around, so get rid of it
1890 DeactivateEntry(entry
);
1892 // loop back around to look for another entry
1895 if (NS_SUCCEEDED(rv
) && request
->mProfileDir
) {
1896 // Custom cache directory has been demanded. Preset the cache device.
1897 if (entry
->StoragePolicy() != nsICache::STORE_OFFLINE
) {
1898 // Failsafe check: this is implemented only for offline cache atm.
1899 rv
= NS_ERROR_FAILURE
;
1901 nsRefPtr
<nsOfflineCacheDevice
> customCacheDevice
;
1902 rv
= GetCustomOfflineDevice(request
->mProfileDir
, -1,
1903 getter_AddRefs(customCacheDevice
));
1904 if (NS_SUCCEEDED(rv
))
1905 entry
->SetCustomCacheDevice(customCacheDevice
);
1909 nsICacheEntryDescriptor
*descriptor
= nullptr;
1911 if (NS_SUCCEEDED(rv
))
1912 rv
= entry
->CreateDescriptor(request
, accessGranted
, &descriptor
);
1914 // If doomedEntry is set, ActivatEntry() doomed an existing entry and
1915 // created a new one for that cache-key. However, any pending requests
1916 // on the doomed entry were not processed and we need to do that here.
1917 // This must be done after adding the created entry to list of active
1918 // entries (which is done in ActivateEntry()) otherwise the hashkeys crash
1919 // (see bug ##561313). It is also important to do this after creating a
1920 // descriptor for this request, or some other request may end up being
1921 // executed first for the newly created entry.
1922 // Finally, it is worth to emphasize that if doomedEntry is set,
1923 // ActivateEntry() created a new entry for the request, which will be
1924 // initialized by RequestAccess() and they both should have returned NS_OK.
1926 (void) ProcessPendingRequests(doomedEntry
);
1927 if (doomedEntry
->IsNotInUse())
1928 DeactivateEntry(doomedEntry
);
1929 doomedEntry
= nullptr;
1932 if (request
->mListener
) { // Asynchronous
1934 if (NS_FAILED(rv
) && calledFromOpenCacheEntry
&& request
->IsBlocking())
1935 return rv
; // skip notifying listener, just return rv to caller
1937 // call listener to report error or descriptor
1938 nsresult rv2
= NotifyListener(request
, descriptor
, accessGranted
, rv
);
1939 if (NS_FAILED(rv2
) && NS_SUCCEEDED(rv
)) {
1940 rv
= rv2
; // trigger delete request
1942 } else { // Synchronous
1943 *result
= descriptor
;
1950 nsCacheService::OpenCacheEntry(nsCacheSession
* session
,
1951 const nsACString
& key
,
1952 nsCacheAccessMode accessRequested
,
1954 nsICacheListener
* listener
,
1955 nsICacheEntryDescriptor
** result
)
1957 CACHE_LOG_DEBUG(("Opening entry for session %p, key %s, mode %d, blocking %d\n",
1958 session
, PromiseFlatCString(key
).get(), accessRequested
,
1960 NS_ASSERTION(gService
, "nsCacheService::gService is null.");
1964 if (!gService
->mInitialized
)
1965 return NS_ERROR_NOT_INITIALIZED
;
1967 nsCacheRequest
* request
= nullptr;
1969 nsresult rv
= gService
->CreateRequest(session
,
1975 if (NS_FAILED(rv
)) return rv
;
1977 CACHE_LOG_DEBUG(("Created request %p\n", request
));
1979 // Process the request on the background thread if we are on the main thread
1980 // and the the request is asynchronous
1981 if (NS_IsMainThread() && listener
&& gService
->mCacheIOThread
) {
1982 nsCOMPtr
<nsIRunnable
> ev
=
1983 new nsProcessRequestEvent(request
);
1984 rv
= DispatchToCacheIOThread(ev
);
1986 // delete request if we didn't post the event
1992 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSCACHESERVICE_OPENCACHEENTRY
));
1993 rv
= gService
->ProcessRequest(request
, true, result
);
1995 // delete requests that have completed
1996 if (!(listener
&& blockingMode
&&
1997 (rv
== NS_ERROR_CACHE_WAIT_FOR_VALIDATION
)))
2006 nsCacheService::ActivateEntry(nsCacheRequest
* request
,
2007 nsCacheEntry
** result
,
2008 nsCacheEntry
** doomedEntry
)
2010 CACHE_LOG_DEBUG(("Activate entry for request %p\n", request
));
2011 if (!mInitialized
|| mClearingEntries
)
2012 return NS_ERROR_NOT_AVAILABLE
;
2014 nsresult rv
= NS_OK
;
2016 NS_ASSERTION(request
!= nullptr, "ActivateEntry called with no request");
2017 if (result
) *result
= nullptr;
2018 if (doomedEntry
) *doomedEntry
= nullptr;
2019 if ((!request
) || (!result
) || (!doomedEntry
))
2020 return NS_ERROR_NULL_POINTER
;
2022 // check if the request can be satisfied
2023 if (!mEnableMemoryDevice
&& !request
->IsStreamBased())
2024 return NS_ERROR_FAILURE
;
2025 if (!IsStorageEnabledForPolicy_Locked(request
->StoragePolicy()))
2026 return NS_ERROR_FAILURE
;
2028 // search active entries (including those not bound to device)
2029 nsCacheEntry
*entry
= mActiveEntries
.GetEntry(&(request
->mKey
));
2030 CACHE_LOG_DEBUG(("Active entry for request %p is %p\n", request
, entry
));
2033 // search cache devices for entry
2034 bool collision
= false;
2035 entry
= SearchCacheDevices(&(request
->mKey
), request
->StoragePolicy(), &collision
);
2036 CACHE_LOG_DEBUG(("Device search for request %p returned %p\n",
2038 // When there is a hashkey collision just refuse to cache it...
2039 if (collision
) return NS_ERROR_CACHE_IN_USE
;
2041 if (entry
) entry
->MarkInitialized();
2043 NS_ASSERTION(entry
->IsActive(), "Inactive entry found in mActiveEntries!");
2054 ((request
->AccessRequested() == nsICache::ACCESS_WRITE
) ||
2055 ((request
->StoragePolicy() != nsICache::STORE_OFFLINE
) &&
2056 (entry
->mExpirationTime
<= SecondsFromPRTime(PR_Now()) &&
2057 request
->WillDoomEntriesIfExpired()))))
2060 // this is FORCE-WRITE request or the entry has expired
2061 // we doom entry without processing pending requests, but store it in
2062 // doomedEntry which causes pending requests to be processed below
2063 rv
= DoomEntry_Internal(entry
, false);
2064 *doomedEntry
= entry
;
2065 if (NS_FAILED(rv
)) {
2066 // XXX what to do? Increment FailedDooms counter?
2072 if (! (request
->AccessRequested() & nsICache::ACCESS_WRITE
)) {
2073 // this is a READ-ONLY request
2074 rv
= NS_ERROR_CACHE_KEY_NOT_FOUND
;
2078 entry
= new nsCacheEntry(request
->mKey
,
2079 request
->IsStreamBased(),
2080 request
->StoragePolicy());
2082 return NS_ERROR_OUT_OF_MEMORY
;
2084 if (request
->IsPrivate())
2085 entry
->MarkPrivate();
2090 // XXX we could perform an early bind in some cases based on storage policy
2093 if (!entry
->IsActive()) {
2094 rv
= mActiveEntries
.AddEntry(entry
);
2095 if (NS_FAILED(rv
)) goto error
;
2096 CACHE_LOG_DEBUG(("Added entry %p to mActiveEntries\n", entry
));
2097 entry
->MarkActive(); // mark entry active, because it's now in mActiveEntries
2110 nsCacheService::SearchCacheDevices(nsCString
* key
, nsCacheStoragePolicy policy
, bool *collision
)
2112 Telemetry::AutoTimer
<Telemetry::CACHE_DEVICE_SEARCH_2
> timer
;
2113 nsCacheEntry
* entry
= nullptr;
2115 CACHE_LOG_DEBUG(("mMemoryDevice: 0x%p\n", mMemoryDevice
));
2118 if ((policy
== nsICache::STORE_ANYWHERE
) || (policy
== nsICache::STORE_IN_MEMORY
)) {
2119 // If there is no memory device, then there is nothing to search...
2120 if (mMemoryDevice
) {
2121 entry
= mMemoryDevice
->FindEntry(key
, collision
);
2122 CACHE_LOG_DEBUG(("Searching mMemoryDevice for key %s found: 0x%p, "
2123 "collision: %d\n", key
->get(), entry
, collision
));
2128 ((policy
== nsICache::STORE_ANYWHERE
) || (policy
== nsICache::STORE_ON_DISK
))) {
2130 if (mEnableDiskDevice
) {
2132 nsresult rv
= CreateDiskDevice();
2137 entry
= mDiskDevice
->FindEntry(key
, collision
);
2141 if (!entry
&& (policy
== nsICache::STORE_OFFLINE
||
2142 (policy
== nsICache::STORE_ANYWHERE
&&
2143 gIOService
->IsOffline()))) {
2145 if (mEnableOfflineDevice
) {
2146 if (!mOfflineDevice
) {
2147 nsresult rv
= CreateOfflineDevice();
2152 entry
= mOfflineDevice
->FindEntry(key
, collision
);
2161 nsCacheService::EnsureEntryHasDevice(nsCacheEntry
* entry
)
2163 nsCacheDevice
* device
= entry
->CacheDevice();
2164 // return device if found, possibly null if the entry is doomed i.e prevent
2165 // doomed entries to bind to a device (see e.g. bugs #548406 and #596443)
2166 if (device
|| entry
->IsDoomed()) return device
;
2168 int64_t predictedDataSize
= entry
->PredictedDataSize();
2169 if (entry
->IsStreamData() && entry
->IsAllowedOnDisk() && mEnableDiskDevice
) {
2170 // this is the default
2172 (void)CreateDiskDevice(); // ignore the error (check for mDiskDevice instead)
2176 // Bypass the cache if Content-Length says the entry will be too big
2177 if (predictedDataSize
!= -1 &&
2178 mDiskDevice
->EntryIsTooBig(predictedDataSize
)) {
2179 DebugOnly
<nsresult
> rv
= nsCacheService::DoomEntry(entry
);
2180 NS_ASSERTION(NS_SUCCEEDED(rv
),"DoomEntry() failed.");
2184 entry
->MarkBinding(); // enter state of binding
2185 nsresult rv
= mDiskDevice
->BindEntry(entry
);
2186 entry
->ClearBinding(); // exit state of binding
2187 if (NS_SUCCEEDED(rv
))
2188 device
= mDiskDevice
;
2192 // if we can't use mDiskDevice, try mMemoryDevice
2193 if (!device
&& mEnableMemoryDevice
&& entry
->IsAllowedInMemory()) {
2194 if (!mMemoryDevice
) {
2195 (void)CreateMemoryDevice(); // ignore the error (check for mMemoryDevice instead)
2197 if (mMemoryDevice
) {
2198 // Bypass the cache if Content-Length says entry will be too big
2199 if (predictedDataSize
!= -1 &&
2200 mMemoryDevice
->EntryIsTooBig(predictedDataSize
)) {
2201 DebugOnly
<nsresult
> rv
= nsCacheService::DoomEntry(entry
);
2202 NS_ASSERTION(NS_SUCCEEDED(rv
),"DoomEntry() failed.");
2206 entry
->MarkBinding(); // enter state of binding
2207 nsresult rv
= mMemoryDevice
->BindEntry(entry
);
2208 entry
->ClearBinding(); // exit state of binding
2209 if (NS_SUCCEEDED(rv
))
2210 device
= mMemoryDevice
;
2214 if (!device
&& entry
->IsStreamData() &&
2215 entry
->IsAllowedOffline() && mEnableOfflineDevice
) {
2216 if (!mOfflineDevice
) {
2217 (void)CreateOfflineDevice(); // ignore the error (check for mOfflineDevice instead)
2220 device
= entry
->CustomCacheDevice()
2221 ? entry
->CustomCacheDevice()
2225 entry
->MarkBinding();
2226 nsresult rv
= device
->BindEntry(entry
);
2227 entry
->ClearBinding();
2234 entry
->SetCacheDevice(device
);
2239 nsCacheService::DoomEntry(nsCacheEntry
* entry
)
2241 return gService
->DoomEntry_Internal(entry
, true);
2246 nsCacheService::DoomEntry_Internal(nsCacheEntry
* entry
,
2247 bool doProcessPendingRequests
)
2249 if (entry
->IsDoomed()) return NS_OK
;
2251 CACHE_LOG_DEBUG(("Dooming entry %p\n", entry
));
2252 nsresult rv
= NS_OK
;
2253 entry
->MarkDoomed();
2255 NS_ASSERTION(!entry
->IsBinding(), "Dooming entry while binding device.");
2256 nsCacheDevice
* device
= entry
->CacheDevice();
2257 if (device
) device
->DoomEntry(entry
);
2259 if (entry
->IsActive()) {
2260 // remove from active entries
2261 mActiveEntries
.RemoveEntry(entry
);
2262 CACHE_LOG_DEBUG(("Removed entry %p from mActiveEntries\n", entry
));
2263 entry
->MarkInactive();
2266 // put on doom list to wait for descriptors to close
2267 NS_ASSERTION(PR_CLIST_IS_EMPTY(entry
), "doomed entry still on device list");
2268 PR_APPEND_LINK(entry
, &mDoomedEntries
);
2270 // handle pending requests only if we're supposed to
2271 if (doProcessPendingRequests
) {
2272 // tell pending requests to get on with their lives...
2273 rv
= ProcessPendingRequests(entry
);
2275 // All requests have been removed, but there may still be open descriptors
2276 if (entry
->IsNotInUse()) {
2277 DeactivateEntry(entry
); // tell device to get rid of it
2285 nsCacheService::OnProfileShutdown(bool cleanse
)
2287 if (!gService
) return;
2288 if (!gService
->mInitialized
) {
2289 // The cache service has been shut down, but someone is still holding
2290 // a reference to it. Ignore this call.
2294 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSCACHESERVICE_ONPROFILESHUTDOWN
));
2295 gService
->mClearingEntries
= true;
2296 gService
->DoomActiveEntries(nullptr);
2299 gService
->CloseAllStreams();
2301 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSCACHESERVICE_ONPROFILESHUTDOWN
));
2302 gService
->ClearDoomList();
2304 // Make sure to wait for any pending cache-operations before
2305 // proceeding with destructive actions (bug #620660)
2306 (void) SyncWithCacheIOThread();
2308 if (gService
->mDiskDevice
&& gService
->mEnableDiskDevice
) {
2310 gService
->mDiskDevice
->EvictEntries(nullptr);
2312 gService
->mDiskDevice
->Shutdown();
2314 gService
->mEnableDiskDevice
= false;
2316 if (gService
->mOfflineDevice
&& gService
->mEnableOfflineDevice
) {
2318 gService
->mOfflineDevice
->EvictEntries(nullptr);
2320 gService
->mOfflineDevice
->Shutdown();
2322 gService
->mCustomOfflineDevices
.Enumerate(
2323 &nsCacheService::ShutdownCustomCacheDeviceEnum
, nullptr);
2325 gService
->mEnableOfflineDevice
= false;
2327 if (gService
->mMemoryDevice
) {
2328 // clear memory cache
2329 gService
->mMemoryDevice
->EvictEntries(nullptr);
2332 gService
->mClearingEntries
= false;
2337 nsCacheService::OnProfileChanged()
2339 if (!gService
) return;
2341 CACHE_LOG_DEBUG(("nsCacheService::OnProfileChanged"));
2343 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSCACHESERVICE_ONPROFILECHANGED
));
2345 gService
->mEnableDiskDevice
= gService
->mObserver
->DiskCacheEnabled();
2346 gService
->mEnableOfflineDevice
= gService
->mObserver
->OfflineCacheEnabled();
2347 gService
->mEnableMemoryDevice
= gService
->mObserver
->MemoryCacheEnabled();
2349 if (gService
->mDiskDevice
) {
2350 gService
->mDiskDevice
->SetCacheParentDirectory(gService
->mObserver
->DiskCacheParentDirectory());
2351 gService
->mDiskDevice
->SetCapacity(gService
->mObserver
->DiskCacheCapacity());
2353 // XXX initialization of mDiskDevice could be made lazily, if mEnableDiskDevice is false
2354 nsresult rv
= gService
->mDiskDevice
->Init();
2355 if (NS_FAILED(rv
)) {
2356 NS_ERROR("nsCacheService::OnProfileChanged: Re-initializing disk device failed");
2357 gService
->mEnableDiskDevice
= false;
2358 // XXX delete mDiskDevice?
2362 if (gService
->mOfflineDevice
) {
2363 gService
->mOfflineDevice
->SetCacheParentDirectory(gService
->mObserver
->OfflineCacheParentDirectory());
2364 gService
->mOfflineDevice
->SetCapacity(gService
->mObserver
->OfflineCacheCapacity());
2366 // XXX initialization of mOfflineDevice could be made lazily, if mEnableOfflineDevice is false
2367 nsresult rv
= gService
->mOfflineDevice
->Init();
2368 if (NS_FAILED(rv
)) {
2369 NS_ERROR("nsCacheService::OnProfileChanged: Re-initializing offline device failed");
2370 gService
->mEnableOfflineDevice
= false;
2371 // XXX delete mOfflineDevice?
2375 // If memoryDevice exists, reset its size to the new profile
2376 if (gService
->mMemoryDevice
) {
2377 if (gService
->mEnableMemoryDevice
) {
2378 // make sure that capacity is reset to the right value
2379 int32_t capacity
= gService
->mObserver
->MemoryCacheCapacity();
2380 CACHE_LOG_DEBUG(("Resetting memory device capacity to %d\n",
2382 gService
->mMemoryDevice
->SetCapacity(capacity
);
2384 // tell memory device to evict everything
2385 CACHE_LOG_DEBUG(("memory device disabled\n"));
2386 gService
->mMemoryDevice
->SetCapacity(0);
2387 // Don't delete memory device, because some entries may be active still...
2394 nsCacheService::SetDiskCacheEnabled(bool enabled
)
2396 if (!gService
) return;
2397 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSCACHESERVICE_SETDISKCACHEENABLED
));
2398 gService
->mEnableDiskDevice
= enabled
;
2403 nsCacheService::SetDiskCacheCapacity(int32_t capacity
)
2405 if (!gService
) return;
2406 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSCACHESERVICE_SETDISKCACHECAPACITY
));
2408 if (gService
->mDiskDevice
) {
2409 gService
->mDiskDevice
->SetCapacity(capacity
);
2412 gService
->mEnableDiskDevice
= gService
->mObserver
->DiskCacheEnabled();
2416 nsCacheService::SetDiskCacheMaxEntrySize(int32_t maxSize
)
2418 if (!gService
) return;
2419 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSCACHESERVICE_SETDISKCACHEMAXENTRYSIZE
));
2421 if (gService
->mDiskDevice
) {
2422 gService
->mDiskDevice
->SetMaxEntrySize(maxSize
);
2427 nsCacheService::SetMemoryCacheMaxEntrySize(int32_t maxSize
)
2429 if (!gService
) return;
2430 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSCACHESERVICE_SETMEMORYCACHEMAXENTRYSIZE
));
2432 if (gService
->mMemoryDevice
) {
2433 gService
->mMemoryDevice
->SetMaxEntrySize(maxSize
);
2438 nsCacheService::SetOfflineCacheEnabled(bool enabled
)
2440 if (!gService
) return;
2441 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSCACHESERVICE_SETOFFLINECACHEENABLED
));
2442 gService
->mEnableOfflineDevice
= enabled
;
2446 nsCacheService::SetOfflineCacheCapacity(int32_t capacity
)
2448 if (!gService
) return;
2449 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSCACHESERVICE_SETOFFLINECACHECAPACITY
));
2451 if (gService
->mOfflineDevice
) {
2452 gService
->mOfflineDevice
->SetCapacity(capacity
);
2455 gService
->mEnableOfflineDevice
= gService
->mObserver
->OfflineCacheEnabled();
2460 nsCacheService::SetMemoryCache()
2462 if (!gService
) return;
2464 CACHE_LOG_DEBUG(("nsCacheService::SetMemoryCache"));
2466 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSCACHESERVICE_SETMEMORYCACHE
));
2468 gService
->mEnableMemoryDevice
= gService
->mObserver
->MemoryCacheEnabled();
2470 if (gService
->mEnableMemoryDevice
) {
2471 if (gService
->mMemoryDevice
) {
2472 int32_t capacity
= gService
->mObserver
->MemoryCacheCapacity();
2473 // make sure that capacity is reset to the right value
2474 CACHE_LOG_DEBUG(("Resetting memory device capacity to %d\n",
2476 gService
->mMemoryDevice
->SetCapacity(capacity
);
2479 if (gService
->mMemoryDevice
) {
2480 // tell memory device to evict everything
2481 CACHE_LOG_DEBUG(("memory device disabled\n"));
2482 gService
->mMemoryDevice
->SetCapacity(0);
2483 // Don't delete memory device, because some entries may be active still...
2489 /******************************************************************************
2490 * static methods for nsCacheEntryDescriptor
2491 *****************************************************************************/
2493 nsCacheService::CloseDescriptor(nsCacheEntryDescriptor
* descriptor
)
2495 // ask entry to remove descriptor
2496 nsCacheEntry
* entry
= descriptor
->CacheEntry();
2498 bool stillActive
= entry
->RemoveDescriptor(descriptor
, &doomEntry
);
2500 if (!entry
->IsValid()) {
2501 gService
->ProcessPendingRequests(entry
);
2505 gService
->DoomEntry_Internal(entry
, true);
2510 gService
->DeactivateEntry(entry
);
2516 nsCacheService::GetFileForEntry(nsCacheEntry
* entry
,
2519 nsCacheDevice
* device
= gService
->EnsureEntryHasDevice(entry
);
2520 if (!device
) return NS_ERROR_UNEXPECTED
;
2522 return device
->GetFileForEntry(entry
, result
);
2527 nsCacheService::OpenInputStreamForEntry(nsCacheEntry
* entry
,
2528 nsCacheAccessMode mode
,
2530 nsIInputStream
** result
)
2532 nsCacheDevice
* device
= gService
->EnsureEntryHasDevice(entry
);
2533 if (!device
) return NS_ERROR_UNEXPECTED
;
2535 return device
->OpenInputStreamForEntry(entry
, mode
, offset
, result
);
2539 nsCacheService::OpenOutputStreamForEntry(nsCacheEntry
* entry
,
2540 nsCacheAccessMode mode
,
2542 nsIOutputStream
** result
)
2544 nsCacheDevice
* device
= gService
->EnsureEntryHasDevice(entry
);
2545 if (!device
) return NS_ERROR_UNEXPECTED
;
2547 return device
->OpenOutputStreamForEntry(entry
, mode
, offset
, result
);
2552 nsCacheService::OnDataSizeChange(nsCacheEntry
* entry
, int32_t deltaSize
)
2554 nsCacheDevice
* device
= gService
->EnsureEntryHasDevice(entry
);
2555 if (!device
) return NS_ERROR_UNEXPECTED
;
2557 return device
->OnDataSizeChange(entry
, deltaSize
);
2561 nsCacheService::Lock(mozilla::Telemetry::ID mainThreadLockerID
)
2563 mozilla::Telemetry::ID lockerID
;
2564 mozilla::Telemetry::ID generalID
;
2566 if (NS_IsMainThread()) {
2567 lockerID
= mainThreadLockerID
;
2568 generalID
= mozilla::Telemetry::CACHE_SERVICE_LOCK_WAIT_MAINTHREAD_2
;
2570 lockerID
= mozilla::Telemetry::HistogramCount
;
2571 generalID
= mozilla::Telemetry::CACHE_SERVICE_LOCK_WAIT_2
;
2574 TimeStamp
start(TimeStamp::Now());
2576 gService
->mLock
.Lock();
2578 TimeStamp
stop(TimeStamp::Now());
2580 // Telemetry isn't thread safe on its own, but this is OK because we're
2581 // protecting it with the cache lock.
2582 if (lockerID
!= mozilla::Telemetry::HistogramCount
) {
2583 mozilla::Telemetry::AccumulateTimeDelta(lockerID
, start
, stop
);
2585 mozilla::Telemetry::AccumulateTimeDelta(generalID
, start
, stop
);
2589 nsCacheService::Unlock()
2591 gService
->mLock
.AssertCurrentThreadOwns();
2593 nsTArray
<nsISupports
*> doomed
;
2594 doomed
.SwapElements(gService
->mDoomedObjects
);
2596 gService
->mLock
.Unlock();
2598 for (uint32_t i
= 0; i
< doomed
.Length(); ++i
)
2599 doomed
[i
]->Release();
2603 nsCacheService::ReleaseObject_Locked(nsISupports
* obj
,
2604 nsIEventTarget
* target
)
2606 gService
->mLock
.AssertCurrentThreadOwns();
2609 if (!target
|| (NS_SUCCEEDED(target
->IsOnCurrentThread(&isCur
)) && isCur
)) {
2610 gService
->mDoomedObjects
.AppendElement(obj
);
2612 NS_ProxyRelease(target
, obj
);
2618 nsCacheService::SetCacheElement(nsCacheEntry
* entry
, nsISupports
* element
)
2620 entry
->SetData(element
);
2627 nsCacheService::ValidateEntry(nsCacheEntry
* entry
)
2629 nsCacheDevice
* device
= gService
->EnsureEntryHasDevice(entry
);
2630 if (!device
) return NS_ERROR_UNEXPECTED
;
2633 nsresult rv
= gService
->ProcessPendingRequests(entry
);
2634 NS_ASSERTION(rv
== NS_OK
, "ProcessPendingRequests failed.");
2635 // XXX what else should be done?
2642 nsCacheService::CacheCompressionLevel()
2644 int32_t level
= gService
->mObserver
->CacheCompressionLevel();
2650 nsCacheService::DeactivateEntry(nsCacheEntry
* entry
)
2652 CACHE_LOG_DEBUG(("Deactivating entry %p\n", entry
));
2653 nsresult rv
= NS_OK
;
2654 NS_ASSERTION(entry
->IsNotInUse(), "### deactivating an entry while in use!");
2655 nsCacheDevice
* device
= nullptr;
2657 if (mMaxDataSize
< entry
->DataSize() ) mMaxDataSize
= entry
->DataSize();
2658 if (mMaxMetaSize
< entry
->MetaDataSize() ) mMaxMetaSize
= entry
->MetaDataSize();
2660 if (entry
->IsDoomed()) {
2661 // remove from Doomed list
2662 PR_REMOVE_AND_INIT_LINK(entry
);
2663 } else if (entry
->IsActive()) {
2664 // remove from active entries
2665 mActiveEntries
.RemoveEntry(entry
);
2666 CACHE_LOG_DEBUG(("Removed deactivated entry %p from mActiveEntries\n",
2668 entry
->MarkInactive();
2670 // bind entry if necessary to store meta-data
2671 device
= EnsureEntryHasDevice(entry
);
2673 CACHE_LOG_DEBUG(("DeactivateEntry: unable to bind active "
2676 NS_WARNING("DeactivateEntry: unable to bind active entry\n");
2680 // if mInitialized == false,
2681 // then we're shutting down and this state is okay.
2682 NS_ASSERTION(!mInitialized
, "DeactivateEntry: bad cache entry state.");
2685 device
= entry
->CacheDevice();
2687 rv
= device
->DeactivateEntry(entry
);
2688 if (NS_FAILED(rv
)) {
2689 // increment deactivate failure count
2690 ++mDeactivateFailures
;
2693 // increment deactivating unbound entry statistic
2694 ++mDeactivatedUnboundEntries
;
2695 delete entry
; // because no one else will
2701 nsCacheService::ProcessPendingRequests(nsCacheEntry
* entry
)
2703 nsresult rv
= NS_OK
;
2704 nsCacheRequest
* request
= (nsCacheRequest
*)PR_LIST_HEAD(&entry
->mRequestQ
);
2705 nsCacheRequest
* nextRequest
;
2706 bool newWriter
= false;
2708 CACHE_LOG_DEBUG(("ProcessPendingRequests for %sinitialized %s %salid entry %p\n",
2709 (entry
->IsInitialized()?"" : "Un"),
2710 (entry
->IsDoomed()?"DOOMED" : ""),
2711 (entry
->IsValid()? "V":"Inv"), entry
));
2713 if (request
== &entry
->mRequestQ
) return NS_OK
; // no queued requests
2715 if (!entry
->IsDoomed() && entry
->IsInvalid()) {
2716 // 1st descriptor closed w/o MarkValid()
2717 NS_ASSERTION(PR_CLIST_IS_EMPTY(&entry
->mDescriptorQ
), "shouldn't be here with open descriptors");
2720 // verify no ACCESS_WRITE requests(shouldn't have any of these)
2721 while (request
!= &entry
->mRequestQ
) {
2722 NS_ASSERTION(request
->AccessRequested() != nsICache::ACCESS_WRITE
,
2723 "ACCESS_WRITE request should have been given a new entry");
2724 request
= (nsCacheRequest
*)PR_NEXT_LINK(request
);
2726 request
= (nsCacheRequest
*)PR_LIST_HEAD(&entry
->mRequestQ
);
2728 // find first request with ACCESS_READ_WRITE (if any) and promote it to 1st writer
2729 while (request
!= &entry
->mRequestQ
) {
2730 if (request
->AccessRequested() == nsICache::ACCESS_READ_WRITE
) {
2732 CACHE_LOG_DEBUG((" promoting request %p to 1st writer\n", request
));
2736 request
= (nsCacheRequest
*)PR_NEXT_LINK(request
);
2739 if (request
== &entry
->mRequestQ
) // no requests asked for ACCESS_READ_WRITE, back to top
2740 request
= (nsCacheRequest
*)PR_LIST_HEAD(&entry
->mRequestQ
);
2742 // XXX what should we do if there are only READ requests in queue?
2743 // XXX serialize their accesses, give them only read access, but force them to check validate flag?
2744 // XXX or do readers simply presume the entry is valid
2745 // See fix for bug #467392 below
2748 nsCacheAccessMode accessGranted
= nsICache::ACCESS_NONE
;
2750 while (request
!= &entry
->mRequestQ
) {
2751 nextRequest
= (nsCacheRequest
*)PR_NEXT_LINK(request
);
2752 CACHE_LOG_DEBUG((" %sync request %p for %p\n",
2753 (request
->mListener
?"As":"S"), request
, entry
));
2755 if (request
->mListener
) {
2758 PR_REMOVE_AND_INIT_LINK(request
);
2760 if (entry
->IsDoomed()) {
2761 rv
= ProcessRequest(request
, false, nullptr);
2762 if (rv
== NS_ERROR_CACHE_WAIT_FOR_VALIDATION
)
2767 if (NS_FAILED(rv
)) {
2770 } else if (entry
->IsValid() || newWriter
) {
2771 rv
= entry
->RequestAccess(request
, &accessGranted
);
2772 NS_ASSERTION(NS_SUCCEEDED(rv
),
2773 "if entry is valid, RequestAccess must succeed.");
2774 // XXX if (newWriter) NS_ASSERTION( accessGranted == request->AccessRequested(), "why not?");
2776 // entry->CreateDescriptor dequeues request, and queues descriptor
2777 nsICacheEntryDescriptor
*descriptor
= nullptr;
2778 rv
= entry
->CreateDescriptor(request
,
2782 // post call to listener to report error or descriptor
2783 rv
= NotifyListener(request
, descriptor
, accessGranted
, rv
);
2785 if (NS_FAILED(rv
)) {
2790 // read-only request to an invalid entry - need to wait for
2791 // the entry to become valid so we post an event to process
2792 // the request again later (bug #467392)
2793 nsCOMPtr
<nsIRunnable
> ev
=
2794 new nsProcessRequestEvent(request
);
2795 rv
= DispatchToCacheIOThread(ev
);
2796 if (NS_FAILED(rv
)) {
2797 delete request
; // avoid leak
2802 // Synchronous request
2805 if (newWriter
) break; // process remaining requests after validation
2806 request
= nextRequest
;
2813 nsCacheService::IsDoomListEmpty()
2815 nsCacheEntry
* entry
= (nsCacheEntry
*)PR_LIST_HEAD(&mDoomedEntries
);
2816 return &mDoomedEntries
== entry
;
2820 nsCacheService::ClearDoomList()
2822 nsCacheEntry
* entry
= (nsCacheEntry
*)PR_LIST_HEAD(&mDoomedEntries
);
2824 while (entry
!= &mDoomedEntries
) {
2825 nsCacheEntry
* next
= (nsCacheEntry
*)PR_NEXT_LINK(entry
);
2827 entry
->DetachDescriptors();
2828 DeactivateEntry(entry
);
2834 nsCacheService::GetActiveEntries(PLDHashTable
* table
,
2835 PLDHashEntryHdr
* hdr
,
2839 static_cast<nsVoidArray
*>(arg
)->AppendElement(
2840 ((nsCacheEntryHashTableEntry
*)hdr
)->cacheEntry
);
2841 return PL_DHASH_NEXT
;
2844 struct ActiveEntryArgs
2846 nsTArray
<nsCacheEntry
*>* mActiveArray
;
2847 nsCacheService::DoomCheckFn mCheckFn
;
2851 nsCacheService::DoomActiveEntries(DoomCheckFn check
)
2853 nsAutoTArray
<nsCacheEntry
*, 8> array
;
2854 ActiveEntryArgs args
= { &array
, check
};
2856 mActiveEntries
.VisitEntries(RemoveActiveEntry
, &args
);
2858 uint32_t count
= array
.Length();
2859 for (uint32_t i
=0; i
< count
; ++i
)
2860 DoomEntry_Internal(array
[i
], true);
2864 nsCacheService::RemoveActiveEntry(PLDHashTable
* table
,
2865 PLDHashEntryHdr
* hdr
,
2869 nsCacheEntry
* entry
= ((nsCacheEntryHashTableEntry
*)hdr
)->cacheEntry
;
2870 NS_ASSERTION(entry
, "### active entry = nullptr!");
2872 ActiveEntryArgs
* args
= static_cast<ActiveEntryArgs
*>(arg
);
2873 if (args
->mCheckFn
&& !args
->mCheckFn(entry
))
2874 return PL_DHASH_NEXT
;
2876 NS_ASSERTION(args
->mActiveArray
, "### array = nullptr!");
2877 args
->mActiveArray
->AppendElement(entry
);
2879 // entry is being removed from the active entry list
2880 entry
->MarkInactive();
2881 return PL_DHASH_REMOVE
; // and continue enumerating
2886 nsCacheService::CloseAllStreams()
2888 nsTArray
<nsRefPtr
<nsCacheEntryDescriptor::nsInputStreamWrapper
> > inputs
;
2889 nsTArray
<nsRefPtr
<nsCacheEntryDescriptor::nsOutputStreamWrapper
> > outputs
;
2892 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSCACHESERVICE_CLOSEALLSTREAMS
));
2894 nsVoidArray entries
;
2897 // make sure there is no active entry
2898 mActiveEntries
.VisitEntries(GetActiveEntries
, &entries
);
2899 NS_ASSERTION(entries
.Count() == 0, "Bad state");
2902 // Get doomed entries
2903 nsCacheEntry
* entry
= (nsCacheEntry
*)PR_LIST_HEAD(&mDoomedEntries
);
2904 while (entry
!= &mDoomedEntries
) {
2905 nsCacheEntry
* next
= (nsCacheEntry
*)PR_NEXT_LINK(entry
);
2906 entries
.AppendElement(entry
);
2910 // Iterate through all entries and collect input and output streams
2911 for (int32_t i
= 0 ; i
< entries
.Count() ; i
++) {
2912 entry
= static_cast<nsCacheEntry
*>(entries
.ElementAt(i
));
2914 nsTArray
<nsRefPtr
<nsCacheEntryDescriptor
> > descs
;
2915 entry
->GetDescriptors(descs
);
2917 for (uint32_t j
= 0 ; j
< descs
.Length() ; j
++) {
2918 if (descs
[j
]->mOutputWrapper
)
2919 outputs
.AppendElement(descs
[j
]->mOutputWrapper
);
2921 for (int32_t k
= 0 ; k
< descs
[j
]->mInputWrappers
.Count() ; k
++)
2922 inputs
.AppendElement(static_cast<
2923 nsCacheEntryDescriptor::nsInputStreamWrapper
*>(
2924 descs
[j
]->mInputWrappers
[k
]));
2930 for (i
= 0 ; i
< inputs
.Length() ; i
++)
2933 for (i
= 0 ; i
< outputs
.Length() ; i
++)
2934 outputs
[i
]->Close();
2939 nsCacheService::GetClearingEntries()
2942 return gService
->mClearingEntries
;
2946 #if defined(PR_LOGGING)
2948 nsCacheService::LogCacheStatistics()
2950 uint32_t hitPercentage
= (uint32_t)((((double)mCacheHits
) /
2951 ((double)(mCacheHits
+ mCacheMisses
))) * 100);
2952 CACHE_LOG_ALWAYS(("\nCache Service Statistics:\n\n"));
2953 CACHE_LOG_ALWAYS((" TotalEntries = %d\n", mTotalEntries
));
2954 CACHE_LOG_ALWAYS((" Cache Hits = %d\n", mCacheHits
));
2955 CACHE_LOG_ALWAYS((" Cache Misses = %d\n", mCacheMisses
));
2956 CACHE_LOG_ALWAYS((" Cache Hit %% = %d%%\n", hitPercentage
));
2957 CACHE_LOG_ALWAYS((" Max Key Length = %d\n", mMaxKeyLength
));
2958 CACHE_LOG_ALWAYS((" Max Meta Size = %d\n", mMaxMetaSize
));
2959 CACHE_LOG_ALWAYS((" Max Data Size = %d\n", mMaxDataSize
));
2960 CACHE_LOG_ALWAYS(("\n"));
2961 CACHE_LOG_ALWAYS((" Deactivate Failures = %d\n",
2962 mDeactivateFailures
));
2963 CACHE_LOG_ALWAYS((" Deactivated Unbound Entries = %d\n",
2964 mDeactivatedUnboundEntries
));
2969 nsCacheService::SetDiskSmartSize()
2971 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSCACHESERVICE_SETDISKSMARTSIZE
));
2973 if (!gService
) return NS_ERROR_NOT_AVAILABLE
;
2975 return gService
->SetDiskSmartSize_Locked();
2979 nsCacheService::SetDiskSmartSize_Locked()
2983 if (!mObserver
->DiskCacheParentDirectory())
2984 return NS_ERROR_NOT_AVAILABLE
;
2987 return NS_ERROR_NOT_AVAILABLE
;
2989 if (!mObserver
->SmartSizeEnabled())
2990 return NS_ERROR_NOT_AVAILABLE
;
2992 nsAutoString cachePath
;
2993 rv
= mObserver
->DiskCacheParentDirectory()->GetPath(cachePath
);
2994 if (NS_SUCCEEDED(rv
)) {
2995 nsCOMPtr
<nsIRunnable
> event
=
2996 new nsGetSmartSizeEvent(cachePath
, mDiskDevice
->getCacheSize(),
2997 mObserver
->ShouldUseOldMaxSmartSize());
2998 DispatchToCacheIOThread(event
);
3000 return NS_ERROR_FAILURE
;
3007 nsCacheService::MoveOrRemoveDiskCache(nsIFile
*aOldCacheDir
,
3008 nsIFile
*aNewCacheDir
,
3009 const char *aCacheSubdir
)
3012 if (NS_FAILED(aOldCacheDir
->Equals(aNewCacheDir
, &same
)) || same
)
3015 nsCOMPtr
<nsIFile
> aOldCacheSubdir
;
3016 aOldCacheDir
->Clone(getter_AddRefs(aOldCacheSubdir
));
3018 nsresult rv
= aOldCacheSubdir
->AppendNative(
3019 nsDependentCString(aCacheSubdir
));
3024 if (NS_FAILED(aOldCacheSubdir
->Exists(&exists
)) || !exists
)
3027 nsCOMPtr
<nsIFile
> aNewCacheSubdir
;
3028 aNewCacheDir
->Clone(getter_AddRefs(aNewCacheSubdir
));
3030 rv
= aNewCacheSubdir
->AppendNative(nsDependentCString(aCacheSubdir
));
3034 nsAutoCString newPath
;
3035 rv
= aNewCacheSubdir
->GetNativePath(newPath
);
3039 if (NS_SUCCEEDED(aNewCacheSubdir
->Exists(&exists
)) && !exists
) {
3040 // New cache directory does not exist, try to move the old one here
3041 // rename needs an empty target directory
3042 rv
= aNewCacheSubdir
->Create(nsIFile::DIRECTORY_TYPE
, 0777);
3043 if (NS_SUCCEEDED(rv
)) {
3044 nsAutoCString oldPath
;
3045 rv
= aOldCacheSubdir
->GetNativePath(oldPath
);
3048 if(rename(oldPath
.get(), newPath
.get()) == 0)
3053 // Delay delete by 1 minute to avoid IO thrash on startup.
3054 nsDeleteDir::DeleteDir(aOldCacheSubdir
, false, 60000);
3058 IsEntryPrivate(nsCacheEntry
* entry
)
3060 return entry
->IsPrivate();
3064 nsCacheService::LeavePrivateBrowsing()
3066 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSCACHESERVICE_LEAVEPRIVATEBROWSING
));
3068 gService
->DoomActiveEntries(IsEntryPrivate
);
3070 if (gService
->mMemoryDevice
) {
3071 // clear memory cache
3072 gService
->mMemoryDevice
->EvictPrivateEntries();