diff --git a/netwerk/cache/public/nsICacheEntryDescriptor.idl b/netwerk/cache/public/nsICacheEntryDescriptor.idl index 2d98235b9263..978b67388856 100644 --- a/netwerk/cache/public/nsICacheEntryDescriptor.idl +++ b/netwerk/cache/public/nsICacheEntryDescriptor.idl @@ -31,6 +31,8 @@ interface nsISimpleEnumerator; interface nsICacheListener; interface nsITransport; interface nsIFile; +interface nsICacheMetaDataVisitor; + [scriptable, uuid(49c1a11d-f5d2-4f09-8262-551e64908ada)] interface nsICacheEntryDescriptor : nsICacheEntryInfo @@ -116,6 +118,20 @@ interface nsICacheEntryDescriptor : nsICacheEntryInfo string getMetaDataElement(in string key); void setMetaDataElement(in string key, in string value); - nsISimpleEnumerator getMetaDataEnumerator(); /* todo */ + /** + * Visitor will be called with key/value pair for each meta data element. + */ + void visitMetaData(in nsICacheMetaDataVisitor visitor); }; + + +[scriptable, uuid(22f9a49c-3cf8-4c23-8006-54efb11ac562)] +interface nsICacheMetaDataVisitor : nsISupports +{ + /** + * Called for each key/value pair in the meta data for a cache entry + */ + boolean visitMetaDataElement(in string key, + in string value); +}; diff --git a/netwerk/cache/src/nsCache.cpp b/netwerk/cache/src/nsCache.cpp index edbd99adcc45..8c8bc34ff3cc 100644 --- a/netwerk/cache/src/nsCache.cpp +++ b/netwerk/cache/src/nsCache.cpp @@ -26,32 +26,20 @@ #include "nsCache.h" #include "nsReadableUtils.h" + /** * Cache Service Utility Functions */ -#if 0 -// time conversion utils from nsCachedNetData.cpp -// Convert PRTime to unix-style time_t, i.e. seconds since the epoch -PRUint32 -ConvertPRTimeToSeconds(PRTime time64) -{ - double fpTime; - LL_L2D(fpTime, time64); - return (PRUint32)(fpTime * 1e-6 + 0.5); -} +#if defined(PR_LOGGING) +PRLogModuleInfo * gCacheLog = nsnull; - -// Convert unix-style time_t, i.e. seconds since the epoch, to PRTime -PRTime -ConvertSecondsToPRTime(PRUint32 seconds) +void +CacheLogInit() { - PRInt64 t64; - LL_I2L(t64, seconds); - PRInt64 mil; - LL_I2L(mil, 1000000); - LL_MUL(t64, t64, mil); - return t64; + if (gCacheLog) return; + gCacheLog = PR_NewLogModule("cache"); + NS_ASSERTION(gCacheLog, "\n### failed to allocate cache log.\n"); } #endif diff --git a/netwerk/cache/src/nsCache.h b/netwerk/cache/src/nsCache.h index b532829c2cd4..da0596031a92 100644 --- a/netwerk/cache/src/nsCache.h +++ b/netwerk/cache/src/nsCache.h @@ -33,13 +33,23 @@ #include "nsAReadableString.h" #include "prtime.h" #include "nsError.h" +#include "prlog.h" -#if 0 - // Convert PRTime to unix-style time_t, i.e. seconds since the epoch -PRUint32 ConvertPRTimeToSeconds(PRTime time64); - - // Convert unix-style time_t, i.e. seconds since the epoch, to PRTime -PRTime ConvertSecondsToPRTime(PRUint32 seconds); +// PR_LOG args = "format string", arg, arg, ... +#if defined(PR_LOGGING) +extern PRLogModuleInfo * gCacheLog; +void CacheLogInit(); +#define CACHE_LOG_INIT() CacheLogInit() +#define CACHE_LOG_ALWAYS(args) PR_LOG(gCacheLog, PR_LOG_ALWAYS, args) +#define CACHE_LOG_ERROR(args) PR_LOG(gCacheLog, PR_LOG_ERROR, args) +#define CACHE_LOG_WARNING(args) PR_LOG(gCacheLog, PR_LOG_WARNING, args) +#define CACHE_LOG_DEBUG(args) PR_LOG(gCacheLog, PR_LOG_DEBUG, args) +#else +#define CACHE_LOG_INIT() {} +#define CACHE_LOG_ALWAYS(args) {} +#define CACHE_LOG_ERROR(args) {} +#define CACHE_LOG_WARNING(args) {} +#define CACHE_LOG_DEBUG(args) {} #endif diff --git a/netwerk/cache/src/nsCacheEntry.cpp b/netwerk/cache/src/nsCacheEntry.cpp index 18a515caa05b..388f246c7978 100644 --- a/netwerk/cache/src/nsCacheEntry.cpp +++ b/netwerk/cache/src/nsCacheEntry.cpp @@ -115,6 +115,18 @@ nsCacheEntry::SetMetaDataElement( const nsAReadableCString& key, } +nsresult +nsCacheEntry::VisitMetaDataElements( nsICacheMetaDataVisitor * visitor) +{ + NS_ENSURE_ARG_POINTER(visitor); + + if (mMetaData) + mMetaData->VisitElements(visitor); + + return NS_OK; +} + + nsresult nsCacheEntry::FlattenMetaData(char ** data, PRUint32 * size) { @@ -535,7 +547,6 @@ nsCacheEntryHashTable::MoveEntry(PLDHashTable * /* table */, const PLDHashEntryHdr *from, PLDHashEntryHdr *to) { - to->keyHash = from->keyHash; ((nsCacheEntryHashTableEntry *)to)->cacheEntry = ((nsCacheEntryHashTableEntry *)from)->cacheEntry; } @@ -545,7 +556,6 @@ void PR_CALLBACK nsCacheEntryHashTable::ClearEntry(PLDHashTable * /* table */, PLDHashEntryHdr * hashEntry) { - ((nsCacheEntryHashTableEntry *)hashEntry)->keyHash = 0; ((nsCacheEntryHashTableEntry *)hashEntry)->cacheEntry = 0; } diff --git a/netwerk/cache/src/nsCacheEntry.h b/netwerk/cache/src/nsCacheEntry.h index 831538ffc59c..f3545fd67172 100644 --- a/netwerk/cache/src/nsCacheEntry.h +++ b/netwerk/cache/src/nsCacheEntry.h @@ -95,8 +95,10 @@ public: nsresult SetMetaDataElement( const nsAReadableCString& key, const nsAReadableCString& value); - nsresult FlattenMetaData(char ** data, PRUint32 * size); - nsresult UnflattenMetaData(char * data, PRUint32 size); + nsresult VisitMetaDataElements( nsICacheMetaDataVisitor * visitor); + + nsresult FlattenMetaData( char ** data, PRUint32 * size); + nsresult UnflattenMetaData( char * data, PRUint32 size); PRUint32 MetaDataSize() { return mMetaSize;} @@ -122,8 +124,13 @@ public: eStreamDataMask = 0x00001000, eActiveMask = 0x00002000, eInitializedMask = 0x00004000, - eValidMask = 0x00008000 + eValidMask = 0x00008000, + eBindingMask = 0x00010000 }; + + void MarkBinding() { mFlags |= eBindingMask; } + void ClearBinding() { mFlags &= ~eBindingMask; } + PRBool IsBinding() { return (mFlags & eBindingMask) != 0; } void MarkEntryDirty() { mFlags |= eEntryDirtyMask; } void MarkEntryClean() { mFlags &= ~eEntryDirtyMask; } diff --git a/netwerk/cache/src/nsCacheEntryDescriptor.cpp b/netwerk/cache/src/nsCacheEntryDescriptor.cpp index 74d15e919fd5..5164ae06a31b 100644 --- a/netwerk/cache/src/nsCacheEntryDescriptor.cpp +++ b/netwerk/cache/src/nsCacheEntryDescriptor.cpp @@ -395,12 +395,12 @@ nsCacheEntryDescriptor::SetMetaDataElement(const char *key, const char *value) NS_IMETHODIMP -nsCacheEntryDescriptor::GetMetaDataEnumerator(nsISimpleEnumerator ** result) +nsCacheEntryDescriptor::VisitMetaData(nsICacheMetaDataVisitor * visitor) { - NS_ENSURE_ARG_POINTER(result); + NS_ENSURE_ARG_POINTER(visitor); if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; - return NS_ERROR_NOT_IMPLEMENTED; + return mCacheEntry->VisitMetaDataElements(visitor); } diff --git a/netwerk/cache/src/nsCacheMetaData.cpp b/netwerk/cache/src/nsCacheMetaData.cpp index fd5e3796a4f6..7fa01a306bc8 100644 --- a/netwerk/cache/src/nsCacheMetaData.cpp +++ b/netwerk/cache/src/nsCacheMetaData.cpp @@ -23,6 +23,7 @@ #include "nsCacheMetaData.h" #include "nsString.h" +#include "nsICacheEntryDescriptor.h" /* @@ -167,7 +168,7 @@ nsCacheMetaData::FlattenMetaData(char ** data, PRUint32 * size) *data = new char[*size]; if (*data == nsnull) return NS_ERROR_OUT_OF_MEMORY; char* state = *data; - PL_DHashTableEnumerate(&table, AccumulateElements, &state); + PL_DHashTableEnumerate(&table, AccumulateElement, &state); } return NS_OK; @@ -194,6 +195,15 @@ nsCacheMetaData::UnflattenMetaData(char * data, PRUint32 size) return rv; } + +nsresult +nsCacheMetaData::VisitElements(nsICacheMetaDataVisitor * visitor) +{ + (void) PL_DHashTableEnumerate(&table, VisitElement, visitor); + return NS_OK; +} + + /* * hash table operation callback functions */ @@ -231,7 +241,6 @@ nsCacheMetaData::MoveEntry(PLDHashTable * /* table */, const PLDHashEntryHdr *from, PLDHashEntryHdr *to) { - to->keyHash = from->keyHash; ((nsCacheMetaDataHashTableEntry *)to)->key = ((nsCacheMetaDataHashTableEntry *)from)->key; ((nsCacheMetaDataHashTableEntry *)to)->value = @@ -243,7 +252,6 @@ void PR_CALLBACK nsCacheMetaData::ClearEntry(PLDHashTable * /* table */, PLDHashEntryHdr * hashEntry) { - ((nsCacheMetaDataHashTableEntry *)hashEntry)->keyHash = 0; ((nsCacheMetaDataHashTableEntry *)hashEntry)->key = 0; ((nsCacheMetaDataHashTableEntry *)hashEntry)->value = 0; } @@ -252,7 +260,7 @@ nsCacheMetaData::ClearEntry(PLDHashTable * /* table */, void PR_CALLBACK nsCacheMetaData::Finalize(PLDHashTable * table) { - (void) PL_DHashTableEnumerate(table, FreeElements, nsnull); + (void) PL_DHashTableEnumerate(table, FreeElement, nsnull); } @@ -272,7 +280,7 @@ nsCacheMetaData::CalculateSize(PLDHashTable *table, } PLDHashOperator PR_CALLBACK -nsCacheMetaData::AccumulateElements(PLDHashTable *table, +nsCacheMetaData::AccumulateElement(PLDHashTable *table, PLDHashEntryHdr *hdr, PRUint32 number, void *arg) @@ -289,7 +297,7 @@ nsCacheMetaData::AccumulateElements(PLDHashTable *table, } PLDHashOperator PR_CALLBACK -nsCacheMetaData::FreeElements(PLDHashTable *table, +nsCacheMetaData::FreeElement(PLDHashTable *table, PLDHashEntryHdr *hdr, PRUint32 number, void *arg) @@ -299,3 +307,21 @@ nsCacheMetaData::FreeElements(PLDHashTable *table, delete entry->value; return PL_DHASH_NEXT; } + + +PLDHashOperator PR_CALLBACK +nsCacheMetaData::VisitElement(PLDHashTable *table, + PLDHashEntryHdr *hdr, + PRUint32 number, + void *arg) +{ + nsCacheMetaDataHashTableEntry *entry = (nsCacheMetaDataHashTableEntry *)hdr; + nsICacheMetaDataVisitor *visitor = (nsICacheMetaDataVisitor *)arg; + const char * key = entry->key ? entry->key->get() : nsnull; + const char * value = entry->value ? entry->value->get() : nsnull; + + PRBool keepGoing; + nsresult rv = visitor->VisitMetaDataElement(key, value, &keepGoing); + + return NS_SUCCEEDED(rv) && keepGoing ? PL_DHASH_NEXT : PL_DHASH_STOP; +} diff --git a/netwerk/cache/src/nsCacheMetaData.h b/netwerk/cache/src/nsCacheMetaData.h index 26632aa4fa9a..2385b13b7f39 100644 --- a/netwerk/cache/src/nsCacheMetaData.h +++ b/netwerk/cache/src/nsCacheMetaData.h @@ -31,6 +31,8 @@ #include "nsString.h" // #include "nsAReadableString.h" +class nsICacheMetaDataVisitor; + typedef struct { nsCString * key; nsCString * value; @@ -65,6 +67,8 @@ public: nsresult UnflattenMetaData(char * data, PRUint32 size); + nsresult VisitElements(nsICacheMetaDataVisitor * visitor); + private: // PLDHashTable operation callbacks static const void * PR_CALLBACK GetKey( PLDHashTable *table, PLDHashEntryHdr *entry); @@ -90,16 +94,21 @@ private: void *arg); static - PLDHashOperator PR_CALLBACK AccumulateElements(PLDHashTable *table, + PLDHashOperator PR_CALLBACK AccumulateElement(PLDHashTable *table, PLDHashEntryHdr *hdr, PRUint32 number, void *arg); static - PLDHashOperator PR_CALLBACK FreeElements(PLDHashTable *table, + PLDHashOperator PR_CALLBACK FreeElement(PLDHashTable *table, PLDHashEntryHdr *hdr, PRUint32 number, void *arg); + static + PLDHashOperator PR_CALLBACK VisitElement(PLDHashTable *table, + PLDHashEntryHdr *hdr, + PRUint32 number, + void *arg); // member variables static PLDHashTableOps ops; diff --git a/netwerk/cache/src/nsCacheService.cpp b/netwerk/cache/src/nsCacheService.cpp index a1ed7c02ae07..820053deb0f2 100644 --- a/netwerk/cache/src/nsCacheService.cpp +++ b/netwerk/cache/src/nsCacheService.cpp @@ -201,6 +201,8 @@ nsCacheService::Init() if (mCacheServiceLock) return NS_ERROR_ALREADY_INITIALIZED; + CACHE_LOG_INIT(); + mCacheServiceLock = PR_NewLock(); if (mCacheServiceLock == nsnull) return NS_ERROR_OUT_OF_MEMORY; @@ -275,6 +277,9 @@ nsCacheService::Shutdown() printf("### beging nsCacheService::Shutdown()\n"); #endif +#if defined(PR_LOGGING) + LogCacheStatistics(); +#endif // Clear entries ClearDoomList(); ClearActiveEntries(); @@ -369,19 +374,31 @@ NS_IMETHODIMP nsCacheService::VisitEntries(nsICacheVisitor *visitor) NS_IMETHODIMP nsCacheService::EvictEntries(nsCacheStoragePolicy storagePolicy) { - nsresult rv = NS_ERROR_NOT_IMPLEMENTED; + nsresult rv; + + nsAutoLock lock(mCacheServiceLock); - if (storagePolicy == nsICache::STORE_ON_DISK) { + // XXX what should we do about error handling? + + if (storagePolicy == nsICache::STORE_ANYWHERE || storagePolicy == nsICache::STORE_ON_DISK) { if (mEnableDiskDevice) { if (!mDiskDevice) { rv = CreateDiskDevice(); if (NS_FAILED(rv)) return rv; } rv = mDiskDevice->EvictEntries(nsnull); + if (NS_FAILED(rv)) return rv; + } + } + + if (storagePolicy == nsICache::STORE_ANYWHERE || storagePolicy == nsICache::STORE_IN_MEMORY) { + if (mEnableMemoryDevice) { + rv = mMemoryDevice->EvictEntries(nsnull); + if (NS_FAILED(rv)) return rv; } } - return rv; + return NS_OK; } @@ -715,7 +732,9 @@ nsCacheService::EnsureEntryHasDevice(nsCacheEntry * entry) if (device == nsnull) return nsnull; + entry->MarkBinding(); // XXX nsresult rv = device->BindEntry(entry); + entry->ClearBinding(); // XXX if (NS_FAILED(rv)) return nsnull; entry->SetCacheDevice(device); @@ -755,10 +774,11 @@ nsCacheService::DoomEntry_Locked(nsCacheEntry * entry) { if (this == nsnull) return NS_ERROR_NOT_AVAILABLE; if (entry->IsDoomed()) return NS_OK; - + nsresult rv = NS_OK; entry->MarkDoomed(); - + + NS_ASSERTION(!entry->IsBinding(), "Dooming entry while binding device."); nsCacheDevice * device = entry->CacheDevice(); if (device) device->DoomEntry(entry); @@ -844,9 +864,7 @@ nsCacheService::CloseDescriptor(nsCacheEntryDescriptor * descriptor) nsresult rv = NS_OK; if (!entry->IsValid()) { - if (PR_CLIST_IS_EMPTY(&entry->mRequestQ)) { - rv = ProcessPendingRequests(entry); - } + rv = ProcessPendingRequests(entry); } if (!stillActive) { @@ -1035,3 +1053,22 @@ NS_IMETHODIMP nsCacheService::Observe(nsISupports *aSubject, const PRUnichar *aT return Shutdown(); } +#if defined(PR_LOGGING) +void +nsCacheService::LogCacheStatistics() +{ + PRUint32 hitPercentage = (PRUint32)((((double)mCacheHits) / + ((double)(mCacheHits + mCacheMisses))) * 100); + CACHE_LOG_ALWAYS(("\nCache Service Statistics:\n\n")); + CACHE_LOG_ALWAYS((" TotalEntries = %d\n", mTotalEntries)); + CACHE_LOG_ALWAYS((" Cache Hits = %d\n", mCacheHits)); + CACHE_LOG_ALWAYS((" Cache Misses = %d\n", mCacheMisses)); + CACHE_LOG_ALWAYS((" Cache Hit %% = %d%%\n", hitPercentage)); + CACHE_LOG_ALWAYS((" Max Key Length = %d\n", mMaxKeyLength)); + CACHE_LOG_ALWAYS((" Max Meta Size = %d\n", mMaxMetaSize)); + CACHE_LOG_ALWAYS((" Max Data Size = %d\n", mMaxDataSize)); + CACHE_LOG_ALWAYS(("\n")); + CACHE_LOG_ALWAYS((" Deactivate Failures = %d\n", mDeactivateFailures)); + CACHE_LOG_ALWAYS((" Deactivated Unbound Entries = %d\n", mDeactivatedUnboundEntries)); +} +#endif diff --git a/netwerk/cache/src/nsCacheService.h b/netwerk/cache/src/nsCacheService.h index 98d736632b2f..f87411edf71f 100644 --- a/netwerk/cache/src/nsCacheService.h +++ b/netwerk/cache/src/nsCacheService.h @@ -141,7 +141,9 @@ private: PLDHashEntryHdr * hdr, PRUint32 number, void * arg); - +#if defined(PR_LOGGING) + void LogCacheStatistics(); +#endif /** * Data Members */ @@ -165,6 +167,7 @@ private: PRCList mDoomedEntries; // stats + PRUint32 mTotalEntries; PRUint32 mCacheHits; PRUint32 mCacheMisses; diff --git a/netwerk/cache/src/nsDiskCache.h b/netwerk/cache/src/nsDiskCache.h index 0b4f7a0c2f05..0bc8fc672aa1 100644 --- a/netwerk/cache/src/nsDiskCache.h +++ b/netwerk/cache/src/nsDiskCache.h @@ -48,7 +48,7 @@ public: { } - PRUint32 HashNumber() + PRUint32 HashNumber() const { return mHashNumber; } @@ -58,7 +58,7 @@ public: mHashNumber = hashNumber; } - PRUint32 EvictionRank() + PRUint32 EvictionRank() const { return mEvictionRank; } @@ -68,7 +68,7 @@ public: mEvictionRank = rank; } - PRUint32 LocationSelector() + PRUint32 LocationSelector() const { return (PRUint32)(mLocation & eLocationSelectorMask) >> 22; } @@ -79,7 +79,7 @@ public: mLocation |= (selector & eLocationSelectorMask) << 22; } - PRUint32 BlockCount() + PRUint32 BlockCount() const { return (PRUint32)((mLocation & eExtraBlocksMask) >> 20) + 1; } @@ -92,7 +92,7 @@ public: mLocation |= (count & eExtraBlocksMask) << 20; } - PRUint32 BlockNumber() + PRUint32 BlockNumber() const { return (mLocation & eBlockNumberMask); } @@ -103,7 +103,7 @@ public: mLocation |= blockNumber & eBlockNumberMask; } - PRUint16 FileGeneration() + PRUint16 FileGeneration() const { return (mLocation & eFileGenerationMask); } diff --git a/netwerk/cache/src/nsDiskCacheDevice.cpp b/netwerk/cache/src/nsDiskCacheDevice.cpp index f867f5667bd8..f05aa50cb76d 100644 --- a/netwerk/cache/src/nsDiskCacheDevice.cpp +++ b/netwerk/cache/src/nsDiskCacheDevice.cpp @@ -43,6 +43,8 @@ #include "nsIObserverService.h" #include "nsIPref.h" +#include "nsQuickSort.h" + static nsresult ensureCacheDirectory(nsIFile * cacheDirectory); static const char DISK_CACHE_DEVICE_ID[] = { "disk" }; @@ -99,7 +101,7 @@ NS_IMETHODIMP nsDiskCacheObserver::Observe(nsISupports *aSubject, const PRUnicha PRInt32 cacheCapacity; rv = prefs->GetIntPref(CACHE_DISK_CAPACITY_PREF, &cacheCapacity); if (NS_SUCCEEDED(rv)) - mDevice->setCacheCapacity(cacheCapacity); + mDevice->setCacheCapacity(cacheCapacity * 1024); } } else if (NS_LITERAL_STRING("profile-do-change").Equals(aTopic)) { // XXX need to regenerate the cache directory. hopefully the @@ -595,14 +597,6 @@ private: nsresult* mErrorPtr; }; -#if 0 -inline const nsQueryElementAt -do_QueryElementAt( nsICollection* aCollection, PRUint32 aIndex, nsresult* aErrorPtr = 0 ) -{ - return nsQueryElementAt(aCollection, aIndex, aErrorPtr); -} -#endif - // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX static nsCOMPtr gFileTransportService; @@ -731,18 +725,16 @@ nsDiskCacheDevice::FindEntry(nsCString * key) nsresult nsDiskCacheDevice::DeactivateEntry(nsCacheEntry * entry) { - if (!entry->IsDoomed()) { - nsDiskCacheEntry * diskEntry = mBoundEntries.GetEntry(entry->Key()->get()); - NS_ASSERTION(diskEntry, "DeactivateEntry called for an entry we don't have!"); - if (!diskEntry) - return NS_ERROR_INVALID_POINTER; - - // commit any changes about this entry to disk. - updateDiskCacheEntry(diskEntry); - + nsDiskCacheEntry* diskEntry = ensureDiskCacheEntry(entry); + if (mBoundEntries.GetEntry(diskEntry->getHashNumber()) == diskEntry) { // XXX eventually, as a performance enhancement, keep entries around for a while before deleting them. // XXX right now, to prove correctness, destroy the entries eagerly. mBoundEntries.RemoveEntry(diskEntry); + } + + if (!entry->IsDoomed()) { + // commit any changes about this entry to disk. + updateDiskCacheEntry(diskEntry); // XXX if this entry collided with other concurrently bound entries, then its // generation count will be non-zero. The other entries that came before it @@ -755,10 +747,8 @@ nsDiskCacheDevice::DeactivateEntry(nsCacheEntry * entry) delete entry; } else { // obliterate all knowledge of this entry on disk. - nsDiskCacheEntry* diskEntry = ensureDiskCacheEntry(entry); - NS_ASSERTION(diskEntry, "nsDiskCacheDevice::DeactivateEntry"); deleteDiskCacheEntry(diskEntry); - + // XXX if this entry resides on a list, then there must have been a collision // during the entry's lifetime. use this deactivation as a trigger to scavenge // generation numbers, and reset the live entry's generation to zero. @@ -786,21 +776,32 @@ nsDiskCacheDevice::BindEntry(nsCacheEntry * newEntry) // XXX check for cache collision. if an entry exists on disk that has the same // hash code as this newly bound entry, AND there is already a bound entry for - // that key, we need to ask the Cache service to doom that entry, since two + // that key, we need to ask the cache service to doom that entry, since two // simultaneous entries that have the same hash code aren't allowed until // some sort of chaining mechanism is implemented. nsDiskCacheEntry* oldDiskEntry = mBoundEntries.GetEntry(newEntry->Key()->get()); if (oldDiskEntry) { - // set the generation count on the newly bound entry, - // so that files created will be unique and won't conflict - // with the doomed entries that are still active. - if (oldDiskEntry) { + // XXX Hacky liveness test, remove when we've figured this all out. + if (oldDiskEntry->getRefCount() > 1) { + // set the generation count on the newly bound entry, + // so that files created will be unique and won't conflict + // with the doomed entries that are still active. newDiskEntry->setGeneration(oldDiskEntry->getGeneration() + 1); PR_APPEND_LINK(newDiskEntry, oldDiskEntry); + + // XXX Whom do we tell about this impending doom? + nsCacheEntry* oldEntry = oldDiskEntry->getCacheEntry(); + // XXX Yes Virginia, a doomed entry can be bound. + // NS_ASSERTION(!oldEntry->IsDoomed(), "a bound entry is doomed!"); + if (!oldEntry->IsDoomed()) + nsCacheService::GlobalInstance()->DoomEntry_Locked(oldEntry); + else + mBoundEntries.RemoveEntry(oldDiskEntry); + } else { + // XXX somehow we didn't hear about the entry going away. Ask gordon. + NS_NOTREACHED("bound disk cache entry with no corresponding cache entry."); + mBoundEntries.RemoveEntry(oldDiskEntry); } - - // XXX Whom do we tell about this impending doom? - nsCacheService::GlobalInstance()->DoomEntry_Locked(oldDiskEntry->getCacheEntry()); } rv = mBoundEntries.AddEntry(newDiskEntry); @@ -884,12 +885,10 @@ nsDiskCacheDevice::OnDataSizeChange(nsCacheEntry * entry, PRInt32 deltaSize) { PRUint32 newCacheSize = (mCacheMap->DataSize() += deltaSize); -#if 0 if (newCacheSize > mCacheCapacity) { // XXX go toss out some disk cache entries. evictDiskCacheEntries(); } -#endif return NS_OK; } @@ -910,30 +909,29 @@ nsDiskCacheDevice::Visit(nsICacheVisitor * visitor) return NS_OK; } + nsresult nsDiskCacheDevice::EvictEntries(const char * clientID) { - nsCOMPtr entries; - nsresult rv = scanDiskCacheEntries(getter_AddRefs(entries)); - if (NS_FAILED(rv)) return rv; + nsresult rv; PRUint32 prefixLength = (clientID ? nsCRT::strlen(clientID) : 0); PRUint32 newDataSize = mCacheMap->DataSize(); + PRUint32 newEntryCount = mCacheMap->EntryCount(); + + // XXX make sure meta data is up to date. + rv = updateDiskCacheEntries(); + if (NS_FAILED(rv)) return rv; - PRUint32 count; - entries->Count(&count); - for (PRUint32 i = 0; i < count; ++i) { - nsCOMPtr info = do_QueryElementAt(entries, i, &rv); - if (NS_SUCCEEDED(rv)) { - nsDiskCacheEntryInfo* entryInfo = (nsDiskCacheEntryInfo*) info.get(); - const char* key = entryInfo->Key(); - - // if filtering by clientID, make sure key prefix and clientID match. - if (clientID && nsCRT::strncmp(clientID, key, prefixLength) != 0) - continue; - + for (PRUint32 i = 1; i < nsDiskCacheMap::kBucketsPerTable; ++i) { + nsDiskCacheRecord* bucket = mCacheMap->GetBucket(i); + for (PRUint32 j = 0; j < nsDiskCacheMap::kRecordsPerBucket; ++j) { + nsDiskCacheRecord* record = bucket++; + if (record->HashNumber() == 0) + break; + // if the entry is currently in use, then doom it rather than evicting right here. - nsDiskCacheEntry* diskEntry = mBoundEntries.GetEntry(key); + nsDiskCacheEntry* diskEntry = mBoundEntries.GetEntry(record->HashNumber()); if (diskEntry) { nsCacheService::GlobalInstance()->DoomEntry_Locked(diskEntry->getCacheEntry()); continue; @@ -941,30 +939,62 @@ nsDiskCacheDevice::EvictEntries(const char * clientID) // delete the metadata file. nsCOMPtr metaFile; - rv = getFileForKey(key, PR_TRUE, 0, getter_AddRefs(metaFile)); + rv = getFileForHashNumber(record->HashNumber(), PR_TRUE, 0, getter_AddRefs(metaFile)); if (NS_SUCCEEDED(rv)) { + if (clientID) { + // if filtering by clientID, make sure key prefix and clientID match. + // if anything fails, assume they don't by continuing with the loop. + nsCOMPtr input; + rv = openInputStream(metaFile, getter_AddRefs(input)); + if (NS_FAILED(rv)) continue; + + // read the metadata file. + MetaDataFile metaDataFile; + rv = metaDataFile.Read(input); + input->Close(); + if (NS_FAILED(rv)) continue; + + if (nsCRT::strncmp(clientID, metaDataFile.mKey, prefixLength) != 0) + continue; + } rv = metaFile->Delete(PR_FALSE); } - + + PRUint32 dataSize = 0; + // delete the data file nsCOMPtr dataFile; - rv = getFileForKey(key, PR_FALSE, 0, getter_AddRefs(dataFile)); + rv = getFileForHashNumber(record->HashNumber(), PR_FALSE, 0, getter_AddRefs(dataFile)); if (NS_SUCCEEDED(rv)) { + PRInt64 fileSize; + rv = dataFile->GetFileSize(&fileSize); + if (NS_SUCCEEDED(rv)) + LL_L2I(dataSize, fileSize); rv = dataFile->Delete(PR_FALSE); } - // update the cache size. - PRUint32 dataSize; - info->GetDataSize(&dataSize); - newDataSize -= dataSize; + // remove from cache map. + nsDiskCacheRecord* deletedRecord = mCacheMap->GetRecord(record->HashNumber()); + if (record->HashNumber() == deletedRecord->HashNumber() && record->FileGeneration() == deletedRecord->FileGeneration()) + mCacheMap->DeleteRecord(deletedRecord); + + // update the cache size/entry count. + if (newDataSize >= dataSize) newDataSize -= dataSize; + if (newEntryCount) --newEntryCount; } } - mCacheMap->DataSize() = newDataSize; + if (clientID) { + mCacheMap->DataSize() = newDataSize; + mCacheMap->EntryCount() = newEntryCount; + } else { + mCacheMap->DataSize() = 0; + mCacheMap->EntryCount() = 0; + } + return NS_OK; } - void nsDiskCacheDevice::setPrefsObserver(nsIObserver* observer) { mPrefsObserver = observer; @@ -982,8 +1012,13 @@ void nsDiskCacheDevice::setCacheDirectory(nsILocalFile* cacheDirectory) void nsDiskCacheDevice::setCacheCapacity(PRUint32 capacity) { - // XXX start evicting entries if the new size is smaller! mCacheCapacity = capacity; + if (mInitialized) { + // XXX start evicting entries if the new size is smaller! + // XXX need to enter cache service lock here! + if (mCacheMap->DataSize() > capacity) + evictDiskCacheEntries(); + } } PRUint32 nsDiskCacheDevice::getCacheCapacity() @@ -1101,33 +1136,36 @@ inline PRBool isMetaDataFile(const char* name) return (name[8] == 'm'); } -// XXX All these transports and opening/closing of files. We need a way to cache open files, -// XXX and to seek. Perhaps I should just be using ANSI FILE objects for all of the metadata -// XXX operations. - nsresult nsDiskCacheDevice::visitEntries(nsICacheVisitor * visitor) { - nsCOMPtr entries; - nsresult rv = mCacheDirectory->GetDirectoryEntries(getter_AddRefs(entries)); - if (NS_FAILED(rv)) return rv; + nsresult rv; + // XXX make sure meta data is up to date. + rv = updateDiskCacheEntries(); + if (NS_FAILED(rv)) return rv; + nsDiskCacheEntryInfo* entryInfo = new nsDiskCacheEntryInfo(); if (!entryInfo) return NS_ERROR_OUT_OF_MEMORY; nsCOMPtr ref(entryInfo); - for (PRBool more; NS_SUCCEEDED(entries->HasMoreElements(&more)) && more;) { - nsCOMPtr next; - rv = entries->GetNext(getter_AddRefs(next)); - if (NS_FAILED(rv)) break; - nsCOMPtr file(do_QueryInterface(next, &rv)); - if (NS_FAILED(rv)) break; - nsXPIDLCString name; - rv = file->GetLeafName(getter_Copies(name)); - if (isMetaDataFile(name)) { - // this must be a metadata file. + for (PRUint32 i = 1; i < nsDiskCacheMap::kBucketsPerTable; ++i) { + nsDiskCacheRecord* bucket = mCacheMap->GetBucket(i); + for (PRUint32 j = 0; j < nsDiskCacheMap::kRecordsPerBucket; ++j) { + nsDiskCacheRecord* record = bucket++; + if (record->HashNumber() == 0) + break; + nsCOMPtr file; + rv = getFileForHashNumber(record->HashNumber(), PR_TRUE, record->FileGeneration(), getter_AddRefs(file)); + if (NS_FAILED(rv)) continue; + nsCOMPtr input; rv = openInputStream(file, getter_AddRefs(input)); - if (NS_FAILED(rv)) continue; + if (NS_FAILED(rv)) { + // delete non-existent record. + mCacheMap->DeleteRecord(record); + --bucket; + continue; + } // read the metadata file. rv = entryInfo->Read(input); @@ -1225,48 +1263,44 @@ nsresult nsDiskCacheDevice::readDiskCacheEntry(const char * key, nsDiskCacheEntr nsCOMPtr file; nsresult rv = getFileForKey(key, PR_TRUE, 0, getter_AddRefs(file)); if (NS_FAILED(rv)) return rv; - PRBool exists; - rv = file->Exists(&exists); - if (NS_FAILED(rv) || !exists) return NS_ERROR_NOT_AVAILABLE; - nsCacheEntry* entry = nsnull; - do { - nsCOMPtr input; - rv = openInputStream(file, getter_AddRefs(input)); - if (NS_FAILED(rv)) break; - - // read the metadata file. - MetaDataFile metaDataFile; - rv = metaDataFile.Read(input); - input->Close(); - if (NS_FAILED(rv)) break; - - // Ensure that the keys match. - if (nsCRT::strcmp(key, metaDataFile.mKey) != 0) break; - - rv = NS_NewCacheEntry(&entry, key, PR_TRUE, nsICache::STORE_ON_DISK, this); - if (NS_FAILED(rv)) return rv; + nsCOMPtr input; + rv = openInputStream(file, getter_AddRefs(input)); + if (NS_FAILED(rv)) return rv; - // initialize the entry. - entry->SetFetchCount(metaDataFile.mFetchCount); - entry->SetLastFetched(metaDataFile.mLastFetched); - entry->SetLastModified(metaDataFile.mLastModified); - entry->SetExpirationTime(metaDataFile.mExpirationTime); - entry->SetDataSize(metaDataFile.mDataSize); + // read the metadata file. + MetaDataFile metaDataFile; + rv = metaDataFile.Read(input); + input->Close(); + if (NS_FAILED(rv)) return rv; - // restore the metadata. - if (metaDataFile.mMetaDataSize) { - rv = entry->UnflattenMetaData(metaDataFile.mMetaData, metaDataFile.mMetaDataSize); - if (NS_FAILED(rv)) break; - } - - // celebrate! - *result = ensureDiskCacheEntry(entry); - if (!*result) break; - return NS_OK; - } while (0); + // Ensure that the keys match. + if (nsCRT::strcmp(key, metaDataFile.mKey) != 0) return NS_ERROR_NOT_AVAILABLE; + + nsCacheEntry* entry = nsnull; + rv = NS_NewCacheEntry(&entry, key, PR_TRUE, nsICache::STORE_ON_DISK, this); + if (NS_FAILED(rv)) return rv; - // oh, auto_ptr<> would be nice right about now. + // initialize the entry. + entry->SetFetchCount(metaDataFile.mFetchCount); + entry->SetLastFetched(metaDataFile.mLastFetched); + entry->SetLastModified(metaDataFile.mLastModified); + entry->SetExpirationTime(metaDataFile.mExpirationTime); + entry->SetDataSize(metaDataFile.mDataSize); + + // restore the metadata. + if (metaDataFile.mMetaDataSize) { + rv = entry->UnflattenMetaData(metaDataFile.mMetaData, metaDataFile.mMetaDataSize); + if (NS_FAILED(rv)) goto error; + } + + // celebrate! + *result = ensureDiskCacheEntry(entry); + if (!*result) goto error; + return NS_OK; + +error: + // oh, auto_ptr<> would be nice right about now. delete entry; return NS_ERROR_NOT_AVAILABLE; } @@ -1294,7 +1328,9 @@ nsresult nsDiskCacheDevice::deleteDiskCacheEntry(nsDiskCacheEntry * diskEntry) } // remove from cache map. - mCacheMap->DeleteRecord(diskEntry->getHashNumber()); + nsDiskCacheRecord* record = mCacheMap->GetRecord(diskEntry->getHashNumber()); + if (record->HashNumber() == diskEntry->getHashNumber() && record->FileGeneration() == diskEntry->getGeneration()) + mCacheMap->DeleteRecord(record); return NS_OK; } @@ -1445,53 +1481,86 @@ nsresult nsDiskCacheDevice::scanDiskCacheEntries(nsISupportsArray ** result) return NS_OK; } +static int compareRecords(const void* e1, const void* e2, void* /*unused*/) +{ + const nsDiskCacheRecord* r1 = (const nsDiskCacheRecord*) e1; + const nsDiskCacheRecord* r2 = (const nsDiskCacheRecord*) e2; + return (r1->EvictionRank() - r2->EvictionRank()); +} + nsresult nsDiskCacheDevice::evictDiskCacheEntries() { - nsCOMPtr entries; - nsresult rv = scanDiskCacheEntries(getter_AddRefs(entries)); - if (NS_FAILED(rv)) return rv; + nsresult rv; if (mCacheMap->DataSize() < mCacheCapacity) return NS_OK; - - // these are sorted in oldest to newest order. - PRUint32 count; - entries->Count(&count); - for (PRUint32 i = 0; i < count; ++i) { - nsCOMPtr info = do_QueryElementAt(entries, i, &rv); - if (NS_SUCCEEDED(rv)) { - nsDiskCacheEntryInfo* entryInfo = (nsDiskCacheEntryInfo*) info.get(); - const char* key = entryInfo->Key(); - - // XXX if this entry is currently active, then leave it alone, - // as it is likely to be modified very soon. - nsDiskCacheEntry* diskEntry = mBoundEntries.GetEntry(key); - if (diskEntry) continue; - - // delete the metadata file. - nsCOMPtr metaFile; - rv = getFileForKey(key, PR_TRUE, 0, getter_AddRefs(metaFile)); - if (NS_SUCCEEDED(rv)) { - rv = metaFile->Delete(PR_FALSE); - } - - // delete the data file - nsCOMPtr dataFile; - rv = getFileForKey(key, PR_FALSE, 0, getter_AddRefs(dataFile)); - if (NS_SUCCEEDED(rv)) { - rv = dataFile->Delete(PR_FALSE); - } - // update the cache size. - PRUint32 dataSize; - info->GetDataSize(&dataSize); - if ((mCacheMap->DataSize() -= dataSize) <= mCacheCapacity) - break; + // XXX make sure meta data is up to date. + rv = updateDiskCacheEntries(); + if (NS_FAILED(rv)) return rv; + + // 1. gather all records into an array, sorted by eviction rank. keep deleting them until we recover enough space. + PRUint32 count = 0; + nsDiskCacheRecord* sortedRecords = new nsDiskCacheRecord[mCacheMap->EntryCount()]; + if (sortedRecords) { + for (PRUint32 i = 1; i < nsDiskCacheMap::kBucketsPerTable; ++i) { + nsDiskCacheRecord* bucket = mCacheMap->GetBucket(i); + for (PRUint32 j = 0; j < nsDiskCacheMap::kRecordsPerBucket; ++j) { + nsDiskCacheRecord* record = bucket++; + if (record->HashNumber() == 0) + break; + sortedRecords[count++] = *record; + } } + NS_QuickSort((void*)sortedRecords, count, sizeof(nsDiskCacheRecord), compareRecords, nsnull); + } else { + return NS_ERROR_OUT_OF_MEMORY; } + // these are sorted by eviction rank. lower eviction ranks are more eligible for eviction. + for (PRUint32 i = 0; i < count; ++i) { + nsDiskCacheRecord* record = &sortedRecords[i]; + + // XXX if this entry is currently active, then leave it alone, + // as it is likely to be modified very soon. + nsDiskCacheEntry* diskEntry = mBoundEntries.GetEntry(record->HashNumber()); + if (diskEntry) continue; + + // delete the metadata file. + nsCOMPtr metaFile; + rv = getFileForHashNumber(record->HashNumber(), PR_TRUE, 0, getter_AddRefs(metaFile)); + if (NS_SUCCEEDED(rv)) { + rv = metaFile->Delete(PR_FALSE); + } + + PRUint32 dataSize = 0; + + // delete the data file + nsCOMPtr dataFile; + rv = getFileForHashNumber(record->HashNumber(), PR_FALSE, 0, getter_AddRefs(dataFile)); + if (NS_SUCCEEDED(rv)) { + PRInt64 fileSize; + rv = dataFile->GetFileSize(&fileSize); + if (NS_SUCCEEDED(rv)) + LL_L2I(dataSize, fileSize); + rv = dataFile->Delete(PR_FALSE); + } + + // remove from cache map. + nsDiskCacheRecord* deletedRecord = mCacheMap->GetRecord(record->HashNumber()); + if (record->HashNumber() == deletedRecord->HashNumber() && record->FileGeneration() == deletedRecord->FileGeneration()) + mCacheMap->DeleteRecord(deletedRecord); + + // update the cache size. + if ((mCacheMap->DataSize() -= dataSize) <= mCacheCapacity) + break; + } + + delete[] sortedRecords; + return NS_OK; } + nsresult nsDiskCacheDevice::readCacheMap() { nsCOMPtr file; @@ -1538,6 +1607,8 @@ nsresult nsDiskCacheDevice::updateCacheMap(nsDiskCacheEntry * diskEntry) if (record->HashNumber() != 0) { // eviction of eldest entry in this bucket. evictDiskCacheRecord(record); + } else { + mCacheMap->EntryCount() += 1; } // newly bound record. fill in the blanks. record->SetHashNumber(diskEntry->getHashNumber()); diff --git a/netwerk/cache/src/nsDiskCacheEntry.h b/netwerk/cache/src/nsDiskCacheEntry.h index b9d98329430d..df53281995ab 100644 --- a/netwerk/cache/src/nsDiskCacheEntry.h +++ b/netwerk/cache/src/nsDiskCacheEntry.h @@ -62,11 +62,6 @@ public: { return mTransports[mode - 1]; } - - nsCOMPtr& getMetaTransport(nsCacheAccessMode mode) - { - return mMetaTransports[mode - 1]; - } #endif nsCacheEntry* getCacheEntry() @@ -89,12 +84,16 @@ public: return mHashNumber; } + nsrefcnt getRefCount() + { + return mRefCnt; + } + static PLDHashNumber Hash(const char* key); private: #ifdef MOZ_NEW_CACHE_REUSE_TRANSPORTS nsCOMPtr mTransports[3]; - nsCOMPtr mMetaTransports[3]; #endif nsCacheEntry* mCacheEntry; PRUint32 mGeneration; diff --git a/netwerk/cache/src/nsDiskCacheMap.cpp b/netwerk/cache/src/nsDiskCacheMap.cpp index 49b8e91686a0..a0f95d2a52bf 100644 --- a/netwerk/cache/src/nsDiskCacheMap.cpp +++ b/netwerk/cache/src/nsDiskCacheMap.cpp @@ -50,35 +50,36 @@ nsDiskCacheRecord* nsDiskCacheMap::GetRecord(PRUint32 hashNumber) return oldestRecord; } -void nsDiskCacheMap::DeleteRecord(PRUint32 hashNumber) + +void nsDiskCacheMap::DeleteRecord(nsDiskCacheRecord* deletedRecord) { + PRUint32 hashNumber = deletedRecord->HashNumber(); nsDiskCacheBucket& bucket = mBuckets[(hashNumber & (kBucketsPerTable - 1))]; - for (int r = 0; r < kRecordsPerBucket; ++r) { - nsDiskCacheRecord* record = &bucket.mRecords[r]; - if (record->HashNumber() == hashNumber) { - nsDiskCacheRecord* deletedRecord = record; - nsDiskCacheRecord* lastRecord = nsnull; - // XXX use binary search to find the end, much quicker. - // find the last record, to fill in the deleted record. - for (int j = r + 1; j < kRecordsPerBucket; ++j) { - record = &bucket.mRecords[j]; - if (record->HashNumber() == 0) { - lastRecord = record - 1; - break; - } - } - // copy the last record, to the newly deleted record. - if (lastRecord && deletedRecord != lastRecord) { - *deletedRecord = *lastRecord; - deletedRecord = lastRecord; - } - // mark record as free. - deletedRecord->SetHashNumber(0); + NS_ASSERTION(deletedRecord >= &bucket.mRecords[0] && + deletedRecord < &bucket.mRecords[kRecordsPerBucket], + "invalid record to delete."); + nsDiskCacheRecord* limit = &bucket.mRecords[kRecordsPerBucket]; + nsDiskCacheRecord* lastRecord = nsnull; + // XXX use binary search to find the end, much quicker. + // find the last record, to fill in the deleted record. + for (nsDiskCacheRecord* record = deletedRecord + 1; record < limit; ++record) { + if (record->HashNumber() == 0) { + lastRecord = record - 1; break; } } + // copy the last record, to the newly deleted record. + if (lastRecord && deletedRecord != lastRecord) { + *deletedRecord = *lastRecord; + deletedRecord = lastRecord; + } + // mark record as free. + deletedRecord->SetHashNumber(0); + // reduce the number of entries. + mHeader.mEntryCount--; } + nsresult nsDiskCacheMap::Read(nsIInputStream* input) { nsresult rv; diff --git a/netwerk/cache/src/nsDiskCacheMap.h b/netwerk/cache/src/nsDiskCacheMap.h index 60b6bdffe8ef..851da86f9aee 100644 --- a/netwerk/cache/src/nsDiskCacheMap.h +++ b/netwerk/cache/src/nsDiskCacheMap.h @@ -64,20 +64,21 @@ public: ~nsDiskCacheMap(); PRUint32& DataSize() { return mHeader.mDataSize; } - PRUint32& EntryCount() { return mHeader.mEntryCount; } nsDiskCacheRecord* GetRecord(PRUint32 hashNumber); - void DeleteRecord(PRUint32 hashNumber); - - nsresult Read(nsIInputStream* input); - nsresult Write(nsIOutputStream* output); + void DeleteRecord(nsDiskCacheRecord* record); enum { kRecordsPerBucket = 256, kBucketsPerTable = (1 << 5) // must be a power of 2! }; + nsDiskCacheRecord* GetBucket(PRUint32 index) { return mBuckets[index].mRecords; } + + nsresult Read(nsIInputStream* input); + nsresult Write(nsIOutputStream* output); + private: struct nsDiskCacheBucket { nsDiskCacheRecord mRecords[kRecordsPerBucket]; diff --git a/netwerk/cache/src/nsMemoryCacheDevice.cpp b/netwerk/cache/src/nsMemoryCacheDevice.cpp index f3a1833bd8cd..d4a7c876bfb7 100644 --- a/netwerk/cache/src/nsMemoryCacheDevice.cpp +++ b/netwerk/cache/src/nsMemoryCacheDevice.cpp @@ -18,7 +18,8 @@ * Rights Reserved. * * Contributor(s): - * Gordon Sheridan, 22-February-2001 + * Gordon Sheridan, + * Patrick C. Beard */ #include "nsMemoryCacheDevice.h" @@ -155,6 +156,10 @@ nsMemoryCacheDevice::DeactivateEntry(nsCacheEntry * entry) #if debug // XXX verify we've removed it from mMemCacheEntries & eviction list #endif + // update statistics + mTotalSize -= entry->Size(); + --mEntryCount; + delete entry; return NS_OK; } @@ -177,15 +182,17 @@ nsMemoryCacheDevice::BindEntry(nsCacheEntry * entry) { NS_ASSERTION(PR_CLIST_IS_EMPTY(entry),"entry is already on a list!"); - // append entry to the eviction list - PR_APPEND_LINK(entry, &mEvictionList); + if (!entry->IsDoomed()) { + // append entry to the eviction list + PR_APPEND_LINK(entry, &mEvictionList); - // add entry to hashtable of mem cache entries - nsresult rv = mMemCacheEntries.AddEntry(entry); - if (NS_FAILED(rv)) { - PR_REMOVE_AND_INIT_LINK(entry); - return rv; - } + // add entry to hashtable of mem cache entries + nsresult rv = mMemCacheEntries.AddEntry(entry); + if (NS_FAILED(rv)) { + PR_REMOVE_AND_INIT_LINK(entry); + return rv; + } + } // add size of entry to memory totals ++mEntryCount; @@ -206,10 +213,6 @@ nsMemoryCacheDevice::DoomEntry(nsCacheEntry * entry) // remove entry from our eviction list PR_REMOVE_AND_INIT_LINK(entry); - - // adjust our totals - mTotalSize -= entry->Size(); - mInactiveSize -= entry->Size(); } @@ -276,6 +279,25 @@ nsMemoryCacheDevice::AdjustMemoryLimits(PRUint32 softLimit, PRUint32 hardLimit } +void +nsMemoryCacheDevice::EvictEntry(nsCacheEntry * entry) +{ + // remove entry from our hashtable + mMemCacheEntries.RemoveEntry(entry); + + // remove entry from the eviction list + PR_REMOVE_AND_INIT_LINK(entry); + + // update statistics + PRUint32 memoryRecovered = entry->Size(); + mTotalSize -= memoryRecovered; + mInactiveSize -= memoryRecovered; + --mEntryCount; + + delete entry; +} + + void nsMemoryCacheDevice::EvictEntriesIfNecessary(void) { @@ -293,20 +315,8 @@ nsMemoryCacheDevice::EvictEntriesIfNecessary(void) continue; } - // remove entry from our hashtable - mMemCacheEntries.RemoveEntry(entry); - - // remove entry from the eviction list next = (nsCacheEntry *)PR_NEXT_LINK(entry); - PR_REMOVE_AND_INIT_LINK(entry); - - // update statistics - PRUint32 memoryRecovered = entry->Size(); - mTotalSize -= memoryRecovered; - mInactiveSize -= memoryRecovered; - --mEntryCount; - - delete entry; + EvictEntry(entry); entry = next; if ((mTotalSize < mHardLimit) && (mInactiveSize < mSoftLimit)) @@ -353,7 +363,26 @@ nsMemoryCacheDevice::Visit(nsICacheVisitor * visitor) nsresult nsMemoryCacheDevice::EvictEntries(const char * clientID) { - return NS_ERROR_NOT_IMPLEMENTED; + nsCacheEntry * entry; + PRUint32 prefixLength = nsCRT::strlen(clientID); + + PRCList * elem = PR_LIST_HEAD(&mEvictionList); + while (elem != &mEvictionList) { + entry = (nsCacheEntry *)elem; + elem = PR_NEXT_LINK(elem); + + const char * key = entry->Key()->get(); + if (clientID && nsCRT::strncmp(clientID, key, prefixLength) != 0) + continue; + + if (entry->IsInUse()) { + nsresult rv = nsCacheService::GlobalInstance()->DoomEntry_Locked(entry); + if (NS_FAILED(rv)) return rv; + } else { + EvictEntry(entry); + } + } + return NS_OK; } diff --git a/netwerk/cache/src/nsMemoryCacheDevice.h b/netwerk/cache/src/nsMemoryCacheDevice.h index 2aee1dd17510..6e2470575537 100644 --- a/netwerk/cache/src/nsMemoryCacheDevice.h +++ b/netwerk/cache/src/nsMemoryCacheDevice.h @@ -67,8 +67,9 @@ public: private: friend class nsMemoryCacheDeviceInfo; - void AdjustMemoryLimits(PRUint32 softLimit, PRUint32 hardLimit); - void EvictEntriesIfNecessary(void); + void AdjustMemoryLimits( PRUint32 softLimit, PRUint32 hardLimit); + void EvictEntry( nsCacheEntry * entry ); + void EvictEntriesIfNecessary(); nsCacheEntryHashTable mMemCacheEntries; PRCList mEvictionList;