Bug 707436 - nsSetSmartSizeEvent can cause a lot of IO on the main thread

This commit is contained in:
Michal Novotny 2012-01-06 16:19:10 +01:00
parent 66d485f43c
commit 16221670fa
4 changed files with 107 additions and 62 deletions

View File

@ -75,6 +75,7 @@
#include "mozilla/Util.h" // for DebugOnly #include "mozilla/Util.h" // for DebugOnly
#include "mozilla/Services.h" #include "mozilla/Services.h"
#include "mozilla/Telemetry.h" #include "mozilla/Telemetry.h"
#include "nsITimer.h"
#include "mozilla/FunctionTimer.h" #include "mozilla/FunctionTimer.h"
@ -152,6 +153,7 @@ public:
, mDiskCacheEnabled(false) , mDiskCacheEnabled(false)
, mDiskCacheCapacity(0) , mDiskCacheCapacity(0)
, mDiskCacheMaxEntrySize(-1) // -1 means "no limit" , mDiskCacheMaxEntrySize(-1) // -1 means "no limit"
, mSmartSizeEnabled(false)
, mOfflineCacheEnabled(false) , mOfflineCacheEnabled(false)
, mOfflineCacheCapacity(0) , mOfflineCacheCapacity(0)
, mMemoryCacheEnabled(true) , mMemoryCacheEnabled(true)
@ -175,6 +177,7 @@ public:
void SetDiskCacheCapacity(PRInt32); void SetDiskCacheCapacity(PRInt32);
PRInt32 DiskCacheMaxEntrySize() { return mDiskCacheMaxEntrySize; } PRInt32 DiskCacheMaxEntrySize() { return mDiskCacheMaxEntrySize; }
nsILocalFile * DiskCacheParentDirectory() { return mDiskCacheParentDirectory; } nsILocalFile * DiskCacheParentDirectory() { return mDiskCacheParentDirectory; }
bool SmartSizeEnabled() { return mSmartSizeEnabled; }
bool OfflineCacheEnabled(); bool OfflineCacheEnabled();
PRInt32 OfflineCacheCapacity() { return mOfflineCacheCapacity; } PRInt32 OfflineCacheCapacity() { return mOfflineCacheCapacity; }
@ -199,6 +202,7 @@ private:
PRInt32 mDiskCacheCapacity; // in kilobytes PRInt32 mDiskCacheCapacity; // in kilobytes
PRInt32 mDiskCacheMaxEntrySize; // in kilobytes PRInt32 mDiskCacheMaxEntrySize; // in kilobytes
nsCOMPtr<nsILocalFile> mDiskCacheParentDirectory; nsCOMPtr<nsILocalFile> mDiskCacheParentDirectory;
bool mSmartSizeEnabled;
bool mOfflineCacheEnabled; bool mOfflineCacheEnabled;
PRInt32 mOfflineCacheCapacity; // in kilobytes PRInt32 mOfflineCacheCapacity; // in kilobytes
@ -218,6 +222,23 @@ private:
NS_IMPL_THREADSAFE_ISUPPORTS1(nsCacheProfilePrefObserver, nsIObserver) NS_IMPL_THREADSAFE_ISUPPORTS1(nsCacheProfilePrefObserver, nsIObserver)
class nsSetDiskSmartSizeCallback : public nsITimerCallback
{
public:
NS_DECL_ISUPPORTS
NS_IMETHOD Notify(nsITimer* aTimer) {
if (nsCacheService::gService) {
nsCacheServiceAutoLock autoLock;
nsCacheService::gService->SetDiskSmartSize_Locked();
nsCacheService::gService->mSmartSizeTimer = nsnull;
}
return NS_OK;
}
};
NS_IMPL_THREADSAFE_ISUPPORTS1(nsSetDiskSmartSizeCallback, nsITimerCallback)
// Runnable sent to main thread after the cache IO thread calculates available // Runnable sent to main thread after the cache IO thread calculates available
// disk space, so that there is no race in setting mDiskCacheCapacity. // disk space, so that there is no race in setting mDiskCacheCapacity.
class nsSetSmartSizeEvent: public nsRunnable class nsSetSmartSizeEvent: public nsRunnable
@ -228,32 +249,27 @@ public:
NS_IMETHOD Run() NS_IMETHOD Run()
{ {
nsresult rv;
NS_ASSERTION(NS_IsMainThread(), NS_ASSERTION(NS_IsMainThread(),
"Setting smart size data off the main thread"); "Setting smart size data off the main thread");
// Main thread may have already called nsCacheService::Shutdown // Main thread may have already called nsCacheService::Shutdown
if (!nsCacheService::gService || !nsCacheService::gService->mObserver) if (!nsCacheService::gService || !nsCacheService::gService->mObserver)
return NS_ERROR_NOT_AVAILABLE; return NS_ERROR_NOT_AVAILABLE;
bool smartSizeEnabled; // Ensure smart sizing wasn't switched off while event was pending.
nsCOMPtr<nsIPrefBranch2> branch = do_GetService(NS_PREFSERVICE_CONTRACTID); // It is safe to access the observer without the lock since we are
if (!branch) { // on the main thread and the value changes only on the main thread.
NS_WARNING("Failed to get pref service!"); if (!nsCacheService::gService->mObserver->SmartSizeEnabled())
return NS_ERROR_NOT_AVAILABLE; return NS_OK;
}
// ensure smart sizing wasn't switched off while event was pending nsCacheService::SetDiskCacheCapacity(mSmartSize);
rv = branch->GetBoolPref(DISK_CACHE_SMART_SIZE_ENABLED_PREF,
&smartSizeEnabled); nsCOMPtr<nsIPrefBranch2> ps = do_GetService(NS_PREFSERVICE_CONTRACTID);
if (NS_FAILED(rv)) if (!ps ||
smartSizeEnabled = false; NS_FAILED(ps->SetIntPref(DISK_CACHE_SMART_SIZE_PREF, mSmartSize)))
if (smartSizeEnabled) { NS_WARNING("Failed to set smart size pref");
nsCacheService::SetDiskCacheCapacity(mSmartSize);
rv = branch->SetIntPref(DISK_CACHE_SMART_SIZE_PREF, mSmartSize); return NS_OK;
if (NS_FAILED(rv))
NS_WARNING("Failed to set smart size pref");
}
return rv;
} }
private: private:
@ -445,13 +461,12 @@ nsCacheProfilePrefObserver::Observe(nsISupports * subject,
// Update the cache capacity when smart sizing is turned on/off // Update the cache capacity when smart sizing is turned on/off
} else if (!strcmp(DISK_CACHE_SMART_SIZE_ENABLED_PREF, data.get())) { } else if (!strcmp(DISK_CACHE_SMART_SIZE_ENABLED_PREF, data.get())) {
// Is the update because smartsizing was turned on, or off? // Is the update because smartsizing was turned on, or off?
bool smartSizeEnabled;
rv = branch->GetBoolPref(DISK_CACHE_SMART_SIZE_ENABLED_PREF, rv = branch->GetBoolPref(DISK_CACHE_SMART_SIZE_ENABLED_PREF,
&smartSizeEnabled); &mSmartSizeEnabled);
if (NS_FAILED(rv)) if (NS_FAILED(rv))
return rv; return rv;
PRInt32 newCapacity = 0; PRInt32 newCapacity = 0;
if (smartSizeEnabled) { if (mSmartSizeEnabled) {
nsCacheService::SetDiskSmartSize(); nsCacheService::SetDiskSmartSize();
} else { } else {
// Smart sizing switched off: use user specified size // Smart sizing switched off: use user specified size
@ -671,21 +686,22 @@ nsCacheProfilePrefObserver::PermittedToSmartSize(nsIPrefBranch* branch, bool
// of 50 MB, then keep user's value. Otherwise use smart sizing. // of 50 MB, then keep user's value. Otherwise use smart sizing.
rv = branch->GetIntPref(DISK_CACHE_CAPACITY_PREF, &oldCapacity); rv = branch->GetIntPref(DISK_CACHE_CAPACITY_PREF, &oldCapacity);
if (oldCapacity < PRE_GECKO_2_0_DEFAULT_CACHE_SIZE) { if (oldCapacity < PRE_GECKO_2_0_DEFAULT_CACHE_SIZE) {
branch->SetBoolPref(DISK_CACHE_SMART_SIZE_ENABLED_PREF, mSmartSizeEnabled = false;
false); branch->SetBoolPref(DISK_CACHE_SMART_SIZE_ENABLED_PREF,
return false; mSmartSizeEnabled);
return mSmartSizeEnabled;
} }
} }
// Set manual setting to MAX cache size as starting val for any // Set manual setting to MAX cache size as starting val for any
// adjustment by user: (bug 559942 comment 65) // adjustment by user: (bug 559942 comment 65)
branch->SetIntPref(DISK_CACHE_CAPACITY_PREF, MAX_CACHE_SIZE); branch->SetIntPref(DISK_CACHE_CAPACITY_PREF, MAX_CACHE_SIZE);
} }
bool smartSizeEnabled;
rv = branch->GetBoolPref(DISK_CACHE_SMART_SIZE_ENABLED_PREF, rv = branch->GetBoolPref(DISK_CACHE_SMART_SIZE_ENABLED_PREF,
&smartSizeEnabled); &mSmartSizeEnabled);
if (NS_FAILED(rv)) if (NS_FAILED(rv))
return false; mSmartSizeEnabled = false;
return !!smartSizeEnabled; return mSmartSizeEnabled;
} }
@ -758,20 +774,12 @@ nsCacheProfilePrefObserver::ReadPrefs(nsIPrefBranch* branch)
if (PermittedToSmartSize(branch, firstSmartSizeRun)) { if (PermittedToSmartSize(branch, firstSmartSizeRun)) {
// Avoid evictions: use previous cache size until smart size event // Avoid evictions: use previous cache size until smart size event
// updates mDiskCacheCapacity // updates mDiskCacheCapacity
if (!firstSmartSizeRun) { rv = branch->GetIntPref(firstSmartSizeRun ?
PRInt32 oldSmartSize; DISK_CACHE_CAPACITY_PREF :
rv = branch->GetIntPref(DISK_CACHE_SMART_SIZE_PREF, DISK_CACHE_SMART_SIZE_PREF,
&oldSmartSize); &mDiskCacheCapacity);
mDiskCacheCapacity = oldSmartSize; if (NS_FAILED(rv))
} else { mDiskCacheCapacity = DEFAULT_CACHE_SIZE;
PRInt32 oldCapacity;
rv = branch->GetIntPref(DISK_CACHE_CAPACITY_PREF, &oldCapacity);
if (NS_SUCCEEDED(rv)) {
mDiskCacheCapacity = oldCapacity;
} else {
mDiskCacheCapacity = DEFAULT_CACHE_SIZE;
}
}
} }
if (firstSmartSizeRun) { if (firstSmartSizeRun) {
@ -1142,6 +1150,11 @@ nsCacheService::Shutdown()
ClearDoomList(); ClearDoomList();
ClearActiveEntries(); ClearActiveEntries();
if (mSmartSizeTimer) {
mSmartSizeTimer->Cancel();
mSmartSizeTimer = nsnull;
}
// Make sure to wait for any pending cache-operations before // Make sure to wait for any pending cache-operations before
// proceeding with destructive actions (bug #620660) // proceeding with destructive actions (bug #620660)
(void) SyncWithCacheIOThread(); (void) SyncWithCacheIOThread();
@ -1447,7 +1460,22 @@ nsCacheService::CreateDiskDevice()
mDiskDevice = nsnull; mDiskDevice = nsnull;
} }
SetDiskSmartSize_Locked(true); // Disk device is usually created during the startup. Delay smart size
// calculation to avoid possible massive IO caused by eviction of entries
// in case the new smart size is smaller than current cache usage.
if (!mSmartSizeTimer) {
mSmartSizeTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
if (NS_FAILED(rv))
return rv;
rv = mSmartSizeTimer->InitWithCallback(new nsSetDiskSmartSizeCallback(),
1000*60*3,
nsITimer::TYPE_ONE_SHOT);
if (NS_FAILED(rv)) {
NS_WARNING("Failed to post smart size timer");
mSmartSizeTimer = nsnull;
}
}
return rv; return rv;
} }
@ -2652,11 +2680,11 @@ nsCacheService::SetDiskSmartSize()
if (!gService) return NS_ERROR_NOT_AVAILABLE; if (!gService) return NS_ERROR_NOT_AVAILABLE;
return gService->SetDiskSmartSize_Locked(false); return gService->SetDiskSmartSize_Locked();
} }
nsresult nsresult
nsCacheService::SetDiskSmartSize_Locked(bool checkPref) nsCacheService::SetDiskSmartSize_Locked()
{ {
nsresult rv; nsresult rv;
@ -2666,17 +2694,8 @@ nsCacheService::SetDiskSmartSize_Locked(bool checkPref)
if (!mDiskDevice) if (!mDiskDevice)
return NS_ERROR_NOT_AVAILABLE; return NS_ERROR_NOT_AVAILABLE;
if (checkPref) { if (!mObserver->SmartSizeEnabled())
nsCOMPtr<nsIPrefBranch2> branch = do_GetService(NS_PREFSERVICE_CONTRACTID); return NS_ERROR_NOT_AVAILABLE;
if (!branch) return NS_ERROR_FAILURE;
bool smartSizeEnabled;
rv = branch->GetBoolPref(DISK_CACHE_SMART_SIZE_ENABLED_PREF,
&smartSizeEnabled);
if (NS_FAILED(rv) || !smartSizeEnabled)
return NS_ERROR_NOT_AVAILABLE;
}
nsAutoString cachePath; nsAutoString cachePath;
rv = mObserver->DiskCacheParentDirectory()->GetPath(cachePath); rv = mObserver->DiskCacheParentDirectory()->GetPath(cachePath);

View File

@ -63,6 +63,7 @@ class nsDiskCacheDevice;
class nsMemoryCacheDevice; class nsMemoryCacheDevice;
class nsOfflineCacheDevice; class nsOfflineCacheDevice;
class nsCacheServiceAutoLock; class nsCacheServiceAutoLock;
class nsITimer;
/****************************************************************************** /******************************************************************************
@ -196,6 +197,7 @@ private:
friend class nsProcessRequestEvent; friend class nsProcessRequestEvent;
friend class nsSetSmartSizeEvent; friend class nsSetSmartSizeEvent;
friend class nsBlockOnCacheThreadEvent; friend class nsBlockOnCacheThreadEvent;
friend class nsSetDiskSmartSizeCallback;
/** /**
* Internal Methods * Internal Methods
@ -264,7 +266,7 @@ private:
void LogCacheStatistics(); void LogCacheStatistics();
#endif #endif
nsresult SetDiskSmartSize_Locked(bool checkPref); nsresult SetDiskSmartSize_Locked();
/** /**
* Data Members * Data Members
@ -280,6 +282,7 @@ private:
nsCOMPtr<nsIThread> mCacheIOThread; nsCOMPtr<nsIThread> mCacheIOThread;
nsTArray<nsISupports*> mDoomedObjects; nsTArray<nsISupports*> mDoomedObjects;
nsCOMPtr<nsITimer> mSmartSizeTimer;
bool mInitialized; bool mInitialized;

View File

@ -118,6 +118,22 @@ private:
nsDiskCacheBinding *mBinding; nsDiskCacheBinding *mBinding;
}; };
class nsEvictDiskCacheEntriesEvent : public nsRunnable {
public:
nsEvictDiskCacheEntriesEvent(nsDiskCacheDevice *device)
: mDevice(device) {}
NS_IMETHOD Run()
{
nsCacheServiceAutoLock lock;
mDevice->EvictDiskCacheEntries(mDevice->mCacheCapacity);
return NS_OK;
}
private:
nsDiskCacheDevice *mDevice;
};
/****************************************************************************** /******************************************************************************
* nsDiskCacheEvictor * nsDiskCacheEvictor
* *
@ -1120,8 +1136,14 @@ nsDiskCacheDevice::SetCapacity(PRUint32 capacity)
// Units are KiB's // Units are KiB's
mCacheCapacity = capacity; mCacheCapacity = capacity;
if (Initialized()) { if (Initialized()) {
// start evicting entries if the new size is smaller! if (NS_IsMainThread()) {
EvictDiskCacheEntries(mCacheCapacity); // Do not evict entries on the main thread
nsCacheService::DispatchToCacheIOThread(
new nsEvictDiskCacheEntriesEvent(this));
} else {
// start evicting entries if the new size is smaller!
EvictDiskCacheEntries(mCacheCapacity);
}
} }
// Let cache map know of the new capacity // Let cache map know of the new capacity
mCacheMap.NotifyCapacityChange(capacity); mCacheMap.NotifyCapacityChange(capacity);

View File

@ -107,6 +107,7 @@ public:
private: private:
friend class nsDiskCacheDeviceDeactivateEntryEvent; friend class nsDiskCacheDeviceDeactivateEntryEvent;
friend class nsEvictDiskCacheEntriesEvent;
/** /**
* Private methods * Private methods
*/ */