for bug 103851 "move cache deletion to another thread", r=bryner, sr=darin.

This commit is contained in:
gordon%netscape.com 2003-03-10 23:19:05 +00:00
Родитель f45162d495
Коммит d8058e79cb
10 изменённых файлов: 479 добавлений и 196 удалений

17
netwerk/cache/src/nsCache.cpp поставляемый
Просмотреть файл

@ -26,6 +26,7 @@
#include "nsCache.h"
#include "nsReadableUtils.h"
#include "nsDependentSubstring.h"
#include "nsString.h"
/**
@ -35,15 +36,29 @@
#if defined(PR_LOGGING)
PRLogModuleInfo * gCacheLog = nsnull;
void
CacheLogInit()
{
if (gCacheLog) return;
gCacheLog = PR_NewLogModule("cache");
NS_ASSERTION(gCacheLog, "\n### failed to allocate cache log.\n");
NS_ASSERTION(gCacheLog, "\nfailed to allocate cache log.\n");
}
void
CacheLogPrintPath(PRLogModuleLevel level, char * format, nsIFile * item)
{
nsCAutoString path;
nsresult rv = item->GetNativePath(path);
if (NS_SUCCEEDED(rv)) {
PR_LOG(gCacheLog, level, (format, path.get()));
}
}
#endif
PRUint32
SecondsFromPRTime(PRTime prTime)
{

7
netwerk/cache/src/nsCache.h поставляемый
Просмотреть файл

@ -31,6 +31,7 @@
#define _nsCache_h_
#include "nsISupports.h"
#include "nsIFile.h"
#include "nsAString.h"
#include "prtime.h"
#include "nsError.h"
@ -40,17 +41,23 @@
#if defined(PR_LOGGING)
extern PRLogModuleInfo * gCacheLog;
void CacheLogInit();
void CacheLogPrintPath(PRLogModuleLevel level,
char * format,
nsIFile * item);
#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)
#define CACHE_LOG_PATH(level, format, item) \
CacheLogPrintPath(level, format, item)
#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) {}
#define CACHE_LOG_PATH(level, format, item) {}
#endif

57
netwerk/cache/src/nsDiskCacheBinding.cpp поставляемый
Просмотреть файл

@ -322,7 +322,7 @@ nsDiskCacheBindery::AddBinding(nsDiskCacheBinding * binding)
if (binding->mGeneration == p->mGeneration) {
if (calcGeneration) ++binding->mGeneration; // try the next generation
else {
NS_ASSERTION(binding->mGeneration != p->mGeneration, "generations collide!");
NS_ERROR("### disk cache: generations collide!");
return NS_ERROR_UNEXPECTED;
}
}
@ -332,7 +332,7 @@ nsDiskCacheBindery::AddBinding(nsDiskCacheBinding * binding)
// end of line: insert here or die
p = (nsDiskCacheBinding *)PR_PREV_LINK(p); // back up and check generation
if (p->mGeneration == 255) {
NS_ASSERTION(p->mGeneration < 255, "generation capacity at full, the engines canna take it cap'n");
NS_WARNING("### disk cache: generation capacity at full");
return NS_ERROR_UNEXPECTED;
}
PR_INSERT_BEFORE(binding, hashEntry->mBinding);
@ -355,16 +355,20 @@ nsDiskCacheBindery::RemoveBinding(nsDiskCacheBinding * binding)
HashTableEntry * hashEntry;
void * key = (void *)binding->mRecord.HashNumber();
hashEntry = (HashTableEntry*) PL_DHashTableOperate(&table, (void*) key, PL_DHASH_LOOKUP);
hashEntry = (HashTableEntry*) PL_DHashTableOperate(&table,
(void*) key,
PL_DHASH_LOOKUP);
if (!PL_DHASH_ENTRY_IS_BUSY(hashEntry)) {
NS_ASSERTION(PL_DHASH_ENTRY_IS_BUSY(hashEntry), "binding not in disk cache hashtable!");
NS_WARNING("### disk cache: binding not in hashtable!");
return;
}
if (binding == hashEntry->mBinding) {
if (PR_CLIST_IS_EMPTY(binding)) {
// remove this hash entry
(void) PL_DHashTableOperate(&table, (void*) binding->mRecord.HashNumber(), PL_DHASH_REMOVE);
(void) PL_DHashTableOperate(&table,
(void*) binding->mRecord.HashNumber(),
PL_DHASH_REMOVE);
return;
} else {
@ -374,3 +378,46 @@ nsDiskCacheBindery::RemoveBinding(nsDiskCacheBinding * binding)
}
PR_REMOVE_AND_INIT_LINK(binding);
}
/**
* ActiveBinding : PLDHashTable enumerate function to verify active bindings
*/
PLDHashOperator PR_CALLBACK
ActiveBinding(PLDHashTable * table,
PLDHashEntryHdr * hdr,
PRUint32 number,
void * arg)
{
nsDiskCacheBinding * binding = ((HashTableEntry *)hdr)->mBinding;
NS_ASSERTION(binding, "### disk cache binding = nsnull!");
nsDiskCacheBinding * head = binding;
do {
if (binding->IsActive()) {
*((PRBool *)arg) = PR_TRUE;
return PL_DHASH_STOP;
}
binding = (nsDiskCacheBinding *)PR_NEXT_LINK(binding);
} while (binding != head);
return PL_DHASH_NEXT;
}
/**
* ActiveBindings : return PR_TRUE if any bindings have open descriptors
*/
PRBool
nsDiskCacheBindery::ActiveBindings()
{
NS_ASSERTION(initialized, "nsDiskCacheBindery not initialized");
if (!initialized) return PR_FALSE;
PRBool activeBinding = PR_FALSE;
PL_DHashTableEnumerate(&table, ActiveBinding, &activeBinding);
return activeBinding;
}

3
netwerk/cache/src/nsDiskCacheBinding.h поставляемый
Просмотреть файл

@ -53,6 +53,7 @@ public:
virtual ~nsDiskCacheBinding();
nsresult EnsureStreamIO();
PRBool IsActive() { return mCacheEntry != nsnull;}
// XXX make friends
public:
@ -114,9 +115,11 @@ public:
nsDiskCacheBinding * FindBinding(nsDiskCacheRecord * record);
nsresult AddBinding(nsDiskCacheBinding * binding);
void RemoveBinding(nsDiskCacheBinding * binding);
PRBool ActiveBindings();
private:
// member variables
static PLDHashTableOps ops;
PLDHashTable table;

10
netwerk/cache/src/nsDiskCacheBlockFile.cpp поставляемый
Просмотреть файл

@ -103,11 +103,14 @@ error_exit:
* Close
*****************************************************************************/
nsresult
nsDiskCacheBlockFile::Close()
nsDiskCacheBlockFile::Close(PRBool flush)
{
if (!mFD) return NS_OK;
nsresult rv = FlushBitMap();
nsresult rv = NS_OK;
if (flush)
rv = FlushBitMap();
PRStatus err = PR_Close(mFD);
mFD = nsnull;
@ -285,6 +288,7 @@ nsDiskCacheBlockFile::WriteBlocks( void * buffer,
if (bytesWritten < bytesToWrite) return NS_ERROR_UNEXPECTED;
// write the bit map and flush the file
// XXX except we would take a severe performance hit
// XXX rv = FlushBitMap();
return rv;
}

4
netwerk/cache/src/nsDiskCacheBlockFile.h поставляемый
Просмотреть файл

@ -46,10 +46,10 @@ public:
, mBitMap(nsnull)
, mBitMapDirty(PR_FALSE)
{}
~nsDiskCacheBlockFile() { (void) Close(); }
~nsDiskCacheBlockFile() { (void) Close(PR_TRUE); }
nsresult Open( nsILocalFile * blockFile, PRUint32 blockSize);
nsresult Close();
nsresult Close(PRBool flush);
nsresult Trim();
PRInt32 AllocateBlocks( PRInt32 numBlocks);
nsresult DeallocateBlocks( PRInt32 startBlock, PRInt32 numBlocks);

416
netwerk/cache/src/nsDiskCacheDevice.cpp поставляемый
Просмотреть файл

@ -38,6 +38,9 @@
// XXX add necessary include file for ftruncate (or equivalent)
#endif
#include "prtypes.h"
#include "prthread.h"
#if defined(XP_MAC)
#include "pprio.h"
#else
@ -45,6 +48,7 @@
#endif
#include "nsDiskCacheDevice.h"
#include "nsDiskCacheEntry.h"
#include "nsDiskCacheMap.h"
@ -56,13 +60,13 @@
#include "nsCache.h"
#include "nsICacheVisitor.h"
#include "nsXPIDLString.h"
#include "nsReadableUtils.h"
#include "nsIInputStream.h"
#include "nsIOutputStream.h"
#include "nsAutoLock.h"
#include "nsCRT.h"
#include "nsCOMArray.h"
#include "nsISimpleEnumerator.h"
static const char DISK_CACHE_DEVICE_ID[] = { "disk" };
@ -134,10 +138,9 @@ nsDiskCacheEvictor::VisitRecord(nsDiskCacheRecord * mapRecord)
binding = mBindery->FindActiveBinding(mapRecord->HashNumber());
if (binding) {
// we are currently using this entry, so all we can do is doom it
// since we're enumerating the records, we don't want to call DeleteRecord
// when nsCacheService::DoomEntry() calls us back.
// We are currently using this entry, so all we can do is doom it.
// Since we're enumerating the records, we don't want to call
// DeleteRecord when nsCacheService::DoomEntry() calls us back.
binding->mDoomed = PR_TRUE; // mark binding record as 'deleted'
nsCacheService::DoomEntry(binding->mCacheEntry);
result = kDeleteRecordAndContinue; // this will REALLY delete the record
@ -318,6 +321,7 @@ nsDiskCacheDevice::nsDiskCacheDevice()
: mCacheCapacity(0)
, mCacheMap(nsnull)
, mInitialized(PR_FALSE)
, mFirstInit(PR_TRUE)
{
}
@ -342,33 +346,19 @@ nsDiskCacheDevice::Init()
rv = mBindery.Init();
if (NS_FAILED(rv)) return rv;
// XXX we should spawn another thread to do this after startup
// delete "Cache.Trash" folder
nsCOMPtr<nsIFile> cacheTrashDir;
rv = GetCacheTrashDirectory(getter_AddRefs(cacheTrashDir));
if (NS_FAILED(rv)) goto error_exit;
(void) cacheTrashDir->Remove(PR_TRUE); // ignore errors, we tried...
// Try opening cache map file.
mCacheMap = new nsDiskCacheMap;
if (!mCacheMap) {
rv = NS_ERROR_OUT_OF_MEMORY;
// Open Disk Cache
rv = OpenDiskCache();
if (NS_FAILED(rv)) {
goto error_exit;
}
rv = mCacheMap->Open(mCacheDirectory);
if (NS_FAILED(rv)) {
rv = InitializeCacheDirectory(); // retry one time
if (NS_FAILED(rv)) goto error_exit;
}
mInitialized = PR_TRUE;
mFirstInit = PR_FALSE;
return NS_OK;
error_exit:
// XXX de-install observers?
if (mCacheMap) {
(void) mCacheMap->Close();
(void) mCacheMap->Close(PR_FALSE);
delete mCacheMap;
mCacheMap = nsnull;
}
@ -383,12 +373,19 @@ error_exit:
nsresult
nsDiskCacheDevice::Shutdown()
{
if (Initialized()) {
return Shutdown_Private(PR_TRUE);
}
nsresult
nsDiskCacheDevice::Shutdown_Private(PRBool flush)
{
if (Initialized()) {
// check cache limits in case we need to evict.
EvictDiskCacheEntries((PRInt32)mCacheCapacity);
// write out persistent information about the cache.
(void) mCacheMap->Close();
(void) mCacheMap->Close(flush);
delete mCacheMap;
mCacheMap = nsnull;
@ -401,22 +398,6 @@ nsDiskCacheDevice::Shutdown()
}
nsresult
nsDiskCacheDevice::Create(nsCacheDevice **result)
{
nsDiskCacheDevice * device = new nsDiskCacheDevice();
if (!device) return NS_ERROR_OUT_OF_MEMORY;
nsresult rv = device->Init();
if (NS_FAILED(rv)) {
delete device;
device = nsnull;
}
*result = device;
return rv;
}
const char *
nsDiskCacheDevice::GetDeviceID()
{
@ -444,7 +425,7 @@ nsDiskCacheDevice::FindEntry(nsCString * key)
#if DEBUG /*because we shouldn't be called for active entries */
binding = mBindery.FindActiveBinding(hashNumber);
NS_ASSERTION(!binding, "### FindEntry() called for a bound entry.");
NS_ASSERTION(!binding, "FindEntry() called for a bound entry.");
binding = nsnull;
#endif
@ -580,11 +561,12 @@ nsDiskCacheDevice::DoomEntry(nsCacheEntry * entry)
if (!binding->mDoomed) {
// so it can't be seen by FindEntry() ever again.
nsresult rv = mCacheMap->DoomRecord(&binding->mRecord);
NS_ASSERTION(NS_SUCCEEDED(rv), "DoomRecord failed.");
NS_ASSERTION(NS_SUCCEEDED(rv),"DoomRecord failed.");
binding->mDoomed = PR_TRUE; // record in no longer in cache map
}
}
/**
* NOTE: called while holding the cache service lock
*/
@ -609,6 +591,7 @@ nsDiskCacheDevice::OpenInputStreamForEntry(nsCacheEntry * entry,
return binding->mStreamIO->GetInputStream(offset, result);
}
/**
* NOTE: called while holding the cache service lock
*/
@ -785,8 +768,17 @@ nsDiskCacheDevice::Visit(nsICacheVisitor * visitor)
nsresult
nsDiskCacheDevice::EvictEntries(const char * clientID)
{
nsresult rv;
if (clientID == nsnull) {
// we're clearing the entire disk cache
rv = ClearDiskCache();
if (NS_SUCCEEDED(rv) || (rv != NS_ERROR_CACHE_IN_USE))
return rv;
}
nsDiskCacheEvictor evictor(this, mCacheMap, &mBindery, 0, clientID);
nsresult rv = mCacheMap->VisitRecords(&evictor);
rv = mCacheMap->VisitRecords(&evictor);
if (clientID == nsnull) // we tried to clear the entire cache
rv = mCacheMap->Trim(); // so trim cache block files (if possible)
@ -802,54 +794,8 @@ nsDiskCacheDevice::EvictEntries(const char * clientID)
#pragma mark PRIVATE METHODS
#endif
nsresult
nsDiskCacheDevice::InitializeCacheDirectory()
{
nsresult rv;
// recursively delete the disk cache directory.
rv = mCacheDirectory->Remove(PR_TRUE);
if (NS_FAILED(rv)) {
// try moving it aside
// create "Cache.Trash" directory if necessary
nsCOMPtr<nsIFile> cacheTrashDir;
rv = GetCacheTrashDirectory(getter_AddRefs(cacheTrashDir));
if (NS_FAILED(rv)) return rv;
PRBool exists = PR_FALSE;
rv = cacheTrashDir->Exists(&exists);
if (NS_FAILED(rv)) return rv;
if (!exists) {
// create the "Cache.Trash" directory
rv = cacheTrashDir->Create(nsIFile::DIRECTORY_TYPE,0777);
if (NS_FAILED(rv)) return rv;
}
// create a directory with unique name to contain existing cache directory
rv = cacheTrashDir->AppendNative(NS_LITERAL_CSTRING("Cache"));
if (NS_FAILED(rv)) return rv;
rv = cacheTrashDir->CreateUnique(nsIFile::DIRECTORY_TYPE, 0777);
if (NS_FAILED(rv)) return rv;
// move existing cache directory into profileDir/Cache.Trash/CacheUnique
nsCOMPtr<nsIFile> existingCacheDir;
rv = mCacheDirectory->Clone(getter_AddRefs(existingCacheDir));
if (NS_FAILED(rv)) return rv;
rv = existingCacheDir->MoveToNative(cacheTrashDir, nsCString());
if (NS_FAILED(rv)) return rv;
}
rv = mCacheDirectory->Create(nsIFile::DIRECTORY_TYPE, 0777);
if (NS_FAILED(rv)) return rv;
// reopen the cache map
rv = mCacheMap->Open(mCacheDirectory);
return rv;
}
// "Cache.Trash" directory is a sibling of the "Cache" directory
nsresult
nsDiskCacheDevice::GetCacheTrashDirectory(nsIFile ** result)
{
@ -865,6 +811,261 @@ nsDiskCacheDevice::GetCacheTrashDirectory(nsIFile ** result)
}
nsresult
nsDiskCacheDevice::OpenDiskCache()
{
nsresult rv;
// Try opening cache map file.
NS_ASSERTION(mCacheMap == nsnull, "leaking mCacheMap");
mCacheMap = new nsDiskCacheMap;
if (!mCacheMap) {
return NS_ERROR_OUT_OF_MEMORY;
}
// if we don't have a cache directory, create one and open it
PRBool cacheDirExists;
rv = mCacheDirectory->Exists(&cacheDirExists);
if (NS_FAILED(rv)) return rv;
if (cacheDirExists) {
rv = mCacheMap->Open(mCacheDirectory);
// move "corrupt" caches to trash
if (rv == NS_ERROR_FILE_CORRUPTED) {
rv = MoveCacheToTrash(nsnull); // ignore returned dir name
if (NS_FAILED(rv)) return rv;
cacheDirExists = PR_FALSE;
} else if (NS_FAILED(rv)) return rv;
}
// if we don't have a cache directory, create one and open it
if (!cacheDirExists) {
rv = InitializeCacheDirectory();
if (NS_FAILED(rv)) return rv;
}
if (! mFirstInit) return NS_OK; // we're done
// Empty Cache Trash
PRBool trashDirExists;
nsCOMPtr<nsIFile> trashDir;
rv = GetCacheTrashDirectory(getter_AddRefs(trashDir));
if (NS_FAILED(rv)) return rv;
rv = trashDir->Exists(&trashDirExists);
if (NS_FAILED(rv)) return rv;
if (trashDirExists) {
nsCOMArray<nsIFile> * trashList;
rv = ListTrashContents(&trashList);
if (NS_FAILED(rv)) return rv;
rv = DeleteFiles(trashList); // spin up thread to delete contents
if (NS_FAILED(rv)) return rv;
}
return NS_OK;
}
nsresult
nsDiskCacheDevice::ClearDiskCache()
{
if (mBindery.ActiveBindings()) return NS_ERROR_CACHE_IN_USE;
nsresult rv;
nsCOMPtr<nsIFile> trashDir;
nsCOMArray<nsIFile> * deleteList = new nsCOMArray<nsIFile>;
if (!deleteList) return NS_ERROR_OUT_OF_MEMORY;
rv = Shutdown_Private(PR_FALSE); // false = don't bother flushing
if (NS_FAILED(rv)) goto error_exit;
rv = MoveCacheToTrash(getter_AddRefs(trashDir));
if (NS_FAILED(rv)) goto error_exit;
rv = deleteList->AppendObject(trashDir);
if (NS_FAILED(rv)) goto error_exit;
rv = DeleteFiles(deleteList);
if (NS_FAILED(rv)) goto error_exit;
rv = Init();
return rv;
error_exit:
delete deleteList;
return rv;
}
// function passed to PR_CreateThread
void
DoDeleteFileList(void *arg)
{
nsCOMArray<nsIFile> * fileList = NS_STATIC_CAST(nsCOMArray<nsIFile> *, arg);
nsresult rv;
// iterate over items in fileList, recursively deleting each
PRInt32 count = fileList->Count();
for (PRInt32 i=0; i<count; i++) {
nsIFile * item = fileList->ObjectAt(i);
CACHE_LOG_PATH(PR_LOG_ALWAYS, "deleting: %s\n", item);
rv = item->Remove(PR_TRUE);
NS_ASSERTION(NS_SUCCEEDED(rv),"failure cleaning up cache");
}
delete fileList; // destroy nsCOMArray
}
#define DEFAULT_STACK_SIZE 0
nsresult
nsDiskCacheDevice::DeleteFiles(nsCOMArray<nsIFile> * fileList)
{
// start up another thread to delete deleteDir
PRThread * thread;
thread = PR_CreateThread(PR_USER_THREAD,
DoDeleteFileList,
fileList,
PR_PRIORITY_NORMAL,
PR_GLOBAL_THREAD,
PR_UNJOINABLE_THREAD,
DEFAULT_STACK_SIZE);
if (!thread) return NS_ERROR_UNEXPECTED;
return NS_OK;
}
/**
* ListTrashContents - return pointer to array of nsIFile to delete
*/
nsresult
nsDiskCacheDevice::ListTrashContents(nsCOMArray<nsIFile> ** result)
{
nsresult rv;
nsCOMPtr<nsIFile> trashDir;
*result = nsnull;
// does "Cache.Trash" directory exist?
rv = GetCacheTrashDirectory(getter_AddRefs(trashDir));
if (NS_FAILED(rv)) return rv;
PRBool exists;
rv = trashDir->Exists(&exists);
if (NS_FAILED(rv)) return rv;
if (!exists) {
return NS_OK;
}
nsCOMArray<nsIFile> * array = new nsCOMArray<nsIFile>;
if (!array) return NS_ERROR_OUT_OF_MEMORY;
// iterate over trash directory building array of directory objects
nsCOMPtr<nsISimpleEnumerator> dirEntries;
nsCOMPtr<nsIFile> item;
PRBool more, success;
rv = trashDir->GetDirectoryEntries(getter_AddRefs(dirEntries));
if (NS_FAILED(rv) || !dirEntries) goto error_exit; // !dirEntries returns NS_OK
rv = dirEntries->HasMoreElements(&more);
if (NS_FAILED(rv)) goto error_exit;
while (more) {
rv = dirEntries->GetNext(getter_AddRefs(item));
if (NS_FAILED(rv)) goto error_exit;
success = array->AppendObject(item);
if (!success) {
rv = NS_ERROR_OUT_OF_MEMORY;
goto error_exit;
}
rv = dirEntries->HasMoreElements(&more);
if (NS_FAILED(rv)) goto error_exit;
}
// return resulting array
*result = array;
return NS_OK;
error_exit:
delete array;
return rv;
}
// Move 'Cache' dir into unique directory inside 'Cache.Trash', return name of unique directory
nsresult
nsDiskCacheDevice::MoveCacheToTrash(nsIFile ** result)
{
nsresult rv;
nsCOMPtr<nsIFile> trashDir;
if (result) *result = nsnull;
rv = GetCacheTrashDirectory(getter_AddRefs(trashDir));
if (NS_FAILED(rv)) return rv;
// verify cache.trash exists and is a directory
PRBool exists;
rv = trashDir->Exists(&exists);
if (NS_FAILED(rv)) return rv;
if (exists) {
PRBool isDirectory;
rv = trashDir->IsDirectory(&isDirectory);
if (NS_FAILED(rv)) return rv;
if (!isDirectory) {
// delete file or fail
rv = trashDir->Remove(PR_FALSE);
if (NS_FAILED(rv)) return rv;
exists = PR_FALSE;
}
}
if (!exists) {
// cache.trash doesn't exists, so create it
rv = trashDir->Create(nsIFile::DIRECTORY_TYPE, 0777);
if (NS_FAILED(rv)) return rv;
}
// create unique directory
nsCOMPtr<nsIFile> uniqueDir;
rv = trashDir->Clone(getter_AddRefs(uniqueDir));
if (NS_FAILED(rv)) return rv;
rv = uniqueDir->AppendNative(NS_LITERAL_CSTRING("Trash"));
if (NS_FAILED(rv)) return rv;
rv = uniqueDir->CreateUnique(nsIFile::DIRECTORY_TYPE, 0777);
if (NS_FAILED(rv)) return rv;
// move cache directory into unique trash directory
nsCOMPtr<nsIFile> cacheDir;
rv = mCacheDirectory->Clone(getter_AddRefs(cacheDir));
if (NS_FAILED(rv)) return rv;
rv = cacheDir->MoveToNative(uniqueDir, nsCString());
if (NS_FAILED(rv)) return rv;
// return unique directory, in case caller wants specifically delete it
if (result)
NS_ADDREF(*result = uniqueDir);
return NS_OK;
}
nsresult
nsDiskCacheDevice::InitializeCacheDirectory()
{
nsresult rv;
rv = mCacheDirectory->Create(nsIFile::DIRECTORY_TYPE, 0777);
if (NS_FAILED(rv)) return rv;
// reopen the cache map
rv = mCacheMap->Open(mCacheDirectory);
return rv;
}
nsresult
nsDiskCacheDevice::EvictDiskCacheEntries(PRInt32 targetCapacity)
{
@ -908,7 +1109,7 @@ nsDiskCacheDevice::SetCacheParentDirectory(nsILocalFile * parentDir)
if (NS_SUCCEEDED(rv) && !exists)
rv = parentDir->Create(nsIFile::DIRECTORY_TYPE, 0700);
if (NS_FAILED(rv)) return;
// ensure cache directory exists
nsCOMPtr<nsIFile> directory;
@ -917,32 +1118,7 @@ nsDiskCacheDevice::SetCacheParentDirectory(nsILocalFile * parentDir)
rv = directory->AppendNative(NS_LITERAL_CSTRING("Cache"));
if (NS_FAILED(rv)) return;
rv = directory->Exists(&exists);
if (NS_SUCCEEDED(rv) && !exists)
rv = directory->Create(nsIFile::DIRECTORY_TYPE, 0700);
if (NS_FAILED(rv)) return;
mCacheDirectory = do_QueryInterface(directory);
// clean up Cache.Trash directories
rv = parentDir->Clone(getter_AddRefs(directory));
if (NS_FAILED(rv)) return;
rv = directory->AppendNative(NS_LITERAL_CSTRING("Cache.Trash"));
if (NS_FAILED(rv)) return;
rv = directory->Exists(&exists);
if (NS_SUCCEEDED(rv) && exists)
(void) directory->Remove(PR_TRUE);
// clean up obsolete NewCache directory
rv = parentDir->Clone(getter_AddRefs(directory));
if (NS_FAILED(rv)) return;
rv = directory->AppendNative(NS_LITERAL_CSTRING("NewCache"));
if (NS_FAILED(rv)) return;
rv = directory->Exists(&exists);
if (NS_SUCCEEDED(rv) && exists)
(void) directory->Remove(PR_TRUE);
}
@ -973,11 +1149,13 @@ PRUint32 nsDiskCacheDevice::getCacheCapacity()
return mCacheCapacity;
}
PRUint32 nsDiskCacheDevice::getCacheSize()
{
return mCacheMap->TotalSize();
}
PRUint32 nsDiskCacheDevice::getEntryCount()
{
return mCacheMap->EntryCount();

15
netwerk/cache/src/nsDiskCacheDevice.h поставляемый
Просмотреть файл

@ -32,6 +32,7 @@
#include "nsILocalFile.h"
#include "nsIObserver.h"
#include "nsCOMArray.h"
class nsDiskCacheMap;
@ -41,8 +42,6 @@ public:
nsDiskCacheDevice();
virtual ~nsDiskCacheDevice();
static nsresult Create(nsCacheDevice **result);
virtual nsresult Init();
virtual nsresult Shutdown();
@ -87,13 +86,22 @@ public:
PRBool Initialized() { return mInitialized; }
nsDiskCacheMap * CacheMap() { return mCacheMap; }
nsresult Shutdown_Private(PRBool flush);
private:
/**
* Private methods
*/
nsresult InitializeCacheDirectory();
nsresult GetCacheTrashDirectory(nsIFile ** result);
nsresult OpenDiskCache();
nsresult ClearDiskCache();
nsresult DeleteFiles(nsCOMArray<nsIFile> * fileList);
nsresult ListTrashContents(nsCOMArray<nsIFile> ** result);
nsresult MoveCacheToTrash(nsIFile ** result);
nsresult InitializeCacheDirectory();
nsresult EvictDiskCacheEntries(PRInt32 targetCapacity);
/**
@ -104,6 +112,7 @@ private:
PRUint32 mCacheCapacity; // XXX need soft/hard limits, currentTotal
nsDiskCacheMap * mCacheMap;
PRPackedBool mInitialized;
PRPackedBool mFirstInit;
};
#endif // _nsDiskCacheDevice_h_

132
netwerk/cache/src/nsDiskCacheMap.cpp поставляемый
Просмотреть файл

@ -60,7 +60,7 @@ nsDiskCacheBucket::Unswap()
}
PRUint32
PRInt32
nsDiskCacheBucket::CountRecords()
{
if (mRecords[0].HashNumber() == 0) return 0;
@ -135,6 +135,7 @@ nsDiskCacheBucket::VisitEachRecord(nsDiskCacheRecordVisitor * visitor,
* File operations
*/
nsresult
nsDiskCacheMap::Open(nsILocalFile * cacheDirectory)
{
@ -151,19 +152,21 @@ nsDiskCacheMap::Open(nsILocalFile * cacheDirectory)
if (NS_FAILED(rv)) return rv;
rv = localFile->AppendNative(NS_LITERAL_CSTRING("_CACHE_MAP_"));
if (NS_FAILED(rv)) return rv;
// open the file
rv = localFile->OpenNSPRFileDesc(PR_RDWR | PR_CREATE_FILE, 00666, &mMapFD);
if (NS_FAILED(rv)) return rv; // unable to open or create file
if (NS_FAILED(rv)) return NS_ERROR_FILE_CORRUPTED;
PRBool cacheFilesExist = CacheFilesExist();
rv = NS_ERROR_FILE_CORRUPTED; // presume the worst
// check size of map file
PRInt32 mapSize = PR_Available(mMapFD);
if (mapSize < 0) {
rv = NS_ERROR_UNEXPECTED;
goto error_exit;
}
if (mapSize == 0) {
PRInt32 mapSize = PR_Available(mMapFD);
if (mapSize == 0) { // creating a new _CACHE_MAP_
// block files shouldn't exist if we're creating the _CACHE_MAP_
if (cacheFilesExist) goto error_exit;
// create the file - initialize in memory
mHeader.mVersion = nsDiskCache::kCurrentVersion;
mHeader.mDataSize = 0;
@ -176,28 +179,32 @@ nsDiskCacheMap::Open(nsILocalFile * cacheDirectory)
memset(mHeader.reserved, 0, nsDiskCacheHeader::kReservedBytes);
memset(mBuckets, 0, sizeof(nsDiskCacheBucket) * kBucketsPerTable);
} else if (mapSize == kCacheMapSize) {
} else if (mapSize == kCacheMapSize) { // read existing _CACHE_MAP_
// if _CACHE_MAP_ exists, so should the block files
if (!cacheFilesExist) goto error_exit;
// read it in
PRUint32 bytesRead = PR_Read(mMapFD, &mHeader, kCacheMapSize);
if (kCacheMapSize != bytesRead) {
rv = NS_ERROR_UNEXPECTED;
goto error_exit;
}
if (kCacheMapSize != bytesRead) goto error_exit;
mHeader.Unswap();
if (mHeader.mIsDirty || mHeader.mVersion != nsDiskCache::kCurrentVersion) {
rv = NS_ERROR_FILE_CORRUPTED;
if (mHeader.mIsDirty ||
mHeader.mVersion != nsDiskCache::kCurrentVersion)
goto error_exit;
}
// Unswap each bucket
PRInt32 total = 0;
for (PRUint32 i = 0; i < kBucketsPerTable; ++i) {
mBuckets[i].Unswap();
total += mBuckets[i].CountRecords();
}
// XXX verify entry count, check size(?)
// verify entry count
if (total != mHeader.mEntryCount) goto error_exit;
} else {
rv = NS_ERROR_FILE_CORRUPTED;
goto error_exit;
}
@ -212,7 +219,7 @@ nsDiskCacheMap::Open(nsILocalFile * cacheDirectory)
return NS_OK;
error_exit:
(void) CloseBlockFiles();
(void) CloseBlockFiles(PR_FALSE);
if (mMapFD) {
(void) PR_Close(mMapFD);
@ -224,22 +231,24 @@ error_exit:
nsresult
nsDiskCacheMap::Close()
nsDiskCacheMap::Close(PRBool flush)
{
if (!mMapFD) return NS_OK;
// close block files
nsresult rv = CloseBlockFiles();
nsresult rv = CloseBlockFiles(flush);
if (NS_FAILED(rv)) goto exit; // this is going to be a mess...
// write map record buckets
rv = FlushBuckets(PR_FALSE); // don't bother swapping buckets back
if (NS_FAILED(rv)) goto exit;
if (flush) {
// write map record buckets
rv = FlushBuckets(PR_FALSE); // don't bother swapping buckets back
if (NS_FAILED(rv)) goto exit;
// clear dirty bit
mHeader.mIsDirty = PR_FALSE;
// clear dirty bit
mHeader.mIsDirty = PR_FALSE;
rv = FlushHeader();
rv = FlushHeader();
}
exit:
PRStatus err = PR_Close(mMapFD);
@ -322,16 +331,12 @@ nsresult
nsDiskCacheMap::AddRecord( nsDiskCacheRecord * mapRecord,
nsDiskCacheRecord * oldRecord)
{
nsresult rv;
PRUint32 hashNumber = mapRecord->HashNumber();
nsDiskCacheBucket * bucket;
nsDiskCacheBucket * bucket= GetBucketForHashNumber(hashNumber);
PRUint32 bucketIndex = GetBucketIndex(hashNumber);
int i;
oldRecord->SetHashNumber(0); // signify no record
rv = GetBucketForHashNumber(hashNumber, &bucket);
if (NS_FAILED(rv)) return rv;
oldRecord->SetHashNumber(0); // signify no record
nsDiskCacheRecord * mostEvictable = &bucket->mRecords[0];
for (i = 0; i < kRecordsPerBucket; ++i) {
@ -373,9 +378,7 @@ nsresult
nsDiskCacheMap::UpdateRecord( nsDiskCacheRecord * mapRecord)
{
PRUint32 hashNumber = mapRecord->HashNumber();
nsDiskCacheBucket * bucket;
nsresult rv = GetBucketForHashNumber(hashNumber, &bucket);
if (NS_FAILED(rv)) return rv;
nsDiskCacheBucket * bucket = GetBucketForHashNumber(hashNumber);
for (int i = 0; i < kRecordsPerBucket; ++i) {
if (bucket->mRecords[i].HashNumber() == mapRecord->HashNumber()) {
@ -403,9 +406,7 @@ NS_ASSERTION(mHeader.mEvictionRank[bucketIndex] == bucket->EvictionRank(0),
nsresult
nsDiskCacheMap::FindRecord( PRUint32 hashNumber, nsDiskCacheRecord * result)
{
nsDiskCacheBucket * bucket;
nsresult rv = GetBucketForHashNumber(hashNumber, &bucket);
if (NS_FAILED(rv)) return rv;
nsDiskCacheBucket * bucket = GetBucketForHashNumber(hashNumber);
for (int i = 0; i < kRecordsPerBucket; ++i) {
if (bucket->mRecords[i].HashNumber() == 0) break;
@ -423,20 +424,20 @@ nsDiskCacheMap::FindRecord( PRUint32 hashNumber, nsDiskCacheRecord * result)
nsresult
nsDiskCacheMap::DeleteRecord( nsDiskCacheRecord * mapRecord)
{
nsDiskCacheBucket * bucket;
nsresult rv = GetBucketForHashNumber(mapRecord->HashNumber(), &bucket);
if (NS_FAILED(rv)) return rv;
nsDiskCacheBucket * bucket = GetBucketForHashNumber(mapRecord->HashNumber());
PRUint32 count = bucket->CountRecords();
for (PRUint32 i = 0; i < count; ++i) {
PRInt32 count = bucket->CountRecords();
for (PRInt32 i = 0; i < count; ++i) {
if (bucket->mRecords[i].HashNumber() == mapRecord->HashNumber()) {
// found it, now delete it.
PRUint32 evictionRank = bucket->mRecords[i].EvictionRank();
NS_ASSERTION(evictionRank == mapRecord->EvictionRank(), "evictionRank out of sync");
if (i != (count - 1)) { // if not the last record, shift last record into opening
NS_ASSERTION(evictionRank == mapRecord->EvictionRank(),
"evictionRank out of sync");
if (i != (count - 1)) {
// if not the last record, shift last record into opening
bucket->mRecords[i] = bucket->mRecords[count - 1];
}
bucket->mRecords[count - 1].SetHashNumber(0); // clear last record
bucket->mRecords[count - 1].SetHashNumber(0); // clear last record
mHeader.mEntryCount--;
// update eviction rank
PRUint32 bucketIndex = GetBucketIndex(mapRecord->HashNumber());
@ -444,8 +445,8 @@ nsDiskCacheMap::DeleteRecord( nsDiskCacheRecord * mapRecord)
mHeader.mEvictionRank[bucketIndex] = bucket->EvictionRank(0);
}
NS_ASSERTION(mHeader.mEvictionRank[bucketIndex] == bucket->EvictionRank(0),
"eviction rank out of sync");
NS_ASSERTION(mHeader.mEvictionRank[bucketIndex] ==
bucket->EvictionRank(0), "eviction rank out of sync");
return NS_OK;
}
}
@ -548,23 +549,42 @@ nsDiskCacheMap::OpenBlockFiles()
return NS_OK;
error_exit:
(void)CloseBlockFiles(); // we already have an error to report
(void)CloseBlockFiles(PR_FALSE); // we already have an error to report
return rv;
}
nsresult
nsDiskCacheMap::CloseBlockFiles()
nsDiskCacheMap::CloseBlockFiles(PRBool flush)
{
nsresult rv, rv2 = NS_OK;
for (int i=0; i < 3; ++i) {
rv = mBlockFile[i].Close();
rv = mBlockFile[i].Close(flush);
if (NS_FAILED(rv)) rv2 = rv; // if one or more errors, report at least one
}
return rv2;
}
PRBool
nsDiskCacheMap::CacheFilesExist()
{
nsCOMPtr<nsILocalFile> blockFile;
nsresult rv;
for (int i = 0; i < 3; ++i) {
PRBool exists;
rv = GetBlockFileForIndex(i, getter_AddRefs(blockFile));
if (NS_FAILED(rv)) return PR_FALSE;
rv = blockFile->Exists(&exists);
if (NS_FAILED(rv) || !exists) return PR_FALSE;
}
return PR_TRUE;
}
nsresult
nsDiskCacheMap::ReadDiskCacheEntry(nsDiskCacheRecord * record, nsDiskCacheEntry ** result)
{

14
netwerk/cache/src/nsDiskCacheMap.h поставляемый
Просмотреть файл

@ -321,7 +321,7 @@ struct nsDiskCacheBucket {
void Swap();
void Unswap();
PRUint32 CountRecords();
PRInt32 CountRecords();
PRUint32 EvictionRank(PRUint32 targetRank); // return largest rank in bucket < targetRank
PRInt32 VisitEachRecord( nsDiskCacheRecordVisitor * visitor,
PRUint32 evictionRank,
@ -400,7 +400,7 @@ public:
{
NS_ASSERTION(sizeof(nsDiskCacheHeader) == sizeof(nsDiskCacheBucket), "structure misalignment");
}
~nsDiskCacheMap() { (void) Close(); }
~nsDiskCacheMap() { (void) Close(PR_TRUE); }
/**
* File Operations
@ -411,7 +411,7 @@ public:
* Returns error if it detects change in format or cache wasn't closed.
*/
nsresult Open( nsILocalFile * cacheDirectory);
nsresult Close();
nsresult Close(PRBool flush);
nsresult Trim();
// nsresult Flush();
@ -480,17 +480,17 @@ private:
* Private methods
*/
nsresult OpenBlockFiles();
nsresult CloseBlockFiles();
nsresult CloseBlockFiles(PRBool flush);
PRBool CacheFilesExist();
PRUint32 CalculateFileIndex(PRUint32 size);
nsresult GetBlockFileForIndex( PRUint32 index, nsILocalFile ** result);
PRUint32 GetBlockSizeForIndex( PRUint32 index);
nsresult GetBucketForHashNumber( PRUint32 hashNumber, nsDiskCacheBucket ** result)
nsDiskCacheBucket * GetBucketForHashNumber( PRUint32 hashNumber)
{
*result = &mBuckets[GetBucketIndex(hashNumber)];
return NS_OK;
return &mBuckets[GetBucketIndex(hashNumber)];
}
PRUint32 GetBucketIndex( PRUint32 hashNumber)