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/ArrayUtils.h"
8 #include "mozilla/Attributes.h"
9 #include "mozilla/Assertions.h"
10 #include "mozilla/DebugOnly.h"
12 #include "necko-config.h"
15 #include "nsCacheService.h"
16 #include "nsCacheRequest.h"
17 #include "nsCacheEntry.h"
18 #include "nsCacheEntryDescriptor.h"
19 #include "nsCacheDevice.h"
20 #include "nsMemoryCacheDevice.h"
21 #include "nsICacheVisitor.h"
22 #include "nsDiskCacheDevice.h"
23 #include "nsDiskCacheDeviceSQL.h"
24 #include "nsCacheUtils.h"
25 #include "../cache2/CacheObserver.h"
27 #include "nsIObserverService.h"
28 #include "nsIPrefService.h"
29 #include "nsIPrefBranch.h"
31 #include "nsIOService.h"
32 #include "nsDirectoryServiceDefs.h"
33 #include "nsAppDirectoryServiceDefs.h"
34 #include "nsThreadUtils.h"
35 #include "nsProxyRelease.h"
36 #include "nsVoidArray.h"
37 #include "nsDeleteDir.h"
39 #include <math.h> // for log()
40 #include "mozilla/Services.h"
42 #include "mozIStorageService.h"
44 #include "mozilla/net/NeckoCommon.h"
45 #include "mozilla/VisualEventTracer.h"
48 using namespace mozilla
;
50 /******************************************************************************
51 * nsCacheProfilePrefObserver
52 *****************************************************************************/
53 #define DISK_CACHE_ENABLE_PREF "browser.cache.disk.enable"
54 #define DISK_CACHE_DIR_PREF "browser.cache.disk.parent_directory"
55 #define DISK_CACHE_SMART_SIZE_FIRST_RUN_PREF\
56 "browser.cache.disk.smart_size.first_run"
57 #define DISK_CACHE_SMART_SIZE_ENABLED_PREF \
58 "browser.cache.disk.smart_size.enabled"
59 #define DISK_CACHE_SMART_SIZE_PREF "browser.cache.disk.smart_size_cached_value"
60 #define DISK_CACHE_CAPACITY_PREF "browser.cache.disk.capacity"
61 #define DISK_CACHE_MAX_ENTRY_SIZE_PREF "browser.cache.disk.max_entry_size"
62 #define DISK_CACHE_CAPACITY 256000
64 #define DISK_CACHE_USE_OLD_MAX_SMART_SIZE_PREF \
65 "browser.cache.disk.smart_size.use_old_max"
67 #define OFFLINE_CACHE_ENABLE_PREF "browser.cache.offline.enable"
68 #define OFFLINE_CACHE_DIR_PREF "browser.cache.offline.parent_directory"
69 #define OFFLINE_CACHE_CAPACITY_PREF "browser.cache.offline.capacity"
70 #define OFFLINE_CACHE_CAPACITY 512000
72 #define MEMORY_CACHE_ENABLE_PREF "browser.cache.memory.enable"
73 #define MEMORY_CACHE_CAPACITY_PREF "browser.cache.memory.capacity"
74 #define MEMORY_CACHE_MAX_ENTRY_SIZE_PREF "browser.cache.memory.max_entry_size"
76 #define CACHE_COMPRESSION_LEVEL_PREF "browser.cache.compression_level"
77 #define CACHE_COMPRESSION_LEVEL 1
79 #define SANITIZE_ON_SHUTDOWN_PREF "privacy.sanitize.sanitizeOnShutdown"
80 #define CLEAR_ON_SHUTDOWN_PREF "privacy.clearOnShutdown.cache"
82 static const char * observerList
[] = {
83 "profile-before-change",
85 NS_XPCOM_SHUTDOWN_OBSERVER_ID
,
86 "last-pb-context-exited",
87 "suspend_process_notification",
88 "resume_process_notification"
91 static const char * prefList
[] = {
92 DISK_CACHE_ENABLE_PREF
,
93 DISK_CACHE_SMART_SIZE_ENABLED_PREF
,
94 DISK_CACHE_CAPACITY_PREF
,
96 DISK_CACHE_MAX_ENTRY_SIZE_PREF
,
97 DISK_CACHE_USE_OLD_MAX_SMART_SIZE_PREF
,
98 OFFLINE_CACHE_ENABLE_PREF
,
99 OFFLINE_CACHE_CAPACITY_PREF
,
100 OFFLINE_CACHE_DIR_PREF
,
101 MEMORY_CACHE_ENABLE_PREF
,
102 MEMORY_CACHE_CAPACITY_PREF
,
103 MEMORY_CACHE_MAX_ENTRY_SIZE_PREF
,
104 CACHE_COMPRESSION_LEVEL_PREF
,
105 SANITIZE_ON_SHUTDOWN_PREF
,
106 CLEAR_ON_SHUTDOWN_PREF
109 // Cache sizes, in KB
110 const int32_t DEFAULT_CACHE_SIZE
= 250 * 1024; // 250 MB
112 const int32_t MAX_CACHE_SIZE
= 200 * 1024; // 200 MB
113 const int32_t OLD_MAX_CACHE_SIZE
= 200 * 1024; // 200 MB
115 const int32_t MAX_CACHE_SIZE
= 350 * 1024; // 350 MB
116 const int32_t OLD_MAX_CACHE_SIZE
= 1024 * 1024; // 1 GB
118 // Default cache size was 50 MB for many years until FF 4:
119 const int32_t PRE_GECKO_2_0_DEFAULT_CACHE_SIZE
= 50 * 1024;
121 class nsCacheProfilePrefObserver
: public nsIObserver
123 virtual ~nsCacheProfilePrefObserver() {}
126 NS_DECL_THREADSAFE_ISUPPORTS
129 nsCacheProfilePrefObserver()
130 : mHaveProfile(false)
131 , mDiskCacheEnabled(false)
132 , mDiskCacheCapacity(0)
133 , mDiskCacheMaxEntrySize(-1) // -1 means "no limit"
134 , mSmartSizeEnabled(false)
135 , mShouldUseOldMaxSmartSize(false)
136 , mOfflineCacheEnabled(false)
137 , mOfflineCacheCapacity(0)
138 , mMemoryCacheEnabled(true)
139 , mMemoryCacheCapacity(-1)
140 , mMemoryCacheMaxEntrySize(-1) // -1 means "no limit"
141 , mCacheCompressionLevel(CACHE_COMPRESSION_LEVEL
)
142 , mSanitizeOnShutdown(false)
143 , mClearCacheOnShutdown(false)
149 nsresult
ReadPrefs(nsIPrefBranch
* branch
);
151 bool DiskCacheEnabled();
152 int32_t DiskCacheCapacity() { return mDiskCacheCapacity
; }
153 void SetDiskCacheCapacity(int32_t);
154 int32_t DiskCacheMaxEntrySize() { return mDiskCacheMaxEntrySize
; }
155 nsIFile
* DiskCacheParentDirectory() { return mDiskCacheParentDirectory
; }
156 bool SmartSizeEnabled() { return mSmartSizeEnabled
; }
158 bool ShouldUseOldMaxSmartSize() { return mShouldUseOldMaxSmartSize
; }
159 void SetUseNewMaxSmartSize(bool useNew
) { mShouldUseOldMaxSmartSize
= !useNew
; }
161 bool OfflineCacheEnabled();
162 int32_t OfflineCacheCapacity() { return mOfflineCacheCapacity
; }
163 nsIFile
* OfflineCacheParentDirectory() { return mOfflineCacheParentDirectory
; }
165 bool MemoryCacheEnabled();
166 int32_t MemoryCacheCapacity();
167 int32_t MemoryCacheMaxEntrySize() { return mMemoryCacheMaxEntrySize
; }
169 int32_t CacheCompressionLevel();
171 bool SanitizeAtShutdown() { return mSanitizeOnShutdown
&& mClearCacheOnShutdown
; }
173 static uint32_t GetSmartCacheSize(const nsAString
& cachePath
,
174 uint32_t currentSize
,
175 bool shouldUseOldMaxSmartSize
);
177 bool PermittedToSmartSize(nsIPrefBranch
*, bool firstRun
);
182 bool mDiskCacheEnabled
;
183 int32_t mDiskCacheCapacity
; // in kilobytes
184 int32_t mDiskCacheMaxEntrySize
; // in kilobytes
185 nsCOMPtr
<nsIFile
> mDiskCacheParentDirectory
;
186 bool mSmartSizeEnabled
;
188 bool mShouldUseOldMaxSmartSize
;
190 bool mOfflineCacheEnabled
;
191 int32_t mOfflineCacheCapacity
; // in kilobytes
192 nsCOMPtr
<nsIFile
> mOfflineCacheParentDirectory
;
194 bool mMemoryCacheEnabled
;
195 int32_t mMemoryCacheCapacity
; // in kilobytes
196 int32_t mMemoryCacheMaxEntrySize
; // in kilobytes
198 int32_t mCacheCompressionLevel
;
200 bool mSanitizeOnShutdown
;
201 bool mClearCacheOnShutdown
;
204 NS_IMPL_ISUPPORTS(nsCacheProfilePrefObserver
, nsIObserver
)
206 class nsSetDiskSmartSizeCallback MOZ_FINAL
: public nsITimerCallback
208 ~nsSetDiskSmartSizeCallback() {}
211 NS_DECL_THREADSAFE_ISUPPORTS
213 NS_IMETHOD
Notify(nsITimer
* aTimer
) {
214 if (nsCacheService::gService
) {
215 nsCacheServiceAutoLock
autoLock(LOCK_TELEM(NSSETDISKSMARTSIZECALLBACK_NOTIFY
));
216 nsCacheService::gService
->SetDiskSmartSize_Locked();
217 nsCacheService::gService
->mSmartSizeTimer
= nullptr;
223 NS_IMPL_ISUPPORTS(nsSetDiskSmartSizeCallback
, nsITimerCallback
)
225 // Runnable sent to main thread after the cache IO thread calculates available
226 // disk space, so that there is no race in setting mDiskCacheCapacity.
227 class nsSetSmartSizeEvent
: public nsRunnable
230 explicit nsSetSmartSizeEvent(int32_t smartSize
)
231 : mSmartSize(smartSize
) {}
235 NS_ASSERTION(NS_IsMainThread(),
236 "Setting smart size data off the main thread");
238 // Main thread may have already called nsCacheService::Shutdown
239 if (!nsCacheService::IsInitialized())
240 return NS_ERROR_NOT_AVAILABLE
;
242 // Ensure smart sizing wasn't switched off while event was pending.
243 // It is safe to access the observer without the lock since we are
244 // on the main thread and the value changes only on the main thread.
245 if (!nsCacheService::gService
->mObserver
->SmartSizeEnabled())
248 nsCacheService::SetDiskCacheCapacity(mSmartSize
);
250 nsCOMPtr
<nsIPrefBranch
> ps
= do_GetService(NS_PREFSERVICE_CONTRACTID
);
252 NS_FAILED(ps
->SetIntPref(DISK_CACHE_SMART_SIZE_PREF
, mSmartSize
)))
253 NS_WARNING("Failed to set smart size pref");
263 // Runnable sent from main thread to cacheIO thread
264 class nsGetSmartSizeEvent
: public nsRunnable
267 nsGetSmartSizeEvent(const nsAString
& cachePath
, uint32_t currentSize
,
268 bool shouldUseOldMaxSmartSize
)
269 : mCachePath(cachePath
)
270 , mCurrentSize(currentSize
)
271 , mShouldUseOldMaxSmartSize(shouldUseOldMaxSmartSize
)
274 // Calculates user's disk space available on a background thread and
275 // dispatches this value back to the main thread.
279 size
= nsCacheProfilePrefObserver::GetSmartCacheSize(mCachePath
,
281 mShouldUseOldMaxSmartSize
);
282 NS_DispatchToMainThread(new nsSetSmartSizeEvent(size
));
288 uint32_t mCurrentSize
;
289 bool mShouldUseOldMaxSmartSize
;
292 class nsBlockOnCacheThreadEvent
: public nsRunnable
{
294 nsBlockOnCacheThreadEvent()
299 nsCacheServiceAutoLock
autoLock(LOCK_TELEM(NSBLOCKONCACHETHREADEVENT_RUN
));
301 CACHE_LOG_DEBUG(("nsBlockOnCacheThreadEvent [%p]\n", this));
303 nsCacheService::gService
->mCondVar
.Notify();
310 nsCacheProfilePrefObserver::Install()
312 // install profile-change observer
313 nsCOMPtr
<nsIObserverService
> observerService
=
314 mozilla::services::GetObserverService();
315 if (!observerService
)
316 return NS_ERROR_FAILURE
;
318 nsresult rv
, rv2
= NS_OK
;
319 for (unsigned int i
=0; i
<ArrayLength(observerList
); i
++) {
320 rv
= observerService
->AddObserver(this, observerList
[i
], false);
325 // install preferences observer
326 nsCOMPtr
<nsIPrefBranch
> branch
= do_GetService(NS_PREFSERVICE_CONTRACTID
);
327 if (!branch
) return NS_ERROR_FAILURE
;
329 for (unsigned int i
=0; i
<ArrayLength(prefList
); i
++) {
330 rv
= branch
->AddObserver(prefList
[i
], this, false);
335 // Determine if we have a profile already
336 // Install() is called *after* the profile-after-change notification
337 // when there is only a single profile, or it is specified on the
338 // commandline at startup.
339 // In that case, we detect the presence of a profile by the existence
340 // of the NS_APP_USER_PROFILE_50_DIR directory.
342 nsCOMPtr
<nsIFile
> directory
;
343 rv
= NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR
,
344 getter_AddRefs(directory
));
345 if (NS_SUCCEEDED(rv
))
348 rv
= ReadPrefs(branch
);
349 NS_ENSURE_SUCCESS(rv
, rv
);
356 nsCacheProfilePrefObserver::Remove()
358 // remove Observer Service observers
359 nsCOMPtr
<nsIObserverService
> obs
=
360 mozilla::services::GetObserverService();
362 for (unsigned int i
=0; i
<ArrayLength(observerList
); i
++) {
363 obs
->RemoveObserver(this, observerList
[i
]);
367 // remove Pref Service observers
368 nsCOMPtr
<nsIPrefBranch
> prefs
=
369 do_GetService(NS_PREFSERVICE_CONTRACTID
);
372 for (unsigned int i
=0; i
<ArrayLength(prefList
); i
++)
373 prefs
->RemoveObserver(prefList
[i
], this); // remove cache pref observers
377 nsCacheProfilePrefObserver::SetDiskCacheCapacity(int32_t capacity
)
379 mDiskCacheCapacity
= std::max(0, capacity
);
384 nsCacheProfilePrefObserver::Observe(nsISupports
* subject
,
386 const char16_t
* data_unicode
)
389 NS_ConvertUTF16toUTF8
data(data_unicode
);
390 CACHE_LOG_ALWAYS(("Observe [topic=%s data=%s]\n", topic
, data
.get()));
392 if (!nsCacheService::IsInitialized()) {
393 if (!strcmp("resume_process_notification", topic
)) {
394 // A suspended process has a closed cache, so re-open it here.
395 nsCacheService::GlobalInstance()->Init();
400 if (!strcmp(NS_XPCOM_SHUTDOWN_OBSERVER_ID
, topic
)) {
401 // xpcom going away, shutdown cache service
402 nsCacheService::GlobalInstance()->Shutdown();
403 } else if (!strcmp("profile-before-change", topic
)) {
404 // profile before change
405 mHaveProfile
= false;
407 // XXX shutdown devices
408 nsCacheService::OnProfileShutdown(!strcmp("shutdown-cleanse",
411 } else if (!strcmp("suspend_process_notification", topic
)) {
412 // A suspended process may never return, so shutdown the cache to reduce
414 nsCacheService::GlobalInstance()->Shutdown();
415 } else if (!strcmp("profile-do-change", topic
)) {
416 // profile after change
418 nsCOMPtr
<nsIPrefBranch
> branch
= do_GetService(NS_PREFSERVICE_CONTRACTID
);
420 nsCacheService::OnProfileChanged();
422 } else if (!strcmp(NS_PREFBRANCH_PREFCHANGE_TOPIC_ID
, topic
)) {
424 // ignore pref changes until we're done switch profiles
428 nsCOMPtr
<nsIPrefBranch
> branch
= do_QueryInterface(subject
, &rv
);
432 // which preference changed?
433 if (!strcmp(DISK_CACHE_ENABLE_PREF
, data
.get())) {
435 rv
= branch
->GetBoolPref(DISK_CACHE_ENABLE_PREF
,
439 nsCacheService::SetDiskCacheEnabled(DiskCacheEnabled());
441 } else if (!strcmp(DISK_CACHE_CAPACITY_PREF
, data
.get())) {
443 int32_t capacity
= 0;
444 rv
= branch
->GetIntPref(DISK_CACHE_CAPACITY_PREF
, &capacity
);
447 mDiskCacheCapacity
= std::max(0, capacity
);
448 nsCacheService::SetDiskCacheCapacity(mDiskCacheCapacity
);
450 // Update the cache capacity when smart sizing is turned on/off
451 } else if (!strcmp(DISK_CACHE_SMART_SIZE_ENABLED_PREF
, data
.get())) {
452 // Is the update because smartsizing was turned on, or off?
453 rv
= branch
->GetBoolPref(DISK_CACHE_SMART_SIZE_ENABLED_PREF
,
457 int32_t newCapacity
= 0;
458 if (mSmartSizeEnabled
) {
459 nsCacheService::SetDiskSmartSize();
461 // Smart sizing switched off: use user specified size
462 rv
= branch
->GetIntPref(DISK_CACHE_CAPACITY_PREF
, &newCapacity
);
465 mDiskCacheCapacity
= std::max(0, newCapacity
);
466 nsCacheService::SetDiskCacheCapacity(mDiskCacheCapacity
);
468 } else if (!strcmp(DISK_CACHE_USE_OLD_MAX_SMART_SIZE_PREF
, data
.get())) {
469 rv
= branch
->GetBoolPref(DISK_CACHE_USE_OLD_MAX_SMART_SIZE_PREF
,
470 &mShouldUseOldMaxSmartSize
);
473 } else if (!strcmp(DISK_CACHE_MAX_ENTRY_SIZE_PREF
, data
.get())) {
475 rv
= branch
->GetIntPref(DISK_CACHE_MAX_ENTRY_SIZE_PREF
,
480 mDiskCacheMaxEntrySize
= std::max(-1, newMaxSize
);
481 nsCacheService::SetDiskCacheMaxEntrySize(mDiskCacheMaxEntrySize
);
484 } else if (!strcmp(DISK_CACHE_DIR_PREF
, data
.get())) {
485 // XXX We probaby don't want to respond to this pref except after
486 // XXX profile changes. Ideally, there should be somekind of user
487 // XXX notification that the pref change won't take effect until
488 // XXX the next time the profile changes (browser launch)
492 // which preference changed?
493 if (!strcmp(OFFLINE_CACHE_ENABLE_PREF
, data
.get())) {
495 rv
= branch
->GetBoolPref(OFFLINE_CACHE_ENABLE_PREF
,
496 &mOfflineCacheEnabled
);
497 if (NS_FAILED(rv
)) return rv
;
498 nsCacheService::SetOfflineCacheEnabled(OfflineCacheEnabled());
500 } else if (!strcmp(OFFLINE_CACHE_CAPACITY_PREF
, data
.get())) {
502 int32_t capacity
= 0;
503 rv
= branch
->GetIntPref(OFFLINE_CACHE_CAPACITY_PREF
, &capacity
);
504 if (NS_FAILED(rv
)) return rv
;
505 mOfflineCacheCapacity
= std::max(0, capacity
);
506 nsCacheService::SetOfflineCacheCapacity(mOfflineCacheCapacity
);
508 } else if (!strcmp(OFFLINE_CACHE_DIR_PREF
, data
.get())) {
509 // XXX We probaby don't want to respond to this pref except after
510 // XXX profile changes. Ideally, there should be some kind of user
511 // XXX notification that the pref change won't take effect until
512 // XXX the next time the profile changes (browser launch)
516 if (!strcmp(MEMORY_CACHE_ENABLE_PREF
, data
.get())) {
518 rv
= branch
->GetBoolPref(MEMORY_CACHE_ENABLE_PREF
,
519 &mMemoryCacheEnabled
);
522 nsCacheService::SetMemoryCache();
524 } else if (!strcmp(MEMORY_CACHE_CAPACITY_PREF
, data
.get())) {
526 mMemoryCacheCapacity
= -1;
527 (void) branch
->GetIntPref(MEMORY_CACHE_CAPACITY_PREF
,
528 &mMemoryCacheCapacity
);
529 nsCacheService::SetMemoryCache();
530 } else if (!strcmp(MEMORY_CACHE_MAX_ENTRY_SIZE_PREF
, data
.get())) {
532 rv
= branch
->GetIntPref(MEMORY_CACHE_MAX_ENTRY_SIZE_PREF
,
537 mMemoryCacheMaxEntrySize
= std::max(-1, newMaxSize
);
538 nsCacheService::SetMemoryCacheMaxEntrySize(mMemoryCacheMaxEntrySize
);
539 } else if (!strcmp(CACHE_COMPRESSION_LEVEL_PREF
, data
.get())) {
540 mCacheCompressionLevel
= CACHE_COMPRESSION_LEVEL
;
541 (void)branch
->GetIntPref(CACHE_COMPRESSION_LEVEL_PREF
,
542 &mCacheCompressionLevel
);
543 mCacheCompressionLevel
= std::max(0, mCacheCompressionLevel
);
544 mCacheCompressionLevel
= std::min(9, mCacheCompressionLevel
);
545 } else if (!strcmp(SANITIZE_ON_SHUTDOWN_PREF
, data
.get())) {
546 rv
= branch
->GetBoolPref(SANITIZE_ON_SHUTDOWN_PREF
,
547 &mSanitizeOnShutdown
);
550 nsCacheService::SetDiskCacheEnabled(DiskCacheEnabled());
551 } else if (!strcmp(CLEAR_ON_SHUTDOWN_PREF
, data
.get())) {
552 rv
= branch
->GetBoolPref(CLEAR_ON_SHUTDOWN_PREF
,
553 &mClearCacheOnShutdown
);
556 nsCacheService::SetDiskCacheEnabled(DiskCacheEnabled());
558 } else if (!strcmp("last-pb-context-exited", topic
)) {
559 nsCacheService::LeavePrivateBrowsing();
565 // Returns default ("smart") size (in KB) of cache, given available disk space
568 SmartCacheSize(const uint32_t availKB
, bool shouldUseOldMaxSmartSize
)
570 uint32_t maxSize
= shouldUseOldMaxSmartSize
? OLD_MAX_CACHE_SIZE
: MAX_CACHE_SIZE
;
572 if (availKB
> 100 * 1024 * 1024)
573 return maxSize
; // skip computing if we're over 100 GB
575 // Grow/shrink in 10 MB units, deliberately, so that in the common case we
576 // don't shrink cache and evict items every time we startup (it's important
577 // that we don't slow down startup benchmarks).
578 uint32_t sz10MBs
= 0;
579 uint32_t avail10MBs
= availKB
/ (1024*10);
581 // .5% of space above 25 GB
582 if (avail10MBs
> 2500) {
583 sz10MBs
+= static_cast<uint32_t>((avail10MBs
- 2500)*.005);
586 // 1% of space between 7GB -> 25 GB
587 if (avail10MBs
> 700) {
588 sz10MBs
+= static_cast<uint32_t>((avail10MBs
- 700)*.01);
591 // 5% of space between 500 MB -> 7 GB
592 if (avail10MBs
> 50) {
593 sz10MBs
+= static_cast<uint32_t>((avail10MBs
- 50)*.05);
598 // On Android, smaller/older devices may have very little storage and
599 // device owners may be sensitive to storage footprint: Use a smaller
600 // percentage of available space and a smaller minimum.
602 // 20% of space up to 500 MB (10 MB min)
603 sz10MBs
+= std::max
<uint32_t>(1, static_cast<uint32_t>(avail10MBs
* .2));
605 // 40% of space up to 500 MB (50 MB min)
606 sz10MBs
+= std::max
<uint32_t>(5, static_cast<uint32_t>(avail10MBs
* .4));
609 return std::min
<uint32_t>(maxSize
, sz10MBs
* 10 * 1024);
612 /* Computes our best guess for the default size of the user's disk cache,
613 * based on the amount of space they have free on their hard drive.
614 * We use a tiered scheme: the more space available,
615 * the larger the disk cache will be. However, we do not want
616 * to enable the disk cache to grow to an unbounded size, so the larger the
617 * user's available space is, the smaller of a percentage we take. We set a
618 * lower bound of 50MB and an upper bound of 1GB.
621 *@return: The size that the user's disk cache should default to, in kBytes.
624 nsCacheProfilePrefObserver::GetSmartCacheSize(const nsAString
& cachePath
,
625 uint32_t currentSize
,
626 bool shouldUseOldMaxSmartSize
)
628 // Check for free space on device where cache directory lives
631 cacheDirectory (do_CreateInstance(NS_LOCAL_FILE_CONTRACTID
, &rv
));
632 if (NS_FAILED(rv
) || !cacheDirectory
)
633 return DEFAULT_CACHE_SIZE
;
634 rv
= cacheDirectory
->InitWithPath(cachePath
);
636 return DEFAULT_CACHE_SIZE
;
637 int64_t bytesAvailable
;
638 rv
= cacheDirectory
->GetDiskSpaceAvailable(&bytesAvailable
);
640 return DEFAULT_CACHE_SIZE
;
642 return SmartCacheSize(static_cast<uint32_t>((bytesAvailable
/ 1024) +
644 shouldUseOldMaxSmartSize
);
647 /* Determine if we are permitted to dynamically size the user's disk cache based
648 * on their disk space available. We may do this so long as the pref
649 * smart_size.enabled is true.
652 nsCacheProfilePrefObserver::PermittedToSmartSize(nsIPrefBranch
* branch
, bool
657 // check if user has set cache size in the past
659 rv
= branch
->PrefHasUserValue(DISK_CACHE_CAPACITY_PREF
, &userSet
);
660 if (NS_FAILED(rv
)) userSet
= true;
663 // If user explicitly set cache size to be smaller than old default
664 // of 50 MB, then keep user's value. Otherwise use smart sizing.
665 rv
= branch
->GetIntPref(DISK_CACHE_CAPACITY_PREF
, &oldCapacity
);
666 if (oldCapacity
< PRE_GECKO_2_0_DEFAULT_CACHE_SIZE
) {
667 mSmartSizeEnabled
= false;
668 branch
->SetBoolPref(DISK_CACHE_SMART_SIZE_ENABLED_PREF
,
670 return mSmartSizeEnabled
;
673 // Set manual setting to MAX cache size as starting val for any
674 // adjustment by user: (bug 559942 comment 65)
675 int32_t maxSize
= mShouldUseOldMaxSmartSize
? OLD_MAX_CACHE_SIZE
: MAX_CACHE_SIZE
;
676 branch
->SetIntPref(DISK_CACHE_CAPACITY_PREF
, maxSize
);
679 rv
= branch
->GetBoolPref(DISK_CACHE_SMART_SIZE_ENABLED_PREF
,
682 mSmartSizeEnabled
= false;
683 return mSmartSizeEnabled
;
688 nsCacheProfilePrefObserver::ReadPrefs(nsIPrefBranch
* branch
)
692 // read disk cache device prefs
693 mDiskCacheEnabled
= true; // presume disk cache is enabled
694 (void) branch
->GetBoolPref(DISK_CACHE_ENABLE_PREF
, &mDiskCacheEnabled
);
696 mDiskCacheCapacity
= DISK_CACHE_CAPACITY
;
697 (void)branch
->GetIntPref(DISK_CACHE_CAPACITY_PREF
, &mDiskCacheCapacity
);
698 mDiskCacheCapacity
= std::max(0, mDiskCacheCapacity
);
700 (void) branch
->GetIntPref(DISK_CACHE_MAX_ENTRY_SIZE_PREF
,
701 &mDiskCacheMaxEntrySize
);
702 mDiskCacheMaxEntrySize
= std::max(-1, mDiskCacheMaxEntrySize
);
704 (void) branch
->GetComplexValue(DISK_CACHE_DIR_PREF
, // ignore error
706 getter_AddRefs(mDiskCacheParentDirectory
));
708 (void) branch
->GetBoolPref(DISK_CACHE_USE_OLD_MAX_SMART_SIZE_PREF
,
709 &mShouldUseOldMaxSmartSize
);
711 if (!mDiskCacheParentDirectory
) {
712 nsCOMPtr
<nsIFile
> directory
;
714 // try to get the disk cache parent directory
715 rv
= NS_GetSpecialDirectory(NS_APP_CACHE_PARENT_DIR
,
716 getter_AddRefs(directory
));
718 // try to get the profile directory (there may not be a profile yet)
719 nsCOMPtr
<nsIFile
> profDir
;
720 NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR
,
721 getter_AddRefs(profDir
));
722 NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR
,
723 getter_AddRefs(directory
));
727 nsCacheService::MoveOrRemoveDiskCache(profDir
, directory
,
731 // use file cache in build tree only if asked, to avoid cache dir litter
732 if (!directory
&& PR_GetEnv("NECKO_DEV_ENABLE_DISK_CACHE")) {
733 rv
= NS_GetSpecialDirectory(NS_XPCOM_CURRENT_PROCESS_DIR
,
734 getter_AddRefs(directory
));
737 mDiskCacheParentDirectory
= do_QueryInterface(directory
, &rv
);
739 if (mDiskCacheParentDirectory
) {
740 bool firstSmartSizeRun
;
741 rv
= branch
->GetBoolPref(DISK_CACHE_SMART_SIZE_FIRST_RUN_PREF
,
744 firstSmartSizeRun
= false;
745 if (PermittedToSmartSize(branch
, firstSmartSizeRun
)) {
746 // Avoid evictions: use previous cache size until smart size event
747 // updates mDiskCacheCapacity
748 rv
= branch
->GetIntPref(firstSmartSizeRun
?
749 DISK_CACHE_CAPACITY_PREF
:
750 DISK_CACHE_SMART_SIZE_PREF
,
751 &mDiskCacheCapacity
);
753 mDiskCacheCapacity
= DEFAULT_CACHE_SIZE
;
756 if (firstSmartSizeRun
) {
757 // It is no longer our first run
758 rv
= branch
->SetBoolPref(DISK_CACHE_SMART_SIZE_FIRST_RUN_PREF
,
761 NS_WARNING("Failed setting first_run pref in ReadPrefs.");
765 // read offline cache device prefs
766 mOfflineCacheEnabled
= true; // presume offline cache is enabled
767 (void) branch
->GetBoolPref(OFFLINE_CACHE_ENABLE_PREF
,
768 &mOfflineCacheEnabled
);
770 mOfflineCacheCapacity
= OFFLINE_CACHE_CAPACITY
;
771 (void)branch
->GetIntPref(OFFLINE_CACHE_CAPACITY_PREF
,
772 &mOfflineCacheCapacity
);
773 mOfflineCacheCapacity
= std::max(0, mOfflineCacheCapacity
);
775 (void) branch
->GetComplexValue(OFFLINE_CACHE_DIR_PREF
, // ignore error
777 getter_AddRefs(mOfflineCacheParentDirectory
));
779 if (!mOfflineCacheParentDirectory
) {
780 nsCOMPtr
<nsIFile
> directory
;
782 // try to get the offline cache parent directory
783 rv
= NS_GetSpecialDirectory(NS_APP_CACHE_PARENT_DIR
,
784 getter_AddRefs(directory
));
786 // try to get the profile directory (there may not be a profile yet)
787 nsCOMPtr
<nsIFile
> profDir
;
788 NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR
,
789 getter_AddRefs(profDir
));
790 NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR
,
791 getter_AddRefs(directory
));
795 nsCacheService::MoveOrRemoveDiskCache(profDir
, directory
,
801 // use current process directory during development
802 rv
= NS_GetSpecialDirectory(NS_XPCOM_CURRENT_PROCESS_DIR
,
803 getter_AddRefs(directory
));
807 mOfflineCacheParentDirectory
= do_QueryInterface(directory
, &rv
);
810 // read memory cache device prefs
811 (void) branch
->GetBoolPref(MEMORY_CACHE_ENABLE_PREF
, &mMemoryCacheEnabled
);
813 mMemoryCacheCapacity
= -1;
814 (void) branch
->GetIntPref(MEMORY_CACHE_CAPACITY_PREF
,
815 &mMemoryCacheCapacity
);
817 (void) branch
->GetIntPref(MEMORY_CACHE_MAX_ENTRY_SIZE_PREF
,
818 &mMemoryCacheMaxEntrySize
);
819 mMemoryCacheMaxEntrySize
= std::max(-1, mMemoryCacheMaxEntrySize
);
821 // read cache compression level pref
822 mCacheCompressionLevel
= CACHE_COMPRESSION_LEVEL
;
823 (void)branch
->GetIntPref(CACHE_COMPRESSION_LEVEL_PREF
,
824 &mCacheCompressionLevel
);
825 mCacheCompressionLevel
= std::max(0, mCacheCompressionLevel
);
826 mCacheCompressionLevel
= std::min(9, mCacheCompressionLevel
);
828 // read cache shutdown sanitization prefs
829 (void) branch
->GetBoolPref(SANITIZE_ON_SHUTDOWN_PREF
,
830 &mSanitizeOnShutdown
);
831 (void) branch
->GetBoolPref(CLEAR_ON_SHUTDOWN_PREF
,
832 &mClearCacheOnShutdown
);
838 nsCacheService::DispatchToCacheIOThread(nsIRunnable
* event
)
840 if (!gService
->mCacheIOThread
) return NS_ERROR_NOT_AVAILABLE
;
841 return gService
->mCacheIOThread
->Dispatch(event
, NS_DISPATCH_NORMAL
);
845 nsCacheService::SyncWithCacheIOThread()
847 gService
->mLock
.AssertCurrentThreadOwns();
848 if (!gService
->mCacheIOThread
) return NS_ERROR_NOT_AVAILABLE
;
850 nsCOMPtr
<nsIRunnable
> event
= new nsBlockOnCacheThreadEvent();
852 // dispatch event - it will notify the monitor when it's done
854 gService
->mCacheIOThread
->Dispatch(event
, NS_DISPATCH_NORMAL
);
856 NS_WARNING("Failed dispatching block-event");
857 return NS_ERROR_UNEXPECTED
;
860 // wait until notified, then return
861 rv
= gService
->mCondVar
.Wait();
868 nsCacheProfilePrefObserver::DiskCacheEnabled()
870 if ((mDiskCacheCapacity
== 0) || (!mDiskCacheParentDirectory
)) return false;
871 return mDiskCacheEnabled
&& (!mSanitizeOnShutdown
|| !mClearCacheOnShutdown
);
876 nsCacheProfilePrefObserver::OfflineCacheEnabled()
878 if ((mOfflineCacheCapacity
== 0) || (!mOfflineCacheParentDirectory
))
881 return mOfflineCacheEnabled
;
886 nsCacheProfilePrefObserver::MemoryCacheEnabled()
888 if (mMemoryCacheCapacity
== 0) return false;
889 return mMemoryCacheEnabled
;
894 * MemoryCacheCapacity
896 * If the browser.cache.memory.capacity preference is positive, we use that
897 * value for the amount of memory available for the cache.
899 * If browser.cache.memory.capacity is zero, the memory cache is disabled.
901 * If browser.cache.memory.capacity is negative or not present, we use a
902 * formula that grows less than linearly with the amount of system memory,
903 * with an upper limit on the cache size. No matter how much physical RAM is
904 * present, the default cache size would not exceed 32 MB. This maximum would
905 * apply only to systems with more than 4 GB of RAM (e.g. terminal servers)
918 * The equation for this is (for cache size C and memory size K (kbytes)):
920 * C = x^2/3 + x + 2/3 + 0.1 (0.1 for rounding)
925 nsCacheProfilePrefObserver::MemoryCacheCapacity()
927 int32_t capacity
= mMemoryCacheCapacity
;
929 CACHE_LOG_DEBUG(("Memory cache capacity forced to %d\n", capacity
));
933 static uint64_t bytes
= PR_GetPhysicalMemorySize();
934 CACHE_LOG_DEBUG(("Physical Memory size is %llu\n", bytes
));
936 // If getting the physical memory failed, arbitrarily assume
937 // 32 MB of RAM. We use a low default to have a reasonable
938 // size on all the devices we support.
940 bytes
= 32 * 1024 * 1024;
942 // Conversion from unsigned int64_t to double doesn't work on all platforms.
943 // We need to truncate the value at INT64_MAX to make sure we don't
945 if (bytes
> INT64_MAX
)
948 uint64_t kbytes
= bytes
>> 10;
950 double kBytesD
= double(kbytes
);
952 double x
= log(kBytesD
)/log(2.0) - 14;
954 capacity
= (int32_t)(x
* x
/ 3.0 + x
+ 2.0 / 3 + 0.1); // 0.1 for rounding
966 nsCacheProfilePrefObserver::CacheCompressionLevel()
968 return mCacheCompressionLevel
;
971 /******************************************************************************
972 * nsProcessRequestEvent
973 *****************************************************************************/
975 class nsProcessRequestEvent
: public nsRunnable
{
977 explicit nsProcessRequestEvent(nsCacheRequest
*aRequest
)
979 MOZ_EVENT_TRACER_NAME_OBJECT(aRequest
, aRequest
->mKey
.get());
980 MOZ_EVENT_TRACER_WAIT(aRequest
, "net::cache::ProcessRequest");
988 NS_ASSERTION(mRequest
->mListener
,
989 "Sync OpenCacheEntry() posted to background thread!");
991 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSPROCESSREQUESTEVENT_RUN
));
992 rv
= nsCacheService::gService
->ProcessRequest(mRequest
,
996 // Don't delete the request if it was queued
997 if (!(mRequest
->IsBlocking() &&
998 rv
== NS_ERROR_CACHE_WAIT_FOR_VALIDATION
))
1005 virtual ~nsProcessRequestEvent() {}
1008 nsCacheRequest
*mRequest
;
1011 /******************************************************************************
1013 *****************************************************************************/
1015 class nsDoomEvent
: public nsRunnable
{
1017 nsDoomEvent(nsCacheSession
*session
,
1018 const nsACString
&key
,
1019 nsICacheListener
*listener
)
1021 mKey
= *session
->ClientID();
1024 mStoragePolicy
= session
->StoragePolicy();
1025 mListener
= listener
;
1026 mThread
= do_GetCurrentThread();
1027 // We addref the listener here and release it in nsNotifyDoomListener
1028 // on the callers thread. If posting of nsNotifyDoomListener event fails
1029 // we leak the listener which is better than releasing it on a wrong
1031 NS_IF_ADDREF(mListener
);
1036 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSDOOMEVENT_RUN
));
1038 bool foundActive
= true;
1039 nsresult status
= NS_ERROR_NOT_AVAILABLE
;
1040 nsCacheEntry
*entry
;
1041 entry
= nsCacheService::gService
->mActiveEntries
.GetEntry(&mKey
);
1043 bool collision
= false;
1044 foundActive
= false;
1045 entry
= nsCacheService::gService
->SearchCacheDevices(&mKey
,
1052 nsCacheService::gService
->DoomEntry_Internal(entry
, foundActive
);
1056 mThread
->Dispatch(new nsNotifyDoomListener(mListener
, status
),
1057 NS_DISPATCH_NORMAL
);
1058 // posted event will release the reference on the correct thread
1059 mListener
= nullptr;
1067 nsCacheStoragePolicy mStoragePolicy
;
1068 nsICacheListener
*mListener
;
1069 nsCOMPtr
<nsIThread
> mThread
;
1072 /******************************************************************************
1074 *****************************************************************************/
1075 nsCacheService
* nsCacheService::gService
= nullptr;
1077 NS_IMPL_ISUPPORTS(nsCacheService
, nsICacheService
, nsICacheServiceInternal
,
1080 nsCacheService::nsCacheService()
1081 : mObserver(nullptr),
1082 mLock("nsCacheService.mLock"),
1083 mCondVar(mLock
, "nsCacheService.mCondVar"),
1084 mTimeStampLock("nsCacheService.mTimeStampLock"),
1085 mInitialized(false),
1086 mClearingEntries(false),
1087 mEnableMemoryDevice(true),
1088 mEnableDiskDevice(true),
1089 mMemoryDevice(nullptr),
1090 mDiskDevice(nullptr),
1091 mOfflineDevice(nullptr),
1098 mDeactivateFailures(0),
1099 mDeactivatedUnboundEntries(0)
1101 NS_ASSERTION(gService
==nullptr, "multiple nsCacheService instances!");
1104 // create list of cache devices
1105 PR_INIT_CLIST(&mDoomedEntries
);
1108 nsCacheService::~nsCacheService()
1110 if (mInitialized
) // Shutdown hasn't been called yet.
1114 mObserver
->Remove();
1115 NS_RELEASE(mObserver
);
1123 nsCacheService::Init()
1125 // Thie method must be called on the main thread because mCacheIOThread must
1126 // only be modified on the main thread.
1127 if (!NS_IsMainThread()) {
1128 NS_ERROR("nsCacheService::Init called off the main thread");
1129 return NS_ERROR_NOT_SAME_THREAD
;
1132 NS_ASSERTION(!mInitialized
, "nsCacheService already initialized.");
1134 return NS_ERROR_ALREADY_INITIALIZED
;
1136 if (mozilla::net::IsNeckoChild()) {
1137 return NS_ERROR_UNEXPECTED
;
1144 mStorageService
= do_GetService("@mozilla.org/storage/service;1", &rv
);
1145 NS_ENSURE_SUCCESS(rv
, rv
);
1147 MOZ_EVENT_TRACER_NAME_OBJECT(nsCacheService::gService
, "nsCacheService");
1149 rv
= NS_NewNamedThread("Cache I/O",
1150 getter_AddRefs(mCacheIOThread
));
1151 if (NS_FAILED(rv
)) {
1152 NS_RUNTIMEABORT("Can't create cache IO thread");
1155 rv
= nsDeleteDir::Init();
1156 if (NS_FAILED(rv
)) {
1157 NS_WARNING("Can't initialize nsDeleteDir");
1160 // initialize hashtable for active cache entries
1161 rv
= mActiveEntries
.Init();
1162 if (NS_FAILED(rv
)) return rv
;
1164 // create profile/preference observer
1166 mObserver
= new nsCacheProfilePrefObserver();
1167 NS_ADDREF(mObserver
);
1168 mObserver
->Install();
1171 mEnableDiskDevice
= mObserver
->DiskCacheEnabled();
1172 mEnableOfflineDevice
= mObserver
->OfflineCacheEnabled();
1173 mEnableMemoryDevice
= mObserver
->MemoryCacheEnabled();
1175 RegisterWeakMemoryReporter(this);
1177 mInitialized
= true;
1183 nsCacheService::ShutdownCustomCacheDeviceEnum(const nsAString
& aProfileDir
,
1184 nsRefPtr
<nsOfflineCacheDevice
>& aDevice
,
1187 aDevice
->Shutdown();
1188 return PL_DHASH_REMOVE
;
1192 nsCacheService::Shutdown()
1194 // This method must be called on the main thread because mCacheIOThread must
1195 // only be modified on the main thread.
1196 if (!NS_IsMainThread()) {
1197 NS_RUNTIMEABORT("nsCacheService::Shutdown called off the main thread");
1200 nsCOMPtr
<nsIThread
> cacheIOThread
;
1201 Telemetry::AutoTimer
<Telemetry::NETWORK_DISK_CACHE_SHUTDOWN
> totalTimer
;
1203 bool shouldSanitize
= false;
1204 nsCOMPtr
<nsIFile
> parentDir
;
1207 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSCACHESERVICE_SHUTDOWN
));
1208 NS_ASSERTION(mInitialized
,
1209 "can't shutdown nsCacheService unless it has been initialized.");
1213 mClearingEntries
= true;
1214 DoomActiveEntries(nullptr);
1219 UnregisterWeakMemoryReporter(this);
1222 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSCACHESERVICE_SHUTDOWN
));
1223 NS_ASSERTION(mInitialized
, "Bad state");
1225 mInitialized
= false;
1230 if (mSmartSizeTimer
) {
1231 mSmartSizeTimer
->Cancel();
1232 mSmartSizeTimer
= nullptr;
1235 // Make sure to wait for any pending cache-operations before
1236 // proceeding with destructive actions (bug #620660)
1237 (void) SyncWithCacheIOThread();
1239 // obtain the disk cache directory in case we need to sanitize it
1240 parentDir
= mObserver
->DiskCacheParentDirectory();
1241 shouldSanitize
= mObserver
->SanitizeAtShutdown();
1243 // deallocate memory and disk caches
1244 delete mMemoryDevice
;
1245 mMemoryDevice
= nullptr;
1248 mDiskDevice
= nullptr;
1251 mOfflineDevice
->Shutdown();
1253 NS_IF_RELEASE(mOfflineDevice
);
1255 mCustomOfflineDevices
.Enumerate(&nsCacheService::ShutdownCustomCacheDeviceEnum
, nullptr);
1258 LogCacheStatistics();
1261 mClearingEntries
= false;
1262 mCacheIOThread
.swap(cacheIOThread
);
1266 nsShutdownThread::BlockingShutdown(cacheIOThread
);
1268 if (shouldSanitize
) {
1269 nsresult rv
= parentDir
->AppendNative(NS_LITERAL_CSTRING("Cache"));
1270 if (NS_SUCCEEDED(rv
)) {
1272 if (NS_SUCCEEDED(parentDir
->Exists(&exists
)) && exists
)
1273 nsDeleteDir::DeleteDir(parentDir
, false);
1275 Telemetry::AutoTimer
<Telemetry::NETWORK_DISK_CACHE_SHUTDOWN_CLEAR_PRIVATE
> timer
;
1276 nsDeleteDir::Shutdown(shouldSanitize
);
1278 Telemetry::AutoTimer
<Telemetry::NETWORK_DISK_CACHE_DELETEDIR_SHUTDOWN
> timer
;
1279 nsDeleteDir::Shutdown(shouldSanitize
);
1285 nsCacheService::Create(nsISupports
* aOuter
, const nsIID
& aIID
, void* *aResult
)
1289 if (aOuter
!= nullptr)
1290 return NS_ERROR_NO_AGGREGATION
;
1292 nsCacheService
* cacheService
= new nsCacheService();
1293 if (cacheService
== nullptr)
1294 return NS_ERROR_OUT_OF_MEMORY
;
1296 NS_ADDREF(cacheService
);
1297 rv
= cacheService
->Init();
1298 if (NS_SUCCEEDED(rv
)) {
1299 rv
= cacheService
->QueryInterface(aIID
, aResult
);
1301 NS_RELEASE(cacheService
);
1307 nsCacheService::CreateSession(const char * clientID
,
1308 nsCacheStoragePolicy storagePolicy
,
1310 nsICacheSession
**result
)
1314 if (net::CacheObserver::UseNewCache())
1315 return NS_ERROR_NOT_IMPLEMENTED
;
1317 return CreateSessionInternal(clientID
, storagePolicy
, streamBased
, result
);
1321 nsCacheService::CreateSessionInternal(const char * clientID
,
1322 nsCacheStoragePolicy storagePolicy
,
1324 nsICacheSession
**result
)
1326 if (this == nullptr) return NS_ERROR_NOT_AVAILABLE
;
1328 nsCacheSession
* session
= new nsCacheSession(clientID
, storagePolicy
, streamBased
);
1329 if (!session
) return NS_ERROR_OUT_OF_MEMORY
;
1331 NS_ADDREF(*result
= session
);
1338 nsCacheService::EvictEntriesForSession(nsCacheSession
* session
)
1340 NS_ASSERTION(gService
, "nsCacheService::gService is null.");
1341 return gService
->EvictEntriesForClient(session
->ClientID()->get(),
1342 session
->StoragePolicy());
1347 class EvictionNotifierRunnable
: public nsRunnable
1350 explicit EvictionNotifierRunnable(nsISupports
* aSubject
)
1351 : mSubject(aSubject
)
1357 nsCOMPtr
<nsISupports
> mSubject
;
1361 EvictionNotifierRunnable::Run()
1363 nsCOMPtr
<nsIObserverService
> obsSvc
=
1364 mozilla::services::GetObserverService();
1366 obsSvc
->NotifyObservers(mSubject
,
1367 NS_CACHESERVICE_EMPTYCACHE_TOPIC_ID
,
1373 } // anonymous namespace
1376 nsCacheService::EvictEntriesForClient(const char * clientID
,
1377 nsCacheStoragePolicy storagePolicy
)
1379 nsRefPtr
<EvictionNotifierRunnable
> r
=
1380 new EvictionNotifierRunnable(NS_ISUPPORTS_CAST(nsICacheService
*, this));
1381 NS_DispatchToMainThread(r
);
1383 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSCACHESERVICE_EVICTENTRIESFORCLIENT
));
1384 nsresult res
= NS_OK
;
1386 if (storagePolicy
== nsICache::STORE_ANYWHERE
||
1387 storagePolicy
== nsICache::STORE_ON_DISK
) {
1389 if (mEnableDiskDevice
) {
1390 nsresult rv
= NS_OK
;
1392 rv
= CreateDiskDevice();
1394 rv
= mDiskDevice
->EvictEntries(clientID
);
1400 // Only clear the offline cache if it has been specifically asked for.
1401 if (storagePolicy
== nsICache::STORE_OFFLINE
) {
1402 if (mEnableOfflineDevice
) {
1403 nsresult rv
= NS_OK
;
1404 if (!mOfflineDevice
)
1405 rv
= CreateOfflineDevice();
1407 rv
= mOfflineDevice
->EvictEntries(clientID
);
1413 if (storagePolicy
== nsICache::STORE_ANYWHERE
||
1414 storagePolicy
== nsICache::STORE_IN_MEMORY
) {
1415 // If there is no memory device, there is no need to evict it...
1416 if (mMemoryDevice
) {
1417 nsresult rv
= mMemoryDevice
->EvictEntries(clientID
);
1428 nsCacheService::IsStorageEnabledForPolicy(nsCacheStoragePolicy storagePolicy
,
1431 if (gService
== nullptr) return NS_ERROR_NOT_AVAILABLE
;
1432 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSCACHESERVICE_ISSTORAGEENABLEDFORPOLICY
));
1434 *result
= gService
->IsStorageEnabledForPolicy_Locked(storagePolicy
);
1440 nsCacheService::DoomEntry(nsCacheSession
*session
,
1441 const nsACString
&key
,
1442 nsICacheListener
*listener
)
1444 CACHE_LOG_DEBUG(("Dooming entry for session %p, key %s\n",
1445 session
, PromiseFlatCString(key
).get()));
1446 NS_ASSERTION(gService
, "nsCacheService::gService is null.");
1448 if (!gService
->mInitialized
)
1449 return NS_ERROR_NOT_INITIALIZED
;
1451 return DispatchToCacheIOThread(new nsDoomEvent(session
, key
, listener
));
1456 nsCacheService::IsStorageEnabledForPolicy_Locked(nsCacheStoragePolicy storagePolicy
)
1458 if (gService
->mEnableMemoryDevice
&&
1459 (storagePolicy
== nsICache::STORE_ANYWHERE
||
1460 storagePolicy
== nsICache::STORE_IN_MEMORY
)) {
1463 if (gService
->mEnableDiskDevice
&&
1464 (storagePolicy
== nsICache::STORE_ANYWHERE
||
1465 storagePolicy
== nsICache::STORE_ON_DISK
)) {
1468 if (gService
->mEnableOfflineDevice
&&
1469 storagePolicy
== nsICache::STORE_OFFLINE
) {
1476 NS_IMETHODIMP
nsCacheService::VisitEntries(nsICacheVisitor
*visitor
)
1478 if (net::CacheObserver::UseNewCache())
1479 return NS_ERROR_NOT_IMPLEMENTED
;
1481 return VisitEntriesInternal(visitor
);
1484 nsresult
nsCacheService::VisitEntriesInternal(nsICacheVisitor
*visitor
)
1486 NS_ENSURE_ARG_POINTER(visitor
);
1488 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSCACHESERVICE_VISITENTRIES
));
1490 if (!(mEnableDiskDevice
|| mEnableMemoryDevice
))
1491 return NS_ERROR_NOT_AVAILABLE
;
1493 // XXX record the fact that a visitation is in progress,
1494 // XXX i.e. keep list of visitors in progress.
1496 nsresult rv
= NS_OK
;
1497 // If there is no memory device, there are then also no entries to visit...
1498 if (mMemoryDevice
) {
1499 rv
= mMemoryDevice
->Visit(visitor
);
1500 if (NS_FAILED(rv
)) return rv
;
1503 if (mEnableDiskDevice
) {
1505 rv
= CreateDiskDevice();
1506 if (NS_FAILED(rv
)) return rv
;
1508 rv
= mDiskDevice
->Visit(visitor
);
1509 if (NS_FAILED(rv
)) return rv
;
1512 if (mEnableOfflineDevice
) {
1513 if (!mOfflineDevice
) {
1514 rv
= CreateOfflineDevice();
1515 if (NS_FAILED(rv
)) return rv
;
1517 rv
= mOfflineDevice
->Visit(visitor
);
1518 if (NS_FAILED(rv
)) return rv
;
1521 // XXX notify any shutdown process that visitation is complete for THIS visitor.
1522 // XXX keep queue of visitors
1527 void nsCacheService::FireClearNetworkCacheStoredAnywhereNotification()
1529 MOZ_ASSERT(NS_IsMainThread());
1530 nsCOMPtr
<nsIObserverService
> obsvc
= mozilla::services::GetObserverService();
1532 obsvc
->NotifyObservers(nullptr,
1533 "network-clear-cache-stored-anywhere",
1538 NS_IMETHODIMP
nsCacheService::EvictEntries(nsCacheStoragePolicy storagePolicy
)
1540 if (net::CacheObserver::UseNewCache())
1541 return NS_ERROR_NOT_IMPLEMENTED
;
1543 return EvictEntriesInternal(storagePolicy
);
1546 nsresult
nsCacheService::EvictEntriesInternal(nsCacheStoragePolicy storagePolicy
)
1548 if (storagePolicy
== nsICache::STORE_ANYWHERE
) {
1549 // if not called on main thread, dispatch the notification to the main thread to notify observers
1550 if (!NS_IsMainThread()) {
1551 nsCOMPtr
<nsIRunnable
> event
= NS_NewRunnableMethod(this,
1552 &nsCacheService::FireClearNetworkCacheStoredAnywhereNotification
);
1553 NS_DispatchToMainThread(event
);
1555 // else you're already on main thread - notify observers
1556 FireClearNetworkCacheStoredAnywhereNotification();
1559 return EvictEntriesForClient(nullptr, storagePolicy
);
1562 NS_IMETHODIMP
nsCacheService::GetCacheIOTarget(nsIEventTarget
* *aCacheIOTarget
)
1564 NS_ENSURE_ARG_POINTER(aCacheIOTarget
);
1566 // Because mCacheIOThread can only be changed on the main thread, it can be
1567 // read from the main thread without the lock. This is useful to prevent
1568 // blocking the main thread on other cache operations.
1569 if (!NS_IsMainThread()) {
1570 Lock(LOCK_TELEM(NSCACHESERVICE_GETCACHEIOTARGET
));
1574 if (mCacheIOThread
) {
1575 NS_ADDREF(*aCacheIOTarget
= mCacheIOThread
);
1578 *aCacheIOTarget
= nullptr;
1579 rv
= NS_ERROR_NOT_AVAILABLE
;
1582 if (!NS_IsMainThread()) {
1589 /* nsICacheServiceInternal
1590 * readonly attribute double lockHeldTime;
1592 NS_IMETHODIMP
nsCacheService::GetLockHeldTime(double *aLockHeldTime
)
1594 MutexAutoLock
lock(mTimeStampLock
);
1596 if (mLockAcquiredTimeStamp
.IsNull()) {
1597 *aLockHeldTime
= 0.0;
1601 (TimeStamp::Now() - mLockAcquiredTimeStamp
).ToMilliseconds();
1611 nsCacheService::CreateDiskDevice()
1613 if (!mInitialized
) return NS_ERROR_NOT_AVAILABLE
;
1614 if (!mEnableDiskDevice
) return NS_ERROR_NOT_AVAILABLE
;
1615 if (mDiskDevice
) return NS_OK
;
1617 mDiskDevice
= new nsDiskCacheDevice
;
1618 if (!mDiskDevice
) return NS_ERROR_OUT_OF_MEMORY
;
1620 // set the preferences
1621 mDiskDevice
->SetCacheParentDirectory(mObserver
->DiskCacheParentDirectory());
1622 mDiskDevice
->SetCapacity(mObserver
->DiskCacheCapacity());
1623 mDiskDevice
->SetMaxEntrySize(mObserver
->DiskCacheMaxEntrySize());
1625 nsresult rv
= mDiskDevice
->Init();
1626 if (NS_FAILED(rv
)) {
1629 printf("### mDiskDevice->Init() failed (0x%.8x)\n",
1630 static_cast<uint32_t>(rv
));
1631 printf("### - disabling disk cache for this session.\n");
1634 mEnableDiskDevice
= false;
1636 mDiskDevice
= nullptr;
1640 Telemetry::Accumulate(Telemetry::DISK_CACHE_SMART_SIZE_USING_OLD_MAX
,
1641 mObserver
->ShouldUseOldMaxSmartSize());
1643 NS_ASSERTION(!mSmartSizeTimer
, "Smartsize timer was already fired!");
1645 // Disk device is usually created during the startup. Delay smart size
1646 // calculation to avoid possible massive IO caused by eviction of entries
1647 // in case the new smart size is smaller than current cache usage.
1648 mSmartSizeTimer
= do_CreateInstance("@mozilla.org/timer;1", &rv
);
1649 if (NS_SUCCEEDED(rv
)) {
1650 rv
= mSmartSizeTimer
->InitWithCallback(new nsSetDiskSmartSizeCallback(),
1652 nsITimer::TYPE_ONE_SHOT
);
1653 if (NS_FAILED(rv
)) {
1654 NS_WARNING("Failed to post smart size timer");
1655 mSmartSizeTimer
= nullptr;
1658 NS_WARNING("Can't create smart size timer");
1660 // Ignore state of the timer and return success since the purpose of the
1661 // method (create the disk-device) has been fulfilled
1666 // Runnable sent from cache thread to main thread
1667 class nsDisableOldMaxSmartSizePrefEvent
: public nsRunnable
1670 nsDisableOldMaxSmartSizePrefEvent() {}
1674 // Main thread may have already called nsCacheService::Shutdown
1675 if (!nsCacheService::IsInitialized())
1676 return NS_ERROR_NOT_AVAILABLE
;
1678 nsCOMPtr
<nsIPrefBranch
> branch
= do_GetService(NS_PREFSERVICE_CONTRACTID
);
1680 return NS_ERROR_NOT_AVAILABLE
;
1683 nsresult rv
= branch
->SetBoolPref(DISK_CACHE_USE_OLD_MAX_SMART_SIZE_PREF
, false);
1684 if (NS_FAILED(rv
)) {
1685 NS_WARNING("Failed to disable old max smart size");
1689 // It is safe to call SetDiskSmartSize_Locked() without holding the lock
1690 // when we are on main thread and nsCacheService is initialized.
1691 nsCacheService::gService
->SetDiskSmartSize_Locked();
1693 if (nsCacheService::gService
->mObserver
->PermittedToSmartSize(branch
, false)) {
1694 rv
= branch
->SetIntPref(DISK_CACHE_CAPACITY_PREF
, MAX_CACHE_SIZE
);
1695 if (NS_FAILED(rv
)) {
1696 NS_WARNING("Failed to set cache capacity pref");
1705 nsCacheService::MarkStartingFresh()
1707 if (!gService
->mObserver
->ShouldUseOldMaxSmartSize()) {
1708 // Already using new max, nothing to do here
1712 gService
->mObserver
->SetUseNewMaxSmartSize(true);
1714 // We always dispatch an event here because we don't want to deal with lock
1715 // reentrance issues.
1716 NS_DispatchToMainThread(new nsDisableOldMaxSmartSizePrefEvent());
1720 nsCacheService::GetOfflineDevice(nsOfflineCacheDevice
**aDevice
)
1722 if (!mOfflineDevice
) {
1723 nsresult rv
= CreateOfflineDevice();
1724 NS_ENSURE_SUCCESS(rv
, rv
);
1727 NS_ADDREF(*aDevice
= mOfflineDevice
);
1732 nsCacheService::GetCustomOfflineDevice(nsIFile
*aProfileDir
,
1734 nsOfflineCacheDevice
**aDevice
)
1738 nsAutoString profilePath
;
1739 rv
= aProfileDir
->GetPath(profilePath
);
1740 NS_ENSURE_SUCCESS(rv
, rv
);
1742 if (!mCustomOfflineDevices
.Get(profilePath
, aDevice
)) {
1743 rv
= CreateCustomOfflineDevice(aProfileDir
, aQuota
, aDevice
);
1744 NS_ENSURE_SUCCESS(rv
, rv
);
1746 (*aDevice
)->SetAutoShutdown();
1747 mCustomOfflineDevices
.Put(profilePath
, *aDevice
);
1754 nsCacheService::CreateOfflineDevice()
1756 CACHE_LOG_ALWAYS(("Creating default offline device"));
1758 if (mOfflineDevice
) return NS_OK
;
1759 if (!nsCacheService::IsInitialized()) {
1760 return NS_ERROR_NOT_AVAILABLE
;
1763 nsresult rv
= CreateCustomOfflineDevice(
1764 mObserver
->OfflineCacheParentDirectory(),
1765 mObserver
->OfflineCacheCapacity(),
1767 NS_ENSURE_SUCCESS(rv
, rv
);
1773 nsCacheService::CreateCustomOfflineDevice(nsIFile
*aProfileDir
,
1775 nsOfflineCacheDevice
**aDevice
)
1777 NS_ENSURE_ARG(aProfileDir
);
1779 #if defined(PR_LOGGING)
1780 nsAutoCString profilePath
;
1781 aProfileDir
->GetNativePath(profilePath
);
1782 CACHE_LOG_ALWAYS(("Creating custom offline device, %s, %d",
1783 profilePath
.BeginReading(), aQuota
));
1786 if (!mInitialized
) return NS_ERROR_NOT_AVAILABLE
;
1787 if (!mEnableOfflineDevice
) return NS_ERROR_NOT_AVAILABLE
;
1789 *aDevice
= new nsOfflineCacheDevice
;
1791 NS_ADDREF(*aDevice
);
1793 // set the preferences
1794 (*aDevice
)->SetCacheParentDirectory(aProfileDir
);
1795 (*aDevice
)->SetCapacity(aQuota
);
1797 nsresult rv
= (*aDevice
)->InitWithSqlite(mStorageService
);
1798 if (NS_FAILED(rv
)) {
1799 CACHE_LOG_DEBUG(("OfflineDevice->InitWithSqlite() failed (0x%.8x)\n", rv
));
1800 CACHE_LOG_DEBUG((" - disabling offline cache for this session.\n"));
1802 NS_RELEASE(*aDevice
);
1808 nsCacheService::CreateMemoryDevice()
1810 if (!mInitialized
) return NS_ERROR_NOT_AVAILABLE
;
1811 if (!mEnableMemoryDevice
) return NS_ERROR_NOT_AVAILABLE
;
1812 if (mMemoryDevice
) return NS_OK
;
1814 mMemoryDevice
= new nsMemoryCacheDevice
;
1815 if (!mMemoryDevice
) return NS_ERROR_OUT_OF_MEMORY
;
1818 int32_t capacity
= mObserver
->MemoryCacheCapacity();
1819 CACHE_LOG_DEBUG(("Creating memory device with capacity %d\n", capacity
));
1820 mMemoryDevice
->SetCapacity(capacity
);
1821 mMemoryDevice
->SetMaxEntrySize(mObserver
->MemoryCacheMaxEntrySize());
1823 nsresult rv
= mMemoryDevice
->Init();
1824 if (NS_FAILED(rv
)) {
1825 NS_WARNING("Initialization of Memory Cache failed.");
1826 delete mMemoryDevice
;
1827 mMemoryDevice
= nullptr;
1834 nsCacheService::RemoveCustomOfflineDevice(nsOfflineCacheDevice
*aDevice
)
1836 nsCOMPtr
<nsIFile
> profileDir
= aDevice
->BaseDirectory();
1838 return NS_ERROR_UNEXPECTED
;
1840 nsAutoString profilePath
;
1841 nsresult rv
= profileDir
->GetPath(profilePath
);
1842 NS_ENSURE_SUCCESS(rv
, rv
);
1844 mCustomOfflineDevices
.Remove(profilePath
);
1849 nsCacheService::CreateRequest(nsCacheSession
* session
,
1850 const nsACString
& clientKey
,
1851 nsCacheAccessMode accessRequested
,
1853 nsICacheListener
* listener
,
1854 nsCacheRequest
** request
)
1856 NS_ASSERTION(request
, "CreateRequest: request is null");
1858 nsAutoCString
key(*session
->ClientID());
1860 key
.Append(clientKey
);
1862 if (mMaxKeyLength
< key
.Length()) mMaxKeyLength
= key
.Length();
1865 *request
= new nsCacheRequest(key
, listener
, accessRequested
,
1866 blockingMode
, session
);
1868 if (!listener
) return NS_OK
; // we're sync, we're done.
1870 // get the request's thread
1871 (*request
)->mThread
= do_GetCurrentThread();
1877 class nsCacheListenerEvent
: public nsRunnable
1880 nsCacheListenerEvent(nsICacheListener
*listener
,
1881 nsICacheEntryDescriptor
*descriptor
,
1882 nsCacheAccessMode accessGranted
,
1884 : mListener(listener
) // transfers reference
1885 , mDescriptor(descriptor
) // transfers reference (may be null)
1886 , mAccessGranted(accessGranted
)
1892 mozilla::eventtracer::AutoEventTracer
tracer(
1893 static_cast<nsIRunnable
*>(this),
1896 "net::cache::OnCacheEntryAvailable");
1898 mListener
->OnCacheEntryAvailable(mDescriptor
, mAccessGranted
, mStatus
);
1900 NS_RELEASE(mListener
);
1901 NS_IF_RELEASE(mDescriptor
);
1906 // We explicitly leak mListener or mDescriptor if Run is not called
1907 // because otherwise we cannot guarantee that they are destroyed on
1908 // the right thread.
1910 nsICacheListener
*mListener
;
1911 nsICacheEntryDescriptor
*mDescriptor
;
1912 nsCacheAccessMode mAccessGranted
;
1918 nsCacheService::NotifyListener(nsCacheRequest
* request
,
1919 nsICacheEntryDescriptor
* descriptor
,
1920 nsCacheAccessMode accessGranted
,
1923 NS_ASSERTION(request
->mThread
, "no thread set in async request!");
1925 // Swap ownership, and release listener on target thread...
1926 nsICacheListener
*listener
= request
->mListener
;
1927 request
->mListener
= nullptr;
1929 nsCOMPtr
<nsIRunnable
> ev
=
1930 new nsCacheListenerEvent(listener
, descriptor
,
1931 accessGranted
, status
);
1933 // Better to leak listener and descriptor if we fail because we don't
1934 // want to destroy them inside the cache service lock or on potentially
1935 // the wrong thread.
1936 return NS_ERROR_OUT_OF_MEMORY
;
1939 MOZ_EVENT_TRACER_NAME_OBJECT(ev
.get(), request
->mKey
.get());
1940 MOZ_EVENT_TRACER_WAIT(ev
.get(), "net::cache::OnCacheEntryAvailable");
1941 return request
->mThread
->Dispatch(ev
, NS_DISPATCH_NORMAL
);
1946 nsCacheService::ProcessRequest(nsCacheRequest
* request
,
1947 bool calledFromOpenCacheEntry
,
1948 nsICacheEntryDescriptor
** result
)
1950 mozilla::eventtracer::AutoEventTracer
tracer(
1954 "net::cache::ProcessRequest");
1956 // !!! must be called with mLock held !!!
1958 nsCacheEntry
* entry
= nullptr;
1959 nsCacheEntry
* doomedEntry
= nullptr;
1960 nsCacheAccessMode accessGranted
= nsICache::ACCESS_NONE
;
1961 if (result
) *result
= nullptr;
1963 while(1) { // Activate entry loop
1964 rv
= ActivateEntry(request
, &entry
, &doomedEntry
); // get the entry for this request
1965 if (NS_FAILED(rv
)) break;
1967 while(1) { // Request Access loop
1968 NS_ASSERTION(entry
, "no entry in Request Access loop!");
1969 // entry->RequestAccess queues request on entry
1970 rv
= entry
->RequestAccess(request
, &accessGranted
);
1971 if (rv
!= NS_ERROR_CACHE_WAIT_FOR_VALIDATION
) break;
1973 if (request
->IsBlocking()) {
1974 if (request
->mListener
) {
1975 // async exits - validate, doom, or close will resume
1979 // XXX this is probably wrong...
1981 rv
= request
->WaitForValidation();
1982 Lock(LOCK_TELEM(NSCACHESERVICE_PROCESSREQUEST
));
1985 PR_REMOVE_AND_INIT_LINK(request
);
1986 if (NS_FAILED(rv
)) break; // non-blocking mode returns WAIT_FOR_VALIDATION error
1987 // okay, we're ready to process this request, request access again
1989 if (rv
!= NS_ERROR_CACHE_ENTRY_DOOMED
) break;
1991 if (entry
->IsNotInUse()) {
1992 // this request was the last one keeping it around, so get rid of it
1993 DeactivateEntry(entry
);
1995 // loop back around to look for another entry
1998 if (NS_SUCCEEDED(rv
) && request
->mProfileDir
) {
1999 // Custom cache directory has been demanded. Preset the cache device.
2000 if (entry
->StoragePolicy() != nsICache::STORE_OFFLINE
) {
2001 // Failsafe check: this is implemented only for offline cache atm.
2002 rv
= NS_ERROR_FAILURE
;
2004 nsRefPtr
<nsOfflineCacheDevice
> customCacheDevice
;
2005 rv
= GetCustomOfflineDevice(request
->mProfileDir
, -1,
2006 getter_AddRefs(customCacheDevice
));
2007 if (NS_SUCCEEDED(rv
))
2008 entry
->SetCustomCacheDevice(customCacheDevice
);
2012 nsICacheEntryDescriptor
*descriptor
= nullptr;
2014 if (NS_SUCCEEDED(rv
))
2015 rv
= entry
->CreateDescriptor(request
, accessGranted
, &descriptor
);
2017 // If doomedEntry is set, ActivatEntry() doomed an existing entry and
2018 // created a new one for that cache-key. However, any pending requests
2019 // on the doomed entry were not processed and we need to do that here.
2020 // This must be done after adding the created entry to list of active
2021 // entries (which is done in ActivateEntry()) otherwise the hashkeys crash
2022 // (see bug ##561313). It is also important to do this after creating a
2023 // descriptor for this request, or some other request may end up being
2024 // executed first for the newly created entry.
2025 // Finally, it is worth to emphasize that if doomedEntry is set,
2026 // ActivateEntry() created a new entry for the request, which will be
2027 // initialized by RequestAccess() and they both should have returned NS_OK.
2029 (void) ProcessPendingRequests(doomedEntry
);
2030 if (doomedEntry
->IsNotInUse())
2031 DeactivateEntry(doomedEntry
);
2032 doomedEntry
= nullptr;
2035 if (request
->mListener
) { // Asynchronous
2037 if (NS_FAILED(rv
) && calledFromOpenCacheEntry
&& request
->IsBlocking())
2038 return rv
; // skip notifying listener, just return rv to caller
2040 // call listener to report error or descriptor
2041 nsresult rv2
= NotifyListener(request
, descriptor
, accessGranted
, rv
);
2042 if (NS_FAILED(rv2
) && NS_SUCCEEDED(rv
)) {
2043 rv
= rv2
; // trigger delete request
2045 } else { // Synchronous
2046 *result
= descriptor
;
2053 nsCacheService::OpenCacheEntry(nsCacheSession
* session
,
2054 const nsACString
& key
,
2055 nsCacheAccessMode accessRequested
,
2057 nsICacheListener
* listener
,
2058 nsICacheEntryDescriptor
** result
)
2060 CACHE_LOG_DEBUG(("Opening entry for session %p, key %s, mode %d, blocking %d\n",
2061 session
, PromiseFlatCString(key
).get(), accessRequested
,
2063 NS_ASSERTION(gService
, "nsCacheService::gService is null.");
2067 if (!gService
->mInitialized
)
2068 return NS_ERROR_NOT_INITIALIZED
;
2070 nsCacheRequest
* request
= nullptr;
2072 nsresult rv
= gService
->CreateRequest(session
,
2078 if (NS_FAILED(rv
)) return rv
;
2080 CACHE_LOG_DEBUG(("Created request %p\n", request
));
2082 // Process the request on the background thread if we are on the main thread
2083 // and the the request is asynchronous
2084 if (NS_IsMainThread() && listener
&& gService
->mCacheIOThread
) {
2085 nsCOMPtr
<nsIRunnable
> ev
=
2086 new nsProcessRequestEvent(request
);
2087 rv
= DispatchToCacheIOThread(ev
);
2089 // delete request if we didn't post the event
2095 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSCACHESERVICE_OPENCACHEENTRY
));
2096 rv
= gService
->ProcessRequest(request
, true, result
);
2098 // delete requests that have completed
2099 if (!(listener
&& blockingMode
&&
2100 (rv
== NS_ERROR_CACHE_WAIT_FOR_VALIDATION
)))
2109 nsCacheService::ActivateEntry(nsCacheRequest
* request
,
2110 nsCacheEntry
** result
,
2111 nsCacheEntry
** doomedEntry
)
2113 CACHE_LOG_DEBUG(("Activate entry for request %p\n", request
));
2114 if (!mInitialized
|| mClearingEntries
)
2115 return NS_ERROR_NOT_AVAILABLE
;
2117 mozilla::eventtracer::AutoEventTracer
tracer(
2121 "net::cache::ActivateEntry");
2123 nsresult rv
= NS_OK
;
2125 NS_ASSERTION(request
!= nullptr, "ActivateEntry called with no request");
2126 if (result
) *result
= nullptr;
2127 if (doomedEntry
) *doomedEntry
= nullptr;
2128 if ((!request
) || (!result
) || (!doomedEntry
))
2129 return NS_ERROR_NULL_POINTER
;
2131 // check if the request can be satisfied
2132 if (!mEnableMemoryDevice
&& !request
->IsStreamBased())
2133 return NS_ERROR_FAILURE
;
2134 if (!IsStorageEnabledForPolicy_Locked(request
->StoragePolicy()))
2135 return NS_ERROR_FAILURE
;
2137 // search active entries (including those not bound to device)
2138 nsCacheEntry
*entry
= mActiveEntries
.GetEntry(&(request
->mKey
));
2139 CACHE_LOG_DEBUG(("Active entry for request %p is %p\n", request
, entry
));
2142 // search cache devices for entry
2143 bool collision
= false;
2144 entry
= SearchCacheDevices(&(request
->mKey
), request
->StoragePolicy(), &collision
);
2145 CACHE_LOG_DEBUG(("Device search for request %p returned %p\n",
2147 // When there is a hashkey collision just refuse to cache it...
2148 if (collision
) return NS_ERROR_CACHE_IN_USE
;
2150 if (entry
) entry
->MarkInitialized();
2152 NS_ASSERTION(entry
->IsActive(), "Inactive entry found in mActiveEntries!");
2163 ((request
->AccessRequested() == nsICache::ACCESS_WRITE
) ||
2164 ((request
->StoragePolicy() != nsICache::STORE_OFFLINE
) &&
2165 (entry
->mExpirationTime
<= SecondsFromPRTime(PR_Now()) &&
2166 request
->WillDoomEntriesIfExpired()))))
2169 // this is FORCE-WRITE request or the entry has expired
2170 // we doom entry without processing pending requests, but store it in
2171 // doomedEntry which causes pending requests to be processed below
2172 rv
= DoomEntry_Internal(entry
, false);
2173 *doomedEntry
= entry
;
2174 if (NS_FAILED(rv
)) {
2175 // XXX what to do? Increment FailedDooms counter?
2181 if (! (request
->AccessRequested() & nsICache::ACCESS_WRITE
)) {
2182 // this is a READ-ONLY request
2183 rv
= NS_ERROR_CACHE_KEY_NOT_FOUND
;
2187 entry
= new nsCacheEntry(request
->mKey
,
2188 request
->IsStreamBased(),
2189 request
->StoragePolicy());
2191 return NS_ERROR_OUT_OF_MEMORY
;
2193 if (request
->IsPrivate())
2194 entry
->MarkPrivate();
2199 // XXX we could perform an early bind in some cases based on storage policy
2202 if (!entry
->IsActive()) {
2203 rv
= mActiveEntries
.AddEntry(entry
);
2204 if (NS_FAILED(rv
)) goto error
;
2205 CACHE_LOG_DEBUG(("Added entry %p to mActiveEntries\n", entry
));
2206 entry
->MarkActive(); // mark entry active, because it's now in mActiveEntries
2219 nsCacheService::SearchCacheDevices(nsCString
* key
, nsCacheStoragePolicy policy
, bool *collision
)
2221 Telemetry::AutoTimer
<Telemetry::CACHE_DEVICE_SEARCH_2
> timer
;
2222 nsCacheEntry
* entry
= nullptr;
2224 MOZ_EVENT_TRACER_NAME_OBJECT(key
, key
->BeginReading());
2225 eventtracer::AutoEventTracer
searchCacheDevices(
2229 "net::cache::SearchCacheDevices");
2231 CACHE_LOG_DEBUG(("mMemoryDevice: 0x%p\n", mMemoryDevice
));
2234 if ((policy
== nsICache::STORE_ANYWHERE
) || (policy
== nsICache::STORE_IN_MEMORY
)) {
2235 // If there is no memory device, then there is nothing to search...
2236 if (mMemoryDevice
) {
2237 entry
= mMemoryDevice
->FindEntry(key
, collision
);
2238 CACHE_LOG_DEBUG(("Searching mMemoryDevice for key %s found: 0x%p, "
2239 "collision: %d\n", key
->get(), entry
, collision
));
2244 ((policy
== nsICache::STORE_ANYWHERE
) || (policy
== nsICache::STORE_ON_DISK
))) {
2246 if (mEnableDiskDevice
) {
2248 nsresult rv
= CreateDiskDevice();
2253 entry
= mDiskDevice
->FindEntry(key
, collision
);
2257 if (!entry
&& (policy
== nsICache::STORE_OFFLINE
||
2258 (policy
== nsICache::STORE_ANYWHERE
&&
2259 gIOService
->IsOffline()))) {
2261 if (mEnableOfflineDevice
) {
2262 if (!mOfflineDevice
) {
2263 nsresult rv
= CreateOfflineDevice();
2268 entry
= mOfflineDevice
->FindEntry(key
, collision
);
2277 nsCacheService::EnsureEntryHasDevice(nsCacheEntry
* entry
)
2279 nsCacheDevice
* device
= entry
->CacheDevice();
2280 // return device if found, possibly null if the entry is doomed i.e prevent
2281 // doomed entries to bind to a device (see e.g. bugs #548406 and #596443)
2282 if (device
|| entry
->IsDoomed()) return device
;
2284 int64_t predictedDataSize
= entry
->PredictedDataSize();
2285 if (entry
->IsStreamData() && entry
->IsAllowedOnDisk() && mEnableDiskDevice
) {
2286 // this is the default
2288 (void)CreateDiskDevice(); // ignore the error (check for mDiskDevice instead)
2292 // Bypass the cache if Content-Length says the entry will be too big
2293 if (predictedDataSize
!= -1 &&
2294 mDiskDevice
->EntryIsTooBig(predictedDataSize
)) {
2295 DebugOnly
<nsresult
> rv
= nsCacheService::DoomEntry(entry
);
2296 NS_ASSERTION(NS_SUCCEEDED(rv
),"DoomEntry() failed.");
2300 entry
->MarkBinding(); // enter state of binding
2301 nsresult rv
= mDiskDevice
->BindEntry(entry
);
2302 entry
->ClearBinding(); // exit state of binding
2303 if (NS_SUCCEEDED(rv
))
2304 device
= mDiskDevice
;
2308 // if we can't use mDiskDevice, try mMemoryDevice
2309 if (!device
&& mEnableMemoryDevice
&& entry
->IsAllowedInMemory()) {
2310 if (!mMemoryDevice
) {
2311 (void)CreateMemoryDevice(); // ignore the error (check for mMemoryDevice instead)
2313 if (mMemoryDevice
) {
2314 // Bypass the cache if Content-Length says entry will be too big
2315 if (predictedDataSize
!= -1 &&
2316 mMemoryDevice
->EntryIsTooBig(predictedDataSize
)) {
2317 DebugOnly
<nsresult
> rv
= nsCacheService::DoomEntry(entry
);
2318 NS_ASSERTION(NS_SUCCEEDED(rv
),"DoomEntry() failed.");
2322 entry
->MarkBinding(); // enter state of binding
2323 nsresult rv
= mMemoryDevice
->BindEntry(entry
);
2324 entry
->ClearBinding(); // exit state of binding
2325 if (NS_SUCCEEDED(rv
))
2326 device
= mMemoryDevice
;
2330 if (!device
&& entry
->IsStreamData() &&
2331 entry
->IsAllowedOffline() && mEnableOfflineDevice
) {
2332 if (!mOfflineDevice
) {
2333 (void)CreateOfflineDevice(); // ignore the error (check for mOfflineDevice instead)
2336 device
= entry
->CustomCacheDevice()
2337 ? entry
->CustomCacheDevice()
2341 entry
->MarkBinding();
2342 nsresult rv
= device
->BindEntry(entry
);
2343 entry
->ClearBinding();
2350 entry
->SetCacheDevice(device
);
2355 nsCacheService::DoomEntry(nsCacheEntry
* entry
)
2357 return gService
->DoomEntry_Internal(entry
, true);
2362 nsCacheService::DoomEntry_Internal(nsCacheEntry
* entry
,
2363 bool doProcessPendingRequests
)
2365 if (entry
->IsDoomed()) return NS_OK
;
2367 CACHE_LOG_DEBUG(("Dooming entry %p\n", entry
));
2368 nsresult rv
= NS_OK
;
2369 entry
->MarkDoomed();
2371 NS_ASSERTION(!entry
->IsBinding(), "Dooming entry while binding device.");
2372 nsCacheDevice
* device
= entry
->CacheDevice();
2373 if (device
) device
->DoomEntry(entry
);
2375 if (entry
->IsActive()) {
2376 // remove from active entries
2377 mActiveEntries
.RemoveEntry(entry
);
2378 CACHE_LOG_DEBUG(("Removed entry %p from mActiveEntries\n", entry
));
2379 entry
->MarkInactive();
2382 // put on doom list to wait for descriptors to close
2383 NS_ASSERTION(PR_CLIST_IS_EMPTY(entry
), "doomed entry still on device list");
2384 PR_APPEND_LINK(entry
, &mDoomedEntries
);
2386 // handle pending requests only if we're supposed to
2387 if (doProcessPendingRequests
) {
2388 // tell pending requests to get on with their lives...
2389 rv
= ProcessPendingRequests(entry
);
2391 // All requests have been removed, but there may still be open descriptors
2392 if (entry
->IsNotInUse()) {
2393 DeactivateEntry(entry
); // tell device to get rid of it
2401 nsCacheService::OnProfileShutdown(bool cleanse
)
2403 if (!gService
) return;
2404 if (!gService
->mInitialized
) {
2405 // The cache service has been shut down, but someone is still holding
2406 // a reference to it. Ignore this call.
2410 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSCACHESERVICE_ONPROFILESHUTDOWN
));
2411 gService
->mClearingEntries
= true;
2412 gService
->DoomActiveEntries(nullptr);
2415 gService
->CloseAllStreams();
2417 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSCACHESERVICE_ONPROFILESHUTDOWN
));
2418 gService
->ClearDoomList();
2420 // Make sure to wait for any pending cache-operations before
2421 // proceeding with destructive actions (bug #620660)
2422 (void) SyncWithCacheIOThread();
2424 if (gService
->mDiskDevice
&& gService
->mEnableDiskDevice
) {
2426 gService
->mDiskDevice
->EvictEntries(nullptr);
2428 gService
->mDiskDevice
->Shutdown();
2430 gService
->mEnableDiskDevice
= false;
2432 if (gService
->mOfflineDevice
&& gService
->mEnableOfflineDevice
) {
2434 gService
->mOfflineDevice
->EvictEntries(nullptr);
2436 gService
->mOfflineDevice
->Shutdown();
2438 gService
->mCustomOfflineDevices
.Enumerate(
2439 &nsCacheService::ShutdownCustomCacheDeviceEnum
, nullptr);
2441 gService
->mEnableOfflineDevice
= false;
2443 if (gService
->mMemoryDevice
) {
2444 // clear memory cache
2445 gService
->mMemoryDevice
->EvictEntries(nullptr);
2448 gService
->mClearingEntries
= false;
2453 nsCacheService::OnProfileChanged()
2455 if (!gService
) return;
2457 CACHE_LOG_DEBUG(("nsCacheService::OnProfileChanged"));
2459 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSCACHESERVICE_ONPROFILECHANGED
));
2461 gService
->mEnableDiskDevice
= gService
->mObserver
->DiskCacheEnabled();
2462 gService
->mEnableOfflineDevice
= gService
->mObserver
->OfflineCacheEnabled();
2463 gService
->mEnableMemoryDevice
= gService
->mObserver
->MemoryCacheEnabled();
2465 if (gService
->mDiskDevice
) {
2466 gService
->mDiskDevice
->SetCacheParentDirectory(gService
->mObserver
->DiskCacheParentDirectory());
2467 gService
->mDiskDevice
->SetCapacity(gService
->mObserver
->DiskCacheCapacity());
2469 // XXX initialization of mDiskDevice could be made lazily, if mEnableDiskDevice is false
2470 nsresult rv
= gService
->mDiskDevice
->Init();
2471 if (NS_FAILED(rv
)) {
2472 NS_ERROR("nsCacheService::OnProfileChanged: Re-initializing disk device failed");
2473 gService
->mEnableDiskDevice
= false;
2474 // XXX delete mDiskDevice?
2478 if (gService
->mOfflineDevice
) {
2479 gService
->mOfflineDevice
->SetCacheParentDirectory(gService
->mObserver
->OfflineCacheParentDirectory());
2480 gService
->mOfflineDevice
->SetCapacity(gService
->mObserver
->OfflineCacheCapacity());
2482 // XXX initialization of mOfflineDevice could be made lazily, if mEnableOfflineDevice is false
2483 nsresult rv
= gService
->mOfflineDevice
->InitWithSqlite(gService
->mStorageService
);
2484 if (NS_FAILED(rv
)) {
2485 NS_ERROR("nsCacheService::OnProfileChanged: Re-initializing offline device failed");
2486 gService
->mEnableOfflineDevice
= false;
2487 // XXX delete mOfflineDevice?
2491 // If memoryDevice exists, reset its size to the new profile
2492 if (gService
->mMemoryDevice
) {
2493 if (gService
->mEnableMemoryDevice
) {
2494 // make sure that capacity is reset to the right value
2495 int32_t capacity
= gService
->mObserver
->MemoryCacheCapacity();
2496 CACHE_LOG_DEBUG(("Resetting memory device capacity to %d\n",
2498 gService
->mMemoryDevice
->SetCapacity(capacity
);
2500 // tell memory device to evict everything
2501 CACHE_LOG_DEBUG(("memory device disabled\n"));
2502 gService
->mMemoryDevice
->SetCapacity(0);
2503 // Don't delete memory device, because some entries may be active still...
2510 nsCacheService::SetDiskCacheEnabled(bool enabled
)
2512 if (!gService
) return;
2513 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSCACHESERVICE_SETDISKCACHEENABLED
));
2514 gService
->mEnableDiskDevice
= enabled
;
2519 nsCacheService::SetDiskCacheCapacity(int32_t capacity
)
2521 if (!gService
) return;
2522 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSCACHESERVICE_SETDISKCACHECAPACITY
));
2524 if (gService
->mDiskDevice
) {
2525 gService
->mDiskDevice
->SetCapacity(capacity
);
2528 gService
->mEnableDiskDevice
= gService
->mObserver
->DiskCacheEnabled();
2532 nsCacheService::SetDiskCacheMaxEntrySize(int32_t maxSize
)
2534 if (!gService
) return;
2535 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSCACHESERVICE_SETDISKCACHEMAXENTRYSIZE
));
2537 if (gService
->mDiskDevice
) {
2538 gService
->mDiskDevice
->SetMaxEntrySize(maxSize
);
2543 nsCacheService::SetMemoryCacheMaxEntrySize(int32_t maxSize
)
2545 if (!gService
) return;
2546 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSCACHESERVICE_SETMEMORYCACHEMAXENTRYSIZE
));
2548 if (gService
->mMemoryDevice
) {
2549 gService
->mMemoryDevice
->SetMaxEntrySize(maxSize
);
2554 nsCacheService::SetOfflineCacheEnabled(bool enabled
)
2556 if (!gService
) return;
2557 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSCACHESERVICE_SETOFFLINECACHEENABLED
));
2558 gService
->mEnableOfflineDevice
= enabled
;
2562 nsCacheService::SetOfflineCacheCapacity(int32_t capacity
)
2564 if (!gService
) return;
2565 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSCACHESERVICE_SETOFFLINECACHECAPACITY
));
2567 if (gService
->mOfflineDevice
) {
2568 gService
->mOfflineDevice
->SetCapacity(capacity
);
2571 gService
->mEnableOfflineDevice
= gService
->mObserver
->OfflineCacheEnabled();
2576 nsCacheService::SetMemoryCache()
2578 if (!gService
) return;
2580 CACHE_LOG_DEBUG(("nsCacheService::SetMemoryCache"));
2582 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSCACHESERVICE_SETMEMORYCACHE
));
2584 gService
->mEnableMemoryDevice
= gService
->mObserver
->MemoryCacheEnabled();
2586 if (gService
->mEnableMemoryDevice
) {
2587 if (gService
->mMemoryDevice
) {
2588 int32_t capacity
= gService
->mObserver
->MemoryCacheCapacity();
2589 // make sure that capacity is reset to the right value
2590 CACHE_LOG_DEBUG(("Resetting memory device capacity to %d\n",
2592 gService
->mMemoryDevice
->SetCapacity(capacity
);
2595 if (gService
->mMemoryDevice
) {
2596 // tell memory device to evict everything
2597 CACHE_LOG_DEBUG(("memory device disabled\n"));
2598 gService
->mMemoryDevice
->SetCapacity(0);
2599 // Don't delete memory device, because some entries may be active still...
2605 /******************************************************************************
2606 * static methods for nsCacheEntryDescriptor
2607 *****************************************************************************/
2609 nsCacheService::CloseDescriptor(nsCacheEntryDescriptor
* descriptor
)
2611 // ask entry to remove descriptor
2612 nsCacheEntry
* entry
= descriptor
->CacheEntry();
2614 bool stillActive
= entry
->RemoveDescriptor(descriptor
, &doomEntry
);
2616 if (!entry
->IsValid()) {
2617 gService
->ProcessPendingRequests(entry
);
2621 gService
->DoomEntry_Internal(entry
, true);
2626 gService
->DeactivateEntry(entry
);
2632 nsCacheService::GetFileForEntry(nsCacheEntry
* entry
,
2635 nsCacheDevice
* device
= gService
->EnsureEntryHasDevice(entry
);
2636 if (!device
) return NS_ERROR_UNEXPECTED
;
2638 return device
->GetFileForEntry(entry
, result
);
2643 nsCacheService::OpenInputStreamForEntry(nsCacheEntry
* entry
,
2644 nsCacheAccessMode mode
,
2646 nsIInputStream
** result
)
2648 nsCacheDevice
* device
= gService
->EnsureEntryHasDevice(entry
);
2649 if (!device
) return NS_ERROR_UNEXPECTED
;
2651 return device
->OpenInputStreamForEntry(entry
, mode
, offset
, result
);
2655 nsCacheService::OpenOutputStreamForEntry(nsCacheEntry
* entry
,
2656 nsCacheAccessMode mode
,
2658 nsIOutputStream
** result
)
2660 nsCacheDevice
* device
= gService
->EnsureEntryHasDevice(entry
);
2661 if (!device
) return NS_ERROR_UNEXPECTED
;
2663 return device
->OpenOutputStreamForEntry(entry
, mode
, offset
, result
);
2668 nsCacheService::OnDataSizeChange(nsCacheEntry
* entry
, int32_t deltaSize
)
2670 nsCacheDevice
* device
= gService
->EnsureEntryHasDevice(entry
);
2671 if (!device
) return NS_ERROR_UNEXPECTED
;
2673 return device
->OnDataSizeChange(entry
, deltaSize
);
2677 nsCacheService::LockAcquired()
2679 MutexAutoLock
lock(mTimeStampLock
);
2680 mLockAcquiredTimeStamp
= TimeStamp::Now();
2684 nsCacheService::LockReleased()
2686 MutexAutoLock
lock(mTimeStampLock
);
2687 mLockAcquiredTimeStamp
= TimeStamp();
2691 nsCacheService::Lock(mozilla::Telemetry::ID mainThreadLockerID
)
2693 mozilla::Telemetry::ID lockerID
;
2694 mozilla::Telemetry::ID generalID
;
2696 if (NS_IsMainThread()) {
2697 lockerID
= mainThreadLockerID
;
2698 generalID
= mozilla::Telemetry::CACHE_SERVICE_LOCK_WAIT_MAINTHREAD_2
;
2700 lockerID
= mozilla::Telemetry::HistogramCount
;
2701 generalID
= mozilla::Telemetry::CACHE_SERVICE_LOCK_WAIT_2
;
2704 TimeStamp
start(TimeStamp::Now());
2705 MOZ_EVENT_TRACER_WAIT(nsCacheService::gService
, "net::cache::lock");
2707 gService
->mLock
.Lock();
2708 gService
->LockAcquired();
2710 TimeStamp
stop(TimeStamp::Now());
2711 MOZ_EVENT_TRACER_EXEC(nsCacheService::gService
, "net::cache::lock");
2713 // Telemetry isn't thread safe on its own, but this is OK because we're
2714 // protecting it with the cache lock.
2715 if (lockerID
!= mozilla::Telemetry::HistogramCount
) {
2716 mozilla::Telemetry::AccumulateTimeDelta(lockerID
, start
, stop
);
2718 mozilla::Telemetry::AccumulateTimeDelta(generalID
, start
, stop
);
2722 nsCacheService::Unlock()
2724 gService
->mLock
.AssertCurrentThreadOwns();
2726 nsTArray
<nsISupports
*> doomed
;
2727 doomed
.SwapElements(gService
->mDoomedObjects
);
2729 gService
->LockReleased();
2730 gService
->mLock
.Unlock();
2732 MOZ_EVENT_TRACER_DONE(nsCacheService::gService
, "net::cache::lock");
2734 for (uint32_t i
= 0; i
< doomed
.Length(); ++i
)
2735 doomed
[i
]->Release();
2739 nsCacheService::ReleaseObject_Locked(nsISupports
* obj
,
2740 nsIEventTarget
* target
)
2742 gService
->mLock
.AssertCurrentThreadOwns();
2745 if (!target
|| (NS_SUCCEEDED(target
->IsOnCurrentThread(&isCur
)) && isCur
)) {
2746 gService
->mDoomedObjects
.AppendElement(obj
);
2748 NS_ProxyRelease(target
, obj
);
2754 nsCacheService::SetCacheElement(nsCacheEntry
* entry
, nsISupports
* element
)
2756 entry
->SetData(element
);
2763 nsCacheService::ValidateEntry(nsCacheEntry
* entry
)
2765 nsCacheDevice
* device
= gService
->EnsureEntryHasDevice(entry
);
2766 if (!device
) return NS_ERROR_UNEXPECTED
;
2769 nsresult rv
= gService
->ProcessPendingRequests(entry
);
2770 NS_ASSERTION(rv
== NS_OK
, "ProcessPendingRequests failed.");
2771 // XXX what else should be done?
2778 nsCacheService::CacheCompressionLevel()
2780 int32_t level
= gService
->mObserver
->CacheCompressionLevel();
2786 nsCacheService::DeactivateEntry(nsCacheEntry
* entry
)
2788 CACHE_LOG_DEBUG(("Deactivating entry %p\n", entry
));
2789 nsresult rv
= NS_OK
;
2790 NS_ASSERTION(entry
->IsNotInUse(), "### deactivating an entry while in use!");
2791 nsCacheDevice
* device
= nullptr;
2793 if (mMaxDataSize
< entry
->DataSize() ) mMaxDataSize
= entry
->DataSize();
2794 if (mMaxMetaSize
< entry
->MetaDataSize() ) mMaxMetaSize
= entry
->MetaDataSize();
2796 if (entry
->IsDoomed()) {
2797 // remove from Doomed list
2798 PR_REMOVE_AND_INIT_LINK(entry
);
2799 } else if (entry
->IsActive()) {
2800 // remove from active entries
2801 mActiveEntries
.RemoveEntry(entry
);
2802 CACHE_LOG_DEBUG(("Removed deactivated entry %p from mActiveEntries\n",
2804 entry
->MarkInactive();
2806 // bind entry if necessary to store meta-data
2807 device
= EnsureEntryHasDevice(entry
);
2809 CACHE_LOG_DEBUG(("DeactivateEntry: unable to bind active "
2812 NS_WARNING("DeactivateEntry: unable to bind active entry\n");
2816 // if mInitialized == false,
2817 // then we're shutting down and this state is okay.
2818 NS_ASSERTION(!mInitialized
, "DeactivateEntry: bad cache entry state.");
2821 device
= entry
->CacheDevice();
2823 rv
= device
->DeactivateEntry(entry
);
2824 if (NS_FAILED(rv
)) {
2825 // increment deactivate failure count
2826 ++mDeactivateFailures
;
2829 // increment deactivating unbound entry statistic
2830 ++mDeactivatedUnboundEntries
;
2831 delete entry
; // because no one else will
2837 nsCacheService::ProcessPendingRequests(nsCacheEntry
* entry
)
2839 mozilla::eventtracer::AutoEventTracer
tracer(
2843 "net::cache::ProcessPendingRequests");
2845 nsresult rv
= NS_OK
;
2846 nsCacheRequest
* request
= (nsCacheRequest
*)PR_LIST_HEAD(&entry
->mRequestQ
);
2847 nsCacheRequest
* nextRequest
;
2848 bool newWriter
= false;
2850 CACHE_LOG_DEBUG(("ProcessPendingRequests for %sinitialized %s %salid entry %p\n",
2851 (entry
->IsInitialized()?"" : "Un"),
2852 (entry
->IsDoomed()?"DOOMED" : ""),
2853 (entry
->IsValid()? "V":"Inv"), entry
));
2855 if (request
== &entry
->mRequestQ
) return NS_OK
; // no queued requests
2857 if (!entry
->IsDoomed() && entry
->IsInvalid()) {
2858 // 1st descriptor closed w/o MarkValid()
2859 NS_ASSERTION(PR_CLIST_IS_EMPTY(&entry
->mDescriptorQ
), "shouldn't be here with open descriptors");
2862 // verify no ACCESS_WRITE requests(shouldn't have any of these)
2863 while (request
!= &entry
->mRequestQ
) {
2864 NS_ASSERTION(request
->AccessRequested() != nsICache::ACCESS_WRITE
,
2865 "ACCESS_WRITE request should have been given a new entry");
2866 request
= (nsCacheRequest
*)PR_NEXT_LINK(request
);
2868 request
= (nsCacheRequest
*)PR_LIST_HEAD(&entry
->mRequestQ
);
2870 // find first request with ACCESS_READ_WRITE (if any) and promote it to 1st writer
2871 while (request
!= &entry
->mRequestQ
) {
2872 if (request
->AccessRequested() == nsICache::ACCESS_READ_WRITE
) {
2874 CACHE_LOG_DEBUG((" promoting request %p to 1st writer\n", request
));
2878 request
= (nsCacheRequest
*)PR_NEXT_LINK(request
);
2881 if (request
== &entry
->mRequestQ
) // no requests asked for ACCESS_READ_WRITE, back to top
2882 request
= (nsCacheRequest
*)PR_LIST_HEAD(&entry
->mRequestQ
);
2884 // XXX what should we do if there are only READ requests in queue?
2885 // XXX serialize their accesses, give them only read access, but force them to check validate flag?
2886 // XXX or do readers simply presume the entry is valid
2887 // See fix for bug #467392 below
2890 nsCacheAccessMode accessGranted
= nsICache::ACCESS_NONE
;
2892 while (request
!= &entry
->mRequestQ
) {
2893 nextRequest
= (nsCacheRequest
*)PR_NEXT_LINK(request
);
2894 CACHE_LOG_DEBUG((" %sync request %p for %p\n",
2895 (request
->mListener
?"As":"S"), request
, entry
));
2897 if (request
->mListener
) {
2900 PR_REMOVE_AND_INIT_LINK(request
);
2902 if (entry
->IsDoomed()) {
2903 rv
= ProcessRequest(request
, false, nullptr);
2904 if (rv
== NS_ERROR_CACHE_WAIT_FOR_VALIDATION
)
2909 if (NS_FAILED(rv
)) {
2912 } else if (entry
->IsValid() || newWriter
) {
2913 rv
= entry
->RequestAccess(request
, &accessGranted
);
2914 NS_ASSERTION(NS_SUCCEEDED(rv
),
2915 "if entry is valid, RequestAccess must succeed.");
2916 // XXX if (newWriter) NS_ASSERTION( accessGranted == request->AccessRequested(), "why not?");
2918 // entry->CreateDescriptor dequeues request, and queues descriptor
2919 nsICacheEntryDescriptor
*descriptor
= nullptr;
2920 rv
= entry
->CreateDescriptor(request
,
2924 // post call to listener to report error or descriptor
2925 rv
= NotifyListener(request
, descriptor
, accessGranted
, rv
);
2927 if (NS_FAILED(rv
)) {
2932 // read-only request to an invalid entry - need to wait for
2933 // the entry to become valid so we post an event to process
2934 // the request again later (bug #467392)
2935 nsCOMPtr
<nsIRunnable
> ev
=
2936 new nsProcessRequestEvent(request
);
2937 rv
= DispatchToCacheIOThread(ev
);
2938 if (NS_FAILED(rv
)) {
2939 delete request
; // avoid leak
2944 // Synchronous request
2947 if (newWriter
) break; // process remaining requests after validation
2948 request
= nextRequest
;
2955 nsCacheService::IsDoomListEmpty()
2957 nsCacheEntry
* entry
= (nsCacheEntry
*)PR_LIST_HEAD(&mDoomedEntries
);
2958 return &mDoomedEntries
== entry
;
2962 nsCacheService::ClearDoomList()
2964 nsCacheEntry
* entry
= (nsCacheEntry
*)PR_LIST_HEAD(&mDoomedEntries
);
2966 while (entry
!= &mDoomedEntries
) {
2967 nsCacheEntry
* next
= (nsCacheEntry
*)PR_NEXT_LINK(entry
);
2969 entry
->DetachDescriptors();
2970 DeactivateEntry(entry
);
2976 nsCacheService::GetActiveEntries(PLDHashTable
* table
,
2977 PLDHashEntryHdr
* hdr
,
2981 static_cast<nsVoidArray
*>(arg
)->AppendElement(
2982 ((nsCacheEntryHashTableEntry
*)hdr
)->cacheEntry
);
2983 return PL_DHASH_NEXT
;
2986 struct ActiveEntryArgs
2988 nsTArray
<nsCacheEntry
*>* mActiveArray
;
2989 nsCacheService::DoomCheckFn mCheckFn
;
2993 nsCacheService::DoomActiveEntries(DoomCheckFn check
)
2995 nsAutoTArray
<nsCacheEntry
*, 8> array
;
2996 ActiveEntryArgs args
= { &array
, check
};
2998 mActiveEntries
.VisitEntries(RemoveActiveEntry
, &args
);
3000 uint32_t count
= array
.Length();
3001 for (uint32_t i
=0; i
< count
; ++i
)
3002 DoomEntry_Internal(array
[i
], true);
3006 nsCacheService::RemoveActiveEntry(PLDHashTable
* table
,
3007 PLDHashEntryHdr
* hdr
,
3011 nsCacheEntry
* entry
= ((nsCacheEntryHashTableEntry
*)hdr
)->cacheEntry
;
3012 NS_ASSERTION(entry
, "### active entry = nullptr!");
3014 ActiveEntryArgs
* args
= static_cast<ActiveEntryArgs
*>(arg
);
3015 if (args
->mCheckFn
&& !args
->mCheckFn(entry
))
3016 return PL_DHASH_NEXT
;
3018 NS_ASSERTION(args
->mActiveArray
, "### array = nullptr!");
3019 args
->mActiveArray
->AppendElement(entry
);
3021 // entry is being removed from the active entry list
3022 entry
->MarkInactive();
3023 return PL_DHASH_REMOVE
; // and continue enumerating
3028 nsCacheService::CloseAllStreams()
3030 nsTArray
<nsRefPtr
<nsCacheEntryDescriptor::nsInputStreamWrapper
> > inputs
;
3031 nsTArray
<nsRefPtr
<nsCacheEntryDescriptor::nsOutputStreamWrapper
> > outputs
;
3034 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSCACHESERVICE_CLOSEALLSTREAMS
));
3036 nsVoidArray entries
;
3039 // make sure there is no active entry
3040 mActiveEntries
.VisitEntries(GetActiveEntries
, &entries
);
3041 NS_ASSERTION(entries
.Count() == 0, "Bad state");
3044 // Get doomed entries
3045 nsCacheEntry
* entry
= (nsCacheEntry
*)PR_LIST_HEAD(&mDoomedEntries
);
3046 while (entry
!= &mDoomedEntries
) {
3047 nsCacheEntry
* next
= (nsCacheEntry
*)PR_NEXT_LINK(entry
);
3048 entries
.AppendElement(entry
);
3052 // Iterate through all entries and collect input and output streams
3053 for (int32_t i
= 0 ; i
< entries
.Count() ; i
++) {
3054 entry
= static_cast<nsCacheEntry
*>(entries
.ElementAt(i
));
3056 nsTArray
<nsRefPtr
<nsCacheEntryDescriptor
> > descs
;
3057 entry
->GetDescriptors(descs
);
3059 for (uint32_t j
= 0 ; j
< descs
.Length() ; j
++) {
3060 if (descs
[j
]->mOutputWrapper
)
3061 outputs
.AppendElement(descs
[j
]->mOutputWrapper
);
3063 for (int32_t k
= 0 ; k
< descs
[j
]->mInputWrappers
.Count() ; k
++)
3064 inputs
.AppendElement(static_cast<
3065 nsCacheEntryDescriptor::nsInputStreamWrapper
*>(
3066 descs
[j
]->mInputWrappers
[k
]));
3072 for (i
= 0 ; i
< inputs
.Length() ; i
++)
3075 for (i
= 0 ; i
< outputs
.Length() ; i
++)
3076 outputs
[i
]->Close();
3081 nsCacheService::GetClearingEntries()
3084 return gService
->mClearingEntries
;
3088 void nsCacheService::GetCacheBaseDirectoty(nsIFile
** result
)
3091 if (!gService
|| !gService
->mObserver
)
3094 nsCOMPtr
<nsIFile
> directory
=
3095 gService
->mObserver
->DiskCacheParentDirectory();
3099 directory
->Clone(result
);
3103 void nsCacheService::GetDiskCacheDirectory(nsIFile
** result
)
3105 nsCOMPtr
<nsIFile
> directory
;
3106 GetCacheBaseDirectoty(getter_AddRefs(directory
));
3110 nsresult rv
= directory
->AppendNative(NS_LITERAL_CSTRING("Cache"));
3114 directory
.forget(result
);
3118 void nsCacheService::GetAppCacheDirectory(nsIFile
** result
)
3120 nsCOMPtr
<nsIFile
> directory
;
3121 GetCacheBaseDirectoty(getter_AddRefs(directory
));
3125 nsresult rv
= directory
->AppendNative(NS_LITERAL_CSTRING("OfflineCache"));
3129 directory
.forget(result
);
3133 #if defined(PR_LOGGING)
3135 nsCacheService::LogCacheStatistics()
3137 uint32_t hitPercentage
= (uint32_t)((((double)mCacheHits
) /
3138 ((double)(mCacheHits
+ mCacheMisses
))) * 100);
3139 CACHE_LOG_ALWAYS(("\nCache Service Statistics:\n\n"));
3140 CACHE_LOG_ALWAYS((" TotalEntries = %d\n", mTotalEntries
));
3141 CACHE_LOG_ALWAYS((" Cache Hits = %d\n", mCacheHits
));
3142 CACHE_LOG_ALWAYS((" Cache Misses = %d\n", mCacheMisses
));
3143 CACHE_LOG_ALWAYS((" Cache Hit %% = %d%%\n", hitPercentage
));
3144 CACHE_LOG_ALWAYS((" Max Key Length = %d\n", mMaxKeyLength
));
3145 CACHE_LOG_ALWAYS((" Max Meta Size = %d\n", mMaxMetaSize
));
3146 CACHE_LOG_ALWAYS((" Max Data Size = %d\n", mMaxDataSize
));
3147 CACHE_LOG_ALWAYS(("\n"));
3148 CACHE_LOG_ALWAYS((" Deactivate Failures = %d\n",
3149 mDeactivateFailures
));
3150 CACHE_LOG_ALWAYS((" Deactivated Unbound Entries = %d\n",
3151 mDeactivatedUnboundEntries
));
3156 nsCacheService::SetDiskSmartSize()
3158 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSCACHESERVICE_SETDISKSMARTSIZE
));
3160 if (!gService
) return NS_ERROR_NOT_AVAILABLE
;
3162 return gService
->SetDiskSmartSize_Locked();
3166 nsCacheService::SetDiskSmartSize_Locked()
3170 if (mozilla::net::CacheObserver::UseNewCache()) {
3171 return NS_ERROR_NOT_AVAILABLE
;
3174 if (!mObserver
->DiskCacheParentDirectory())
3175 return NS_ERROR_NOT_AVAILABLE
;
3178 return NS_ERROR_NOT_AVAILABLE
;
3180 if (!mObserver
->SmartSizeEnabled())
3181 return NS_ERROR_NOT_AVAILABLE
;
3183 nsAutoString cachePath
;
3184 rv
= mObserver
->DiskCacheParentDirectory()->GetPath(cachePath
);
3185 if (NS_SUCCEEDED(rv
)) {
3186 nsCOMPtr
<nsIRunnable
> event
=
3187 new nsGetSmartSizeEvent(cachePath
, mDiskDevice
->getCacheSize(),
3188 mObserver
->ShouldUseOldMaxSmartSize());
3189 DispatchToCacheIOThread(event
);
3191 return NS_ERROR_FAILURE
;
3198 nsCacheService::MoveOrRemoveDiskCache(nsIFile
*aOldCacheDir
,
3199 nsIFile
*aNewCacheDir
,
3200 const char *aCacheSubdir
)
3203 if (NS_FAILED(aOldCacheDir
->Equals(aNewCacheDir
, &same
)) || same
)
3206 nsCOMPtr
<nsIFile
> aOldCacheSubdir
;
3207 aOldCacheDir
->Clone(getter_AddRefs(aOldCacheSubdir
));
3209 nsresult rv
= aOldCacheSubdir
->AppendNative(
3210 nsDependentCString(aCacheSubdir
));
3215 if (NS_FAILED(aOldCacheSubdir
->Exists(&exists
)) || !exists
)
3218 nsCOMPtr
<nsIFile
> aNewCacheSubdir
;
3219 aNewCacheDir
->Clone(getter_AddRefs(aNewCacheSubdir
));
3221 rv
= aNewCacheSubdir
->AppendNative(nsDependentCString(aCacheSubdir
));
3225 nsAutoCString newPath
;
3226 rv
= aNewCacheSubdir
->GetNativePath(newPath
);
3230 if (NS_SUCCEEDED(aNewCacheSubdir
->Exists(&exists
)) && !exists
) {
3231 // New cache directory does not exist, try to move the old one here
3232 // rename needs an empty target directory
3234 // Make sure the parent of the target sub-dir exists
3235 rv
= aNewCacheDir
->Create(nsIFile::DIRECTORY_TYPE
, 0777);
3236 if (NS_SUCCEEDED(rv
) || NS_ERROR_FILE_ALREADY_EXISTS
== rv
) {
3237 nsAutoCString oldPath
;
3238 rv
= aOldCacheSubdir
->GetNativePath(oldPath
);
3241 if (rename(oldPath
.get(), newPath
.get()) == 0)
3246 // Delay delete by 1 minute to avoid IO thrash on startup.
3247 nsDeleteDir::DeleteDir(aOldCacheSubdir
, false, 60000);
3251 IsEntryPrivate(nsCacheEntry
* entry
)
3253 return entry
->IsPrivate();
3257 nsCacheService::LeavePrivateBrowsing()
3259 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSCACHESERVICE_LEAVEPRIVATEBROWSING
));
3261 gService
->DoomActiveEntries(IsEntryPrivate
);
3263 if (gService
->mMemoryDevice
) {
3264 // clear memory cache
3265 gService
->mMemoryDevice
->EvictPrivateEntries();
3269 MOZ_DEFINE_MALLOC_SIZE_OF(DiskCacheDeviceMallocSizeOf
)
3272 nsCacheService::CollectReports(nsIHandleReportCallback
* aHandleReport
,
3273 nsISupports
* aData
, bool aAnonymize
)
3277 nsCacheServiceAutoLock
3278 lock(LOCK_TELEM(NSCACHESERVICE_DISKDEVICEHEAPSIZE
));
3279 disk
= mDiskDevice
->SizeOfIncludingThis(DiskCacheDeviceMallocSizeOf
);
3282 size_t memory
= mMemoryDevice
? mMemoryDevice
->TotalSize() : 0;
3284 #define REPORT(_path, _amount, _desc) \
3287 rv = aHandleReport->Callback(EmptyCString(), \
3288 NS_LITERAL_CSTRING(_path), \
3289 KIND_HEAP, UNITS_BYTES, _amount, \
3290 NS_LITERAL_CSTRING(_desc), aData); \
3291 NS_ENSURE_SUCCESS(rv, rv); \
3294 REPORT("explicit/network/disk-cache", disk
,
3295 "Memory used by the network disk cache.");
3297 REPORT("explicit/network/memory-cache", memory
,
3298 "Memory used by the network memory cache.");