зеркало из https://github.com/mozilla/gecko-dev.git
fixes bug 328196 "nsDiskCacheDevice: Memory and codesize optimisations" patch by Alfred Kayser <alfredkayser@nl.ibm.com>, r=darin
This commit is contained in:
Родитель
e709b63ba8
Коммит
7c37f72def
|
@ -102,77 +102,65 @@ static const char DISK_CACHE_DEVICE_ID[] = { "disk" };
|
|||
class nsDiskCacheEvictor : public nsDiskCacheRecordVisitor
|
||||
{
|
||||
public:
|
||||
nsDiskCacheEvictor( nsDiskCacheDevice * device,
|
||||
nsDiskCacheMap * cacheMap,
|
||||
nsDiskCacheEvictor( nsDiskCacheMap * cacheMap,
|
||||
nsDiskCacheBindery * cacheBindery,
|
||||
PRInt32 targetSize,
|
||||
const char * clientID)
|
||||
: mDevice(device)
|
||||
, mCacheMap(cacheMap)
|
||||
: mCacheMap(cacheMap)
|
||||
, mBindery(cacheBindery)
|
||||
, mTargetSize(targetSize)
|
||||
, mClientID(clientID)
|
||||
{}
|
||||
{
|
||||
mClientIDSize = clientID ? strlen(clientID) : 0;
|
||||
}
|
||||
|
||||
virtual PRInt32 VisitRecord(nsDiskCacheRecord * mapRecord);
|
||||
|
||||
private:
|
||||
nsDiskCacheDevice * mDevice;
|
||||
nsDiskCacheMap * mCacheMap;
|
||||
nsDiskCacheBindery * mBindery;
|
||||
PRInt32 mTargetSize;
|
||||
const char * mClientID;
|
||||
PRUint32 mClientIDSize;
|
||||
};
|
||||
|
||||
|
||||
PRInt32
|
||||
nsDiskCacheEvictor::VisitRecord(nsDiskCacheRecord * mapRecord)
|
||||
{
|
||||
// read disk cache entry
|
||||
nsDiskCacheEntry * diskEntry = nsnull;
|
||||
nsDiskCacheBinding * binding = nsnull;
|
||||
char * clientID = nsnull;
|
||||
PRInt32 result = kVisitNextRecord;
|
||||
|
||||
if (mCacheMap->TotalSize() < mTargetSize)
|
||||
return kStopVisitingRecords;
|
||||
|
||||
if (mClientID) {
|
||||
// we're just evicting records for a specific client
|
||||
// we're just evicting records for a specific client
|
||||
nsDiskCacheEntry * diskEntry = nsnull;
|
||||
nsresult rv = mCacheMap->ReadDiskCacheEntry(mapRecord, &diskEntry);
|
||||
if (NS_FAILED(rv)) goto exit; // XXX or delete record?
|
||||
if (NS_FAILED(rv))
|
||||
return kVisitNextRecord; // XXX or delete record?
|
||||
|
||||
// XXX FIXME compare clientID's without malloc
|
||||
|
||||
// get client ID from key
|
||||
rv = ClientIDFromCacheKey(nsDependentCString(diskEntry->mKeyStart), &clientID);
|
||||
if (NS_FAILED(rv)) goto exit;
|
||||
|
||||
if (nsCRT::strcmp(mClientID, clientID) != 0) goto exit;
|
||||
// Compare clientID's without malloc
|
||||
if ((diskEntry->mKeySize <= mClientIDSize) ||
|
||||
(diskEntry->mKeyStart[mClientIDSize] != ':') ||
|
||||
(memcmp(diskEntry->mKeyStart, mClientID, mClientIDSize) != 0)) {
|
||||
delete [] (char *)diskEntry;
|
||||
return kVisitNextRecord; // clientID doesn't match, skip it
|
||||
}
|
||||
delete [] (char *)diskEntry;
|
||||
}
|
||||
|
||||
if (mCacheMap->TotalSize() < mTargetSize) {
|
||||
result = kStopVisitingRecords;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
binding = mBindery->FindActiveBinding(mapRecord->HashNumber());
|
||||
nsDiskCacheBinding * 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.
|
||||
binding->mDoomed = PR_TRUE; // mark binding record as 'deleted'
|
||||
nsCacheService::DoomEntry(binding->mCacheEntry);
|
||||
result = kDeleteRecordAndContinue; // this will REALLY delete the record
|
||||
|
||||
} else {
|
||||
// entry not in use, just delete storage because we're enumerating the records
|
||||
(void) mCacheMap->DeleteStorage(mapRecord);
|
||||
result = kDeleteRecordAndContinue; // this will REALLY delete the record
|
||||
}
|
||||
|
||||
exit:
|
||||
|
||||
delete clientID;
|
||||
delete [] (char *)diskEntry;
|
||||
return result;
|
||||
return kDeleteRecordAndContinue; // this will REALLY delete the record
|
||||
}
|
||||
|
||||
|
||||
|
@ -333,7 +321,6 @@ nsDiskCache::Truncate(PRFileDesc * fd, PRUint32 newEOF)
|
|||
|
||||
nsDiskCacheDevice::nsDiskCacheDevice()
|
||||
: mCacheCapacity(0)
|
||||
, mCacheMap(nsnull)
|
||||
, mInitialized(PR_FALSE)
|
||||
{
|
||||
}
|
||||
|
@ -341,7 +328,6 @@ nsDiskCacheDevice::nsDiskCacheDevice()
|
|||
nsDiskCacheDevice::~nsDiskCacheDevice()
|
||||
{
|
||||
Shutdown();
|
||||
delete mCacheMap;
|
||||
}
|
||||
|
||||
|
||||
|
@ -355,27 +341,22 @@ nsDiskCacheDevice::Init()
|
|||
|
||||
NS_ENSURE_TRUE(!Initialized(), NS_ERROR_FAILURE);
|
||||
|
||||
if (!mCacheDirectory) return NS_ERROR_FAILURE;
|
||||
if (!mCacheDirectory)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
rv = mBindery.Init();
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
// Open Disk Cache
|
||||
rv = OpenDiskCache();
|
||||
if (NS_FAILED(rv)) {
|
||||
goto error_exit;
|
||||
(void) mCacheMap.Close(PR_FALSE);
|
||||
return rv;
|
||||
}
|
||||
|
||||
mInitialized = PR_TRUE;
|
||||
return NS_OK;
|
||||
|
||||
error_exit:
|
||||
if (mCacheMap) {
|
||||
(void) mCacheMap->Close(PR_FALSE);
|
||||
delete mCacheMap;
|
||||
mCacheMap = nsnull;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
|
@ -412,9 +393,7 @@ nsDiskCacheDevice::Shutdown_Private(PRBool flush)
|
|||
EvictDiskCacheEntries((PRInt32)mCacheCapacity);
|
||||
|
||||
// write out persistent information about the cache.
|
||||
(void) mCacheMap->Close(flush);
|
||||
delete mCacheMap;
|
||||
mCacheMap = nsnull;
|
||||
(void) mCacheMap.Close(flush);
|
||||
|
||||
mBindery.Reset();
|
||||
|
||||
|
@ -458,15 +437,15 @@ nsDiskCacheDevice::FindEntry(nsCString * key)
|
|||
#endif
|
||||
|
||||
// lookup hash number in cache map
|
||||
rv = mCacheMap->FindRecord(hashNumber, &record);
|
||||
rv = mCacheMap.FindRecord(hashNumber, &record);
|
||||
if (NS_FAILED(rv)) return nsnull; // XXX log error?
|
||||
|
||||
nsDiskCacheEntry * diskEntry;
|
||||
rv = mCacheMap->ReadDiskCacheEntry(&record, &diskEntry);
|
||||
rv = mCacheMap.ReadDiskCacheEntry(&record, &diskEntry);
|
||||
if (NS_FAILED(rv)) return nsnull;
|
||||
|
||||
// compare key to be sure
|
||||
if (nsCRT::strcmp(diskEntry->mKeyStart, key->get()) == 0) {
|
||||
if (strcmp(diskEntry->mKeyStart, key->get()) == 0) {
|
||||
entry = diskEntry->CreateCacheEntry(this);
|
||||
}
|
||||
delete [] (char *)diskEntry;
|
||||
|
@ -497,14 +476,15 @@ nsDiskCacheDevice::DeactivateEntry(nsCacheEntry * entry)
|
|||
|
||||
if (entry->IsDoomed()) {
|
||||
// delete data, entry, record from disk for entry
|
||||
rv = mCacheMap->DeleteStorage(&binding->mRecord);
|
||||
rv = mCacheMap.DeleteStorage(&binding->mRecord);
|
||||
|
||||
} else {
|
||||
// save stuff to disk for entry
|
||||
rv = mCacheMap->WriteDiskCacheEntry(binding);
|
||||
rv = mCacheMap.WriteDiskCacheEntry(binding);
|
||||
if (NS_FAILED(rv)) {
|
||||
// clean up as best we can
|
||||
(void) mCacheMap->DeleteRecordAndStorage(&binding->mRecord);
|
||||
(void) mCacheMap.DeleteStorage(&binding->mRecord);
|
||||
(void) mCacheMap.DeleteRecord(&binding->mRecord);
|
||||
binding->mDoomed = PR_TRUE; // record is no longer in cache map
|
||||
}
|
||||
}
|
||||
|
@ -543,7 +523,7 @@ nsDiskCacheDevice::BindEntry(nsCacheEntry * entry)
|
|||
|
||||
if (!entry->IsDoomed()) {
|
||||
// if entry isn't doomed, add it to the cache map
|
||||
rv = mCacheMap->AddRecord(&record, &oldRecord); // deletes old record, if any
|
||||
rv = mCacheMap.AddRecord(&record, &oldRecord); // deletes old record, if any
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
PRUint32 oldHashNumber = oldRecord.HashNumber();
|
||||
|
@ -561,7 +541,7 @@ nsDiskCacheDevice::BindEntry(nsCacheEntry * entry)
|
|||
} else {
|
||||
// delete storage
|
||||
// XXX if debug : compare keys for hashNumber collision
|
||||
rv = mCacheMap->DeleteStorage(&oldRecord);
|
||||
rv = mCacheMap.DeleteStorage(&oldRecord);
|
||||
if (NS_FAILED(rv)) return rv; // XXX delete record we just added?
|
||||
}
|
||||
}
|
||||
|
@ -589,8 +569,8 @@ 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.");
|
||||
nsresult rv = mCacheMap.DeleteRecord(&binding->mRecord);
|
||||
NS_ASSERTION(NS_SUCCEEDED(rv),"DeleteRecord failed.");
|
||||
binding->mDoomed = PR_TRUE; // record in no longer in cache map
|
||||
}
|
||||
}
|
||||
|
@ -675,15 +655,15 @@ nsDiskCacheDevice::GetFileForEntry(nsCacheEntry * entry,
|
|||
binding->mRecord.SetDataFileSize(0); // 1k minimum
|
||||
if (!binding->mDoomed) {
|
||||
// record stored in cache map, so update it
|
||||
rv = mCacheMap->UpdateRecord(&binding->mRecord);
|
||||
rv = mCacheMap.UpdateRecord(&binding->mRecord);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
}
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIFile> file;
|
||||
rv = mCacheMap->GetFileForDiskCacheRecord(&binding->mRecord,
|
||||
nsDiskCache::kData,
|
||||
getter_AddRefs(file));
|
||||
rv = mCacheMap.GetFileForDiskCacheRecord(&binding->mRecord,
|
||||
nsDiskCache::kData,
|
||||
getter_AddRefs(file));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
NS_IF_ADDREF(*result = file);
|
||||
|
@ -733,13 +713,10 @@ nsDiskCacheDevice::OnDataSizeChange(nsCacheEntry * entry, PRInt32 deltaSize)
|
|||
class EntryInfoVisitor : public nsDiskCacheRecordVisitor
|
||||
{
|
||||
public:
|
||||
EntryInfoVisitor(nsDiskCacheDevice * device,
|
||||
nsDiskCacheMap * cacheMap,
|
||||
EntryInfoVisitor(nsDiskCacheMap * cacheMap,
|
||||
nsICacheVisitor * visitor)
|
||||
: mDevice(device)
|
||||
, mCacheMap(cacheMap)
|
||||
: mCacheMap(cacheMap)
|
||||
, mVisitor(visitor)
|
||||
, mResult(NS_OK)
|
||||
{}
|
||||
|
||||
virtual PRInt32 VisitRecord(nsDiskCacheRecord * mapRecord)
|
||||
|
@ -750,14 +727,12 @@ public:
|
|||
nsDiskCacheEntry * diskEntry;
|
||||
nsresult rv = mCacheMap->ReadDiskCacheEntry(mapRecord, &diskEntry);
|
||||
if (NS_FAILED(rv)) {
|
||||
mResult = rv;
|
||||
return kVisitNextRecord;
|
||||
}
|
||||
|
||||
// create nsICacheEntryInfo
|
||||
nsDiskCacheEntryInfo * entryInfo = new nsDiskCacheEntryInfo(DISK_CACHE_DEVICE_ID, diskEntry);
|
||||
if (!entryInfo) {
|
||||
mResult = NS_ERROR_OUT_OF_MEMORY;
|
||||
return kStopVisitingRecords;
|
||||
}
|
||||
nsCOMPtr<nsICacheEntryInfo> ref(entryInfo);
|
||||
|
@ -769,10 +744,8 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
nsDiskCacheDevice * mDevice;
|
||||
nsDiskCacheMap * mCacheMap;
|
||||
nsICacheVisitor * mVisitor;
|
||||
nsresult mResult;
|
||||
};
|
||||
|
||||
|
||||
|
@ -788,8 +761,8 @@ nsDiskCacheDevice::Visit(nsICacheVisitor * visitor)
|
|||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
if (keepGoing) {
|
||||
EntryInfoVisitor infoVisitor(this, mCacheMap, visitor);
|
||||
return mCacheMap->VisitRecords(&infoVisitor);
|
||||
EntryInfoVisitor infoVisitor(&mCacheMap, visitor);
|
||||
return mCacheMap.VisitRecords(&infoVisitor);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
|
@ -809,11 +782,11 @@ nsDiskCacheDevice::EvictEntries(const char * clientID)
|
|||
return rv;
|
||||
}
|
||||
|
||||
nsDiskCacheEvictor evictor(this, mCacheMap, &mBindery, 0, clientID);
|
||||
rv = mCacheMap->VisitRecords(&evictor);
|
||||
nsDiskCacheEvictor evictor(&mCacheMap, &mBindery, 0, clientID);
|
||||
rv = mCacheMap.VisitRecords(&evictor);
|
||||
|
||||
if (clientID == nsnull) // we tried to clear the entire cache
|
||||
rv = mCacheMap->Trim(); // so trim cache block files (if possible)
|
||||
rv = mCacheMap.Trim(); // so trim cache block files (if possible)
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
@ -830,23 +803,16 @@ nsDiskCacheDevice::EvictEntries(const char * clientID)
|
|||
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 exists;
|
||||
rv = mCacheDirectory->Exists(&exists);
|
||||
nsresult rv = mCacheDirectory->Exists(&exists);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
PRBool trashing = PR_FALSE;
|
||||
if (exists) {
|
||||
rv = mCacheMap->Open(mCacheDirectory);
|
||||
// Try opening cache map file.
|
||||
rv = mCacheMap.Open(mCacheDirectory);
|
||||
// move "corrupt" caches to trash
|
||||
if (rv == NS_ERROR_FILE_CORRUPTED) {
|
||||
rv = DeleteDir(mCacheDirectory, PR_TRUE, PR_FALSE);
|
||||
|
@ -861,7 +827,14 @@ nsDiskCacheDevice::OpenDiskCache()
|
|||
|
||||
// if we don't have a cache directory, create one and open it
|
||||
if (!exists) {
|
||||
rv = InitializeCacheDirectory();
|
||||
rv = mCacheDirectory->Create(nsIFile::DIRECTORY_TYPE, 0777);
|
||||
CACHE_LOG_PATH(PR_LOG_ALWAYS, "\ncreate cache directory: %s\n", mCacheDirectory);
|
||||
CACHE_LOG_ALWAYS(("mCacheDirectory->Create() = %x\n", rv));
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
// reopen the cache map
|
||||
rv = mCacheMap.Open(mCacheDirectory);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
}
|
||||
|
@ -901,33 +874,14 @@ nsDiskCacheDevice::ClearDiskCache()
|
|||
}
|
||||
|
||||
|
||||
nsresult
|
||||
nsDiskCacheDevice::InitializeCacheDirectory()
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
rv = mCacheDirectory->Create(nsIFile::DIRECTORY_TYPE, 0777);
|
||||
CACHE_LOG_PATH(PR_LOG_ALWAYS, "\ncreate cache directory: %s\n", mCacheDirectory);
|
||||
CACHE_LOG_ALWAYS(("mCacheDirectory->Create() = %x\n", rv));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
// reopen the cache map
|
||||
rv = mCacheMap->Open(mCacheDirectory);
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
nsresult
|
||||
nsDiskCacheDevice::EvictDiskCacheEntries(PRInt32 targetCapacity)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
if (mCacheMap->TotalSize() < targetCapacity) return NS_OK;
|
||||
if (mCacheMap.TotalSize() < targetCapacity)
|
||||
return NS_OK;
|
||||
|
||||
nsDiskCacheEvictor evictor(this, mCacheMap, &mBindery, targetCapacity, nsnull);
|
||||
rv = mCacheMap->EvictRecords(&evictor);
|
||||
|
||||
return rv;
|
||||
nsDiskCacheEvictor evictor(&mCacheMap, &mBindery, targetCapacity, nsnull);
|
||||
return mCacheMap.EvictRecords(&evictor);
|
||||
}
|
||||
|
||||
|
||||
|
@ -1003,11 +957,11 @@ PRUint32 nsDiskCacheDevice::getCacheCapacity()
|
|||
|
||||
PRUint32 nsDiskCacheDevice::getCacheSize()
|
||||
{
|
||||
return mCacheMap->TotalSize();
|
||||
return mCacheMap.TotalSize();
|
||||
}
|
||||
|
||||
|
||||
PRUint32 nsDiskCacheDevice::getEntryCount()
|
||||
{
|
||||
return mCacheMap->EntryCount();
|
||||
return mCacheMap.EntryCount();
|
||||
}
|
||||
|
|
|
@ -101,21 +101,21 @@ public:
|
|||
PRUint32 getCacheSize();
|
||||
PRUint32 getEntryCount();
|
||||
|
||||
PRBool Initialized() { return mInitialized; }
|
||||
nsDiskCacheMap * CacheMap() { return mCacheMap; }
|
||||
nsresult Shutdown_Private(PRBool flush);
|
||||
nsDiskCacheMap * CacheMap() { return &mCacheMap; }
|
||||
|
||||
private:
|
||||
/**
|
||||
* Private methods
|
||||
*/
|
||||
|
||||
nsresult OpenDiskCache();
|
||||
nsresult ClearDiskCache();
|
||||
nsresult InitializeCacheDirectory();
|
||||
PRBool Initialized() { return mInitialized; }
|
||||
|
||||
nsresult Shutdown_Private(PRBool flush);
|
||||
|
||||
nsresult EvictDiskCacheEntries(PRInt32 targetCapacity);
|
||||
nsresult OpenDiskCache();
|
||||
nsresult ClearDiskCache();
|
||||
|
||||
nsresult EvictDiskCacheEntries(PRInt32 targetCapacity);
|
||||
|
||||
/**
|
||||
* Member variables
|
||||
|
@ -123,7 +123,7 @@ private:
|
|||
nsCOMPtr<nsILocalFile> mCacheDirectory;
|
||||
nsDiskCacheBindery mBindery;
|
||||
PRUint32 mCacheCapacity; // XXX need soft/hard limits, currentTotal
|
||||
nsDiskCacheMap * mCacheMap;
|
||||
nsDiskCacheMap mCacheMap;
|
||||
PRPackedBool mInitialized;
|
||||
};
|
||||
|
||||
|
|
|
@ -857,16 +857,6 @@ nsDiskCacheMap::WriteDataCacheBlocks(nsDiskCacheBinding * binding, char * buffer
|
|||
}
|
||||
|
||||
|
||||
nsresult
|
||||
nsDiskCacheMap::DoomRecord(nsDiskCacheRecord * record)
|
||||
{
|
||||
nsresult rv = DeleteRecord(record);
|
||||
// XXX future: add record to doomed record journal
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
nsresult
|
||||
nsDiskCacheMap::DeleteStorage(nsDiskCacheRecord * record)
|
||||
{
|
||||
|
@ -909,15 +899,6 @@ nsDiskCacheMap::DeleteStorage(nsDiskCacheRecord * record, PRBool metaData)
|
|||
}
|
||||
|
||||
|
||||
nsresult
|
||||
nsDiskCacheMap::DeleteRecordAndStorage(nsDiskCacheRecord * record)
|
||||
{
|
||||
nsresult rv1 = DeleteStorage(record);
|
||||
nsresult rv2 = DeleteRecord(record);
|
||||
return NS_FAILED(rv1) ? rv1 : rv2;
|
||||
}
|
||||
|
||||
|
||||
nsresult
|
||||
nsDiskCacheMap::GetFileForDiskCacheRecord(nsDiskCacheRecord * record,
|
||||
PRBool meta,
|
||||
|
|
|
@ -422,9 +422,7 @@ public:
|
|||
/**
|
||||
* Disk Entry operations
|
||||
*/
|
||||
nsresult DoomRecord( nsDiskCacheRecord * record);
|
||||
nsresult DeleteStorage( nsDiskCacheRecord * record);
|
||||
nsresult DeleteRecordAndStorage( nsDiskCacheRecord * record);
|
||||
|
||||
nsresult GetFileForDiskCacheRecord( nsDiskCacheRecord * record,
|
||||
PRBool meta,
|
||||
|
|
|
@ -511,7 +511,7 @@ nsDiskCacheStreamIO::Flush()
|
|||
rv = cacheMap->DeleteStorage(record, nsDiskCache::kData);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("cacheMap->DeleteStorage() failed.");
|
||||
cacheMap->DoomRecord(record);
|
||||
cacheMap->DeleteRecord(record);
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче