Back to index

lightning-sunbird  0.9+nobinonly
nsCacheEntry.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
00002  *
00003  * ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is nsCacheEntry.cpp, released
00017  * February 22, 2001.
00018  *
00019  * The Initial Developer of the Original Code is
00020  * Netscape Communications Corporation.
00021  * Portions created by the Initial Developer are Copyright (C) 2001
00022  * the Initial Developer. All Rights Reserved.
00023  *
00024  * Contributor(s):
00025  *   Gordon Sheridan <gordon@netscape.com>
00026  *
00027  * Alternatively, the contents of this file may be used under the terms of
00028  * either the GNU General Public License Version 2 or later (the "GPL"), or
00029  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00030  * in which case the provisions of the GPL or the LGPL are applicable instead
00031  * of those above. If you wish to allow use of your version of this file only
00032  * under the terms of either the GPL or the LGPL, and not to allow others to
00033  * use your version of this file under the terms of the MPL, indicate your
00034  * decision by deleting the provisions above and replace them with the notice
00035  * and other provisions required by the GPL or the LGPL. If you do not delete
00036  * the provisions above, a recipient may use your version of this file under
00037  * the terms of any one of the MPL, the GPL or the LGPL.
00038  *
00039  * ***** END LICENSE BLOCK ***** */
00040 
00041 
00042 #include "nspr.h"
00043 #include "nsCacheEntry.h"
00044 #include "nsCacheEntryDescriptor.h"
00045 #include "nsCacheMetaData.h"
00046 #include "nsCacheRequest.h"
00047 #include "nsError.h"
00048 #include "nsICacheService.h"
00049 #include "nsCache.h"
00050 #include "nsCacheService.h"
00051 #include "nsCacheDevice.h"
00052 #include "nsCRT.h"
00053 
00054 
00055 nsCacheEntry::nsCacheEntry(nsCString *          key,
00056                            PRBool               streamBased,
00057                            nsCacheStoragePolicy storagePolicy)
00058     : mKey(key),
00059       mFetchCount(0),
00060       mLastFetched(0),
00061       mLastModified(0),
00062       mExpirationTime(NO_EXPIRATION_TIME),
00063       mFlags(0),
00064       mDataSize(0),
00065       mCacheDevice(nsnull),
00066       mData(nsnull)
00067 {
00068     MOZ_COUNT_CTOR(nsCacheEntry);
00069     PR_INIT_CLIST(this);
00070     PR_INIT_CLIST(&mRequestQ);
00071     PR_INIT_CLIST(&mDescriptorQ);
00072 
00073     if (streamBased) MarkStreamBased();
00074     SetStoragePolicy(storagePolicy);
00075 }
00076 
00077 
00078 nsCacheEntry::~nsCacheEntry()
00079 {
00080     MOZ_COUNT_DTOR(nsCacheEntry);
00081     delete mKey;
00082     
00083     if (IsStreamData())  return;
00084 
00085     // proxy release of of memory cache nsISupports objects
00086     if (!mData)  return;
00087     
00088     nsISupports * data = mData;
00089     NS_ADDREF(data);    // this reference will be owned by the proxy
00090     mData = nsnull;     // release our reference before switching threads
00091 
00092     nsCacheService::ProxyObjectRelease(data, mThread);
00093 }
00094 
00095 
00096 nsresult
00097 nsCacheEntry::Create( const char *          key,
00098                       PRBool                streamBased,
00099                       nsCacheStoragePolicy  storagePolicy,
00100                       nsCacheDevice *       device,
00101                       nsCacheEntry **       result)
00102 {
00103     nsCString* newKey = new nsCString(key);
00104     if (!newKey) return NS_ERROR_OUT_OF_MEMORY;
00105     
00106     nsCacheEntry* entry = new nsCacheEntry(newKey, streamBased, storagePolicy);
00107     if (!entry) { delete newKey; return NS_ERROR_OUT_OF_MEMORY; }
00108     
00109     entry->SetCacheDevice(device);
00110     
00111     *result = entry;
00112     return NS_OK;
00113 }
00114 
00115 
00116 void
00117 nsCacheEntry::Fetched()
00118 {
00119     mLastFetched = SecondsFromPRTime(PR_Now());
00120     ++mFetchCount;
00121     MarkEntryDirty();
00122 }
00123 
00124 
00125 const char *
00126 nsCacheEntry::GetDeviceID()
00127 {
00128     if (mCacheDevice)  return mCacheDevice->GetDeviceID();
00129     return nsnull;
00130 }
00131 
00132 
00133 void
00134 nsCacheEntry::TouchData()
00135 {
00136     mLastModified = SecondsFromPRTime(PR_Now());
00137     MarkDataDirty();
00138 }
00139 
00140 
00141 void
00142 nsCacheEntry::TouchMetaData()
00143 {
00144     mLastModified = SecondsFromPRTime(PR_Now());
00145     MarkMetaDataDirty();
00146 }
00147 
00148 
00149 nsresult
00150 nsCacheEntry::GetSecurityInfo( nsISupports ** result)
00151 {
00152     NS_ENSURE_ARG_POINTER(result);
00153     NS_IF_ADDREF(*result = mSecurityInfo);
00154     return NS_OK;
00155 }
00156 
00157 
00166 nsresult
00167 nsCacheEntry::RequestAccess(nsCacheRequest * request, nsCacheAccessMode *accessGranted)
00168 {
00169     nsresult  rv = NS_OK;
00170     
00171     if (!IsInitialized()) {
00172         // brand new, unbound entry
00173         request->mKey = nsnull;  // steal ownership of the key string
00174         if (request->IsStreamBased())  MarkStreamBased();
00175         MarkInitialized();
00176 
00177         *accessGranted = request->AccessRequested() & nsICache::ACCESS_WRITE;
00178         NS_ASSERTION(*accessGranted, "new cache entry for READ-ONLY request");
00179         PR_APPEND_LINK(request, &mRequestQ);
00180         return rv;
00181     }
00182     
00183     if (IsDoomed()) return NS_ERROR_CACHE_ENTRY_DOOMED;
00184 
00185     if (IsStreamData() != request->IsStreamBased()) {
00186         *accessGranted = nsICache::ACCESS_NONE;
00187         return request->IsStreamBased() ?
00188             NS_ERROR_CACHE_DATA_IS_NOT_STREAM : NS_ERROR_CACHE_DATA_IS_STREAM;
00189     }
00190 
00191     if (PR_CLIST_IS_EMPTY(&mDescriptorQ)) {
00192         // 1st descriptor for existing bound entry
00193         *accessGranted = request->AccessRequested();
00194         if (*accessGranted & nsICache::ACCESS_WRITE) {
00195             MarkInvalid();
00196         } else {
00197             MarkValid();
00198         }
00199     } else {
00200         // nth request for existing, bound entry
00201         *accessGranted = request->AccessRequested() & ~nsICache::ACCESS_WRITE;
00202         if (!IsValid())
00203             rv = NS_ERROR_CACHE_WAIT_FOR_VALIDATION;
00204     }
00205     PR_APPEND_LINK(request,&mRequestQ);
00206 
00207     return rv;
00208 }
00209 
00210 
00211 nsresult
00212 nsCacheEntry::CreateDescriptor(nsCacheRequest *           request,
00213                                nsCacheAccessMode          accessGranted,
00214                                nsICacheEntryDescriptor ** result)
00215 {
00216     NS_ENSURE_ARG_POINTER(request && result);
00217 
00218     nsCacheEntryDescriptor * descriptor =
00219         new nsCacheEntryDescriptor(this, accessGranted);
00220 
00221     // XXX check request is on q
00222     PR_REMOVE_AND_INIT_LINK(request); // remove request regardless of success
00223 
00224     if (descriptor == nsnull)
00225         return NS_ERROR_OUT_OF_MEMORY;
00226 
00227     PR_APPEND_LINK(descriptor, &mDescriptorQ);
00228 
00229     NS_ADDREF(*result = descriptor);
00230     return NS_OK;
00231 }
00232 
00233 
00234 PRBool
00235 nsCacheEntry::RemoveRequest(nsCacheRequest * request)
00236 {
00237     // XXX if debug: verify this request belongs to this entry
00238     PR_REMOVE_AND_INIT_LINK(request);
00239 
00240     // return true if this entry should stay active
00241     return !((PR_CLIST_IS_EMPTY(&mRequestQ)) &&
00242              (PR_CLIST_IS_EMPTY(&mDescriptorQ)));
00243 }
00244 
00245 
00246 PRBool
00247 nsCacheEntry::RemoveDescriptor(nsCacheEntryDescriptor * descriptor)
00248 {
00249     NS_ASSERTION(descriptor->CacheEntry() == this, "### Wrong cache entry!!");
00250     PR_REMOVE_AND_INIT_LINK(descriptor);
00251     descriptor->ClearCacheEntry();
00252 
00253     if (!PR_CLIST_IS_EMPTY(&mDescriptorQ))
00254         return PR_TRUE;  // stay active if we still have open descriptors
00255 
00256     if (PR_CLIST_IS_EMPTY(&mRequestQ))
00257         return PR_FALSE; // no descriptors or requests, we can deactivate
00258 
00259     return PR_TRUE;     // find next best request to give a descriptor to
00260 }
00261 
00262 
00263 void
00264 nsCacheEntry::DetachDescriptors(void)
00265 {
00266     nsCacheEntryDescriptor * descriptor =
00267         (nsCacheEntryDescriptor *)PR_LIST_HEAD(&mDescriptorQ);
00268 
00269     while (descriptor != &mDescriptorQ) {
00270         nsCacheEntryDescriptor * nextDescriptor =
00271             (nsCacheEntryDescriptor *)PR_NEXT_LINK(descriptor);
00272         
00273         descriptor->ClearCacheEntry();
00274         PR_REMOVE_AND_INIT_LINK(descriptor);
00275         descriptor = nextDescriptor;
00276     }
00277 }
00278 
00279 
00280 /******************************************************************************
00281  * nsCacheEntryInfo - for implementing about:cache
00282  *****************************************************************************/
00283 
00284 NS_IMPL_ISUPPORTS1(nsCacheEntryInfo, nsICacheEntryInfo)
00285 
00286 
00287 NS_IMETHODIMP
00288 nsCacheEntryInfo::GetClientID(char ** clientID)
00289 {
00290     NS_ENSURE_ARG_POINTER(clientID);
00291     if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
00292 
00293     return ClientIDFromCacheKey(*mCacheEntry->Key(), clientID);
00294 }
00295 
00296 
00297 NS_IMETHODIMP
00298 nsCacheEntryInfo::GetDeviceID(char ** deviceID)
00299 {
00300     NS_ENSURE_ARG_POINTER(deviceID);
00301     if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
00302     
00303     *deviceID = nsCRT::strdup(mCacheEntry->GetDeviceID());
00304     return *deviceID ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
00305 }
00306 
00307 
00308 NS_IMETHODIMP
00309 nsCacheEntryInfo::GetKey(nsACString &key)
00310 {
00311     if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
00312 
00313     return ClientKeyFromCacheKey(*mCacheEntry->Key(), key);
00314 }
00315 
00316 
00317 NS_IMETHODIMP
00318 nsCacheEntryInfo::GetFetchCount(PRInt32 * fetchCount)
00319 {
00320     NS_ENSURE_ARG_POINTER(fetchCount);
00321     if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
00322 
00323     *fetchCount = mCacheEntry->FetchCount();
00324     return NS_OK;
00325 }
00326 
00327 
00328 NS_IMETHODIMP
00329 nsCacheEntryInfo::GetLastFetched(PRUint32 * lastFetched)
00330 {
00331     NS_ENSURE_ARG_POINTER(lastFetched);
00332     if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
00333 
00334     *lastFetched = mCacheEntry->LastFetched();
00335     return NS_OK;
00336 }
00337 
00338 
00339 NS_IMETHODIMP
00340 nsCacheEntryInfo::GetLastModified(PRUint32 * lastModified)
00341 {
00342     NS_ENSURE_ARG_POINTER(lastModified);
00343     if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
00344 
00345     *lastModified = mCacheEntry->LastModified();
00346     return NS_OK;
00347 }
00348 
00349 
00350 NS_IMETHODIMP
00351 nsCacheEntryInfo::GetExpirationTime(PRUint32 * expirationTime)
00352 {
00353     NS_ENSURE_ARG_POINTER(expirationTime);
00354     if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
00355 
00356     *expirationTime = mCacheEntry->ExpirationTime();
00357     return NS_OK;
00358 }
00359 
00360 
00361 NS_IMETHODIMP
00362 nsCacheEntryInfo::GetDataSize(PRUint32 * dataSize)
00363 {
00364     NS_ENSURE_ARG_POINTER(dataSize);
00365     if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
00366 
00367     *dataSize = mCacheEntry->DataSize();
00368     return NS_OK;
00369 }
00370 
00371 
00372 NS_IMETHODIMP
00373 nsCacheEntryInfo::IsStreamBased(PRBool * result)
00374 {
00375     NS_ENSURE_ARG_POINTER(result);
00376     if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
00377     
00378     *result = mCacheEntry->IsStreamData();
00379     return NS_OK;
00380 }
00381 
00382 
00383 /******************************************************************************
00384  *  nsCacheEntryHashTable
00385  *****************************************************************************/
00386 
00387 PLDHashTableOps
00388 nsCacheEntryHashTable::ops =
00389 {
00390     PL_DHashAllocTable,
00391     PL_DHashFreeTable,
00392     GetKey,
00393     HashKey,
00394     MatchEntry,
00395     MoveEntry,
00396     ClearEntry,
00397     PL_DHashFinalizeStub
00398 };
00399 
00400 
00401 nsCacheEntryHashTable::nsCacheEntryHashTable()
00402     : initialized(PR_FALSE)
00403 {
00404     MOZ_COUNT_CTOR(nsCacheEntryHashTable);
00405 }
00406 
00407 
00408 nsCacheEntryHashTable::~nsCacheEntryHashTable()
00409 {
00410     MOZ_COUNT_DTOR(nsCacheEntryHashTable);
00411     if (initialized)
00412         Shutdown();
00413 }
00414 
00415 
00416 nsresult
00417 nsCacheEntryHashTable::Init()
00418 {
00419     nsresult rv = NS_OK;
00420     initialized = PL_DHashTableInit(&table, &ops, nsnull,
00421                                            sizeof(nsCacheEntryHashTableEntry), 512);
00422 
00423     if (!initialized) rv = NS_ERROR_OUT_OF_MEMORY;
00424     
00425     return rv;
00426 }
00427 
00428 void
00429 nsCacheEntryHashTable::Shutdown()
00430 {
00431     if (initialized) {
00432         PL_DHashTableFinish(&table);
00433         initialized = PR_FALSE;
00434     }
00435 }
00436 
00437 
00438 nsCacheEntry *
00439 nsCacheEntryHashTable::GetEntry( const nsCString * key)
00440 {
00441     PLDHashEntryHdr *hashEntry;
00442     nsCacheEntry    *result = nsnull;
00443 
00444     NS_ASSERTION(initialized, "nsCacheEntryHashTable not initialized");
00445     if (!initialized)  return nsnull;
00446     
00447     hashEntry = PL_DHashTableOperate(&table, key, PL_DHASH_LOOKUP);
00448     if (PL_DHASH_ENTRY_IS_BUSY(hashEntry)) {
00449         result = ((nsCacheEntryHashTableEntry *)hashEntry)->cacheEntry;
00450     }
00451     return result;
00452 }
00453 
00454 
00455 nsresult
00456 nsCacheEntryHashTable::AddEntry( nsCacheEntry *cacheEntry)
00457 {
00458     PLDHashEntryHdr    *hashEntry;
00459 
00460     NS_ASSERTION(initialized, "nsCacheEntryHashTable not initialized");
00461     if (!initialized)  return NS_ERROR_NOT_INITIALIZED;
00462     if (!cacheEntry)   return NS_ERROR_NULL_POINTER;
00463 
00464     hashEntry = PL_DHashTableOperate(&table, cacheEntry->mKey, PL_DHASH_ADD);
00465 #ifndef DEBUG_dougt
00466     NS_ASSERTION(((nsCacheEntryHashTableEntry *)hashEntry)->cacheEntry == 0,
00467                  "### nsCacheEntryHashTable::AddEntry - entry already used");
00468 #endif
00469     ((nsCacheEntryHashTableEntry *)hashEntry)->cacheEntry = cacheEntry;
00470 
00471     return NS_OK;
00472 }
00473 
00474 
00475 void
00476 nsCacheEntryHashTable::RemoveEntry( nsCacheEntry *cacheEntry)
00477 {
00478     NS_ASSERTION(initialized, "nsCacheEntryHashTable not initialized");
00479     NS_ASSERTION(cacheEntry, "### cacheEntry == nsnull");
00480 
00481     if (!initialized)  return; // NS_ERROR_NOT_INITIALIZED
00482 
00483 #if DEBUG
00484     // XXX debug code to make sure we have the entry we're trying to remove
00485     nsCacheEntry *check = GetEntry(cacheEntry->mKey);
00486     NS_ASSERTION(check == cacheEntry, "### Attempting to remove unknown cache entry!!!");
00487 #endif
00488     (void) PL_DHashTableOperate(&table, cacheEntry->mKey, PL_DHASH_REMOVE);
00489 }
00490 
00491 
00492 void
00493 nsCacheEntryHashTable::VisitEntries( nsCacheEntryHashTable::Visitor *visitor)
00494 {
00495     NS_ASSERTION(initialized, "nsCacheEntryHashTable not initialized");
00496     if (!initialized)  return; // NS_ERROR_NOT_INITIALIZED
00497     PL_DHashTableEnumerate(&table, VisitEntry, visitor);
00498 }
00499 
00500 
00501 PLDHashOperator PR_CALLBACK
00502 nsCacheEntryHashTable::VisitEntry(PLDHashTable *table,
00503                                   PLDHashEntryHdr *hashEntry,
00504                                   PRUint32 number,
00505                                   void *arg)
00506 {
00507     nsCacheEntry *cacheEntry = ((nsCacheEntryHashTableEntry *)hashEntry)->cacheEntry;
00508     nsCacheEntryHashTable::Visitor *visitor = (nsCacheEntryHashTable::Visitor*) arg;
00509     return (visitor->VisitEntry(cacheEntry) ? PL_DHASH_NEXT : PL_DHASH_STOP);
00510 }
00511 
00515 const void * PR_CALLBACK
00516 nsCacheEntryHashTable::GetKey( PLDHashTable * /*table*/, PLDHashEntryHdr *hashEntry)
00517 {
00518     nsCacheEntry *cacheEntry = ((nsCacheEntryHashTableEntry *)hashEntry)->cacheEntry;
00519     return cacheEntry->mKey;
00520 }
00521 
00522 
00523 PLDHashNumber PR_CALLBACK
00524 nsCacheEntryHashTable::HashKey( PLDHashTable *table, const void *key)
00525 {
00526     return PL_DHashStringKey(table,((nsCString *)key)->get());
00527 }
00528 
00529 PRBool PR_CALLBACK
00530 nsCacheEntryHashTable::MatchEntry(PLDHashTable *       /* table */,
00531                                   const PLDHashEntryHdr * hashEntry,
00532                                   const void *            key)
00533 {
00534     NS_ASSERTION(key !=  nsnull, "### nsCacheEntryHashTable::MatchEntry : null key");
00535     nsCacheEntry *cacheEntry = ((nsCacheEntryHashTableEntry *)hashEntry)->cacheEntry;
00536 
00537     return cacheEntry->mKey->Equals(*(nsCString *)key);
00538 }
00539 
00540 
00541 void PR_CALLBACK
00542 nsCacheEntryHashTable::MoveEntry(PLDHashTable * /* table */,
00543                                  const PLDHashEntryHdr *from,
00544                                  PLDHashEntryHdr       *to)
00545 {
00546     ((nsCacheEntryHashTableEntry *)to)->cacheEntry =
00547         ((nsCacheEntryHashTableEntry *)from)->cacheEntry;
00548 }
00549 
00550 
00551 void PR_CALLBACK
00552 nsCacheEntryHashTable::ClearEntry(PLDHashTable * /* table */,
00553                                   PLDHashEntryHdr * hashEntry)
00554 {
00555     ((nsCacheEntryHashTableEntry *)hashEntry)->cacheEntry = 0;
00556 }
00557