Files
Mozilla/mozilla/netwerk/cache/src/nsCacheService.cpp
gordon%netscape.com 19a0033de2 Added GetTransportForEntry(), DoomEntry() to public methods, and BindEntry() and DoomEntry_Internal() to private methods.
git-svn-id: svn://10.0.0.236/trunk@88167 18797224-902f-48f8-a5cc-f745e15eee43
2001-02-28 04:10:43 +00:00

459 lines
12 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is nsCacheService.cpp, released February 10, 2001.
*
* The Initial Developer of the Original Code is Netscape Communications
* Corporation. Portions created by Netscape are
* Copyright (C) 2001 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Gordon Sheridan, 10-February-2001
*/
#include "nsCacheService.h"
#include "nsCacheEntry.h"
#include "nsCacheEntryDescriptor.h"
#include "nsCacheDevice.h"
#include "nsCacheRequest.h"
#include "nsMemoryCacheDevice.h"
#include "nsDiskCacheDevice.h"
#include "nsAutoLock.h"
#include "nsVoidArray.h"
nsCacheService * nsCacheService::gService = nsnull;
NS_IMPL_THREADSAFE_ISUPPORTS1(nsCacheService, nsICacheService)
nsCacheService::nsCacheService()
: mCacheServiceLock(nsnull),
mMemoryDevice(nsnull),
mDiskDevice(nsnull)
{
NS_INIT_REFCNT();
NS_ASSERTION(gService==nsnull, "multiple nsCacheService instances!");
gService = this;
// create list of cache devices
PR_INIT_CLIST(&mDoomedEntries);
}
nsCacheService::~nsCacheService()
{
if (mCacheServiceLock) // Shutdown hasn't been called yet.
(void) Shutdown();
gService = nsnull;
}
NS_IMETHODIMP
nsCacheService::Init()
{
nsresult rv;
NS_ASSERTION(mCacheServiceLock== nsnull, "nsCacheService already initialized.");
if (mCacheServiceLock)
return NS_ERROR_ALREADY_INITIALIZED;
mCacheServiceLock = PR_NewLock();
if (mCacheServiceLock == nsnull)
return NS_ERROR_OUT_OF_MEMORY;
// initialize hashtable for active cache entries
rv = mActiveEntries.Init();
if (NS_FAILED(rv)) goto error;
// create memory cache
mMemoryDevice = new nsMemoryCacheDevice;
if (!mMemoryDevice) {
rv = NS_ERROR_OUT_OF_MEMORY;
goto error;
}
rv = mMemoryDevice->Init();
if (NS_FAILED(rv)) goto error;
// create disk cache
mDiskDevice = new nsDiskCacheDevice;
if (!mDiskDevice) {
rv = NS_ERROR_OUT_OF_MEMORY;
goto error;
}
rv = mDiskDevice->Init();
if (NS_FAILED(rv)) goto error;
return NS_OK;
error:
(void)Shutdown();
return rv;
}
NS_IMETHODIMP
nsCacheService::Shutdown()
{
NS_ASSERTION(mCacheServiceLock != nsnull,
"can't shutdown nsCacheService unless it has been initialized.");
if (mCacheServiceLock) {
//** check for pending requests...
//** finalize active entries
// deallocate memory and disk caches
delete mMemoryDevice;
mMemoryDevice = nsnull;
delete mDiskDevice;
mDiskDevice = nsnull;
PR_DestroyLock(mCacheServiceLock);
mCacheServiceLock = nsnull;
}
return NS_OK;
}
NS_METHOD
nsCacheService::Create(nsISupports* aOuter, const nsIID& aIID, void* *aResult)
{
nsresult rv;
if (aOuter != nsnull)
return NS_ERROR_NO_AGGREGATION;
nsCacheService* cacheService = new nsCacheService();
if (cacheService == nsnull)
return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(cacheService);
rv = cacheService->Init();
if (NS_SUCCEEDED(rv)) {
rv = cacheService->QueryInterface(aIID, aResult);
}
NS_RELEASE(cacheService);
return rv;
}
NS_IMETHODIMP
nsCacheService::CreateSession(const char * clientID,
nsCacheStoragePolicy storagePolicy,
PRBool streamBased,
nsICacheSession **result)
{
*result = nsnull;
nsCacheSession * session = new nsCacheSession(clientID, storagePolicy, streamBased);
if (!session) return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(*result = session);
return NS_OK;
}
/* void visitEntries (in nsICacheVisitor visitor); */
NS_IMETHODIMP nsCacheService::VisitEntries(nsICacheVisitor *visitor)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
nsresult
nsCacheService::CreateRequest(nsCacheSession * session,
const char * clientKey,
nsCacheAccessMode accessRequested,
nsICacheListener * listener,
nsCacheRequest ** request)
{
NS_ASSERTION(request, "CommonOpenCacheEntry: request or entry is null");
nsCString * key = new nsCString(*session->ClientID() +
nsLiteralCString(":") +
nsLiteralCString(clientKey));
if (!key)
return NS_ERROR_OUT_OF_MEMORY;
// create request
*request = new nsCacheRequest(key, listener, accessRequested,
session->StreamBased(),
session->StoragePolicy());
if (!*request) {
delete key;
return NS_ERROR_OUT_OF_MEMORY;
}
return NS_OK;
}
nsresult
nsCacheService::OpenCacheEntry(nsCacheSession * session,
const char * key,
nsCacheAccessMode accessRequested,
nsICacheEntryDescriptor ** result)
{
*result = nsnull;
if (!mCacheServiceLock) return NS_ERROR_NOT_INITIALIZED;
nsCacheRequest * request = nsnull;
nsCacheEntry * entry = nsnull;
nsresult rv = CreateRequest(session, key, accessRequested, nsnull, &request);
if (NS_FAILED(rv)) return rv;
while (1) {
//** acquire lock
rv = ActivateEntry(request, &entry);
if (NS_FAILED(rv)) break;
//** check for NS_ERROR_CACHE_KEY_NOT_FOUND & READ-ONLY request
rv = entry->Open(request, result); //** release lock before waiting on request
if (rv != NS_ERROR_CACHE_ENTRY_DOOMED) break;
}
return rv;
}
nsresult
nsCacheService::AsyncOpenCacheEntry(nsCacheSession * session,
const char * key,
nsCacheAccessMode accessRequested,
nsICacheListener * listener)
{
if (!mCacheServiceLock) return NS_ERROR_NOT_INITIALIZED;
if (!listener) return NS_ERROR_NULL_POINTER;
nsCacheRequest * request = nsnull;
nsCacheEntry * entry = nsnull;
nsresult rv = CreateRequest(session, key, accessRequested, listener, &request);
//** acquire service lock PR_Lock(mCacheServiceLock);
rv = ActivateEntry(request, &entry);
if (NS_SUCCEEDED(rv)) {
entry->AsyncOpen(request); //** release lock after request queued, etc.
}
return rv;
}
nsresult
nsCacheService::ActivateEntry(nsCacheRequest * request,
nsCacheEntry ** result)
{
nsresult rv = NS_OK;
NS_ASSERTION(request != nsnull, "ActivateEntry called with no request");
if (result) *result = nsnull;
if ((!request) || (!result)) return NS_ERROR_NULL_POINTER;
// search active entries (including those not bound to device)
nsCacheEntry *entry = mActiveEntries.GetEntry(request->mKey);
// doom existing entry if we are processing a FORCE-WRITE
if (entry && (request->mAccessRequested == nsICache::ACCESS_WRITE)) {
DoomEntry_Internal(entry);
entry = nsnull;
}
if (!entry) {
entry = SearchCacheDevices(request->mKey, request->mStoragePolicy);
if (!entry) {
if (!(request->mAccessRequested & nsICache::ACCESS_WRITE)) {
// this was a READ-ONLY request
rv = NS_ERROR_CACHE_KEY_NOT_FOUND;
goto error;
}
entry = new nsCacheEntry(request->mKey,
request->mStreamBased,
request->mStoragePolicy);
if (!entry)
return NS_ERROR_OUT_OF_MEMORY;
#if 0
//** we could bind entry to device early in somecases
if ((request->mStoragePolicy == nsICache::STORE_IN_MEMORY) ||
(!request->mStreamBased))
{
// we can bind the memory cache
rv = mMemoryDevice->BindEntry(entry);
if (NS_FAILED(rv)) {
//** what to do?
goto error;
}
} else if (request->mStoragePolicy == nsICache::STORE_ON_DISK) {
// we can bind to the disk cache
rv = mDiskDevice->BindEntry(entry);
if (NS_FAILED(rv)) {
//** what to do?
goto error;
}
}
#endif
}
rv = mActiveEntries.AddEntry(entry);
if (NS_FAILED(rv)) goto error;
}
*result = entry;
return NS_OK;
error:
*result = nsnull;
if (entry) {
//** clean up
}
return rv;
}
nsCacheEntry *
nsCacheService::SearchCacheDevices(nsCString * key, nsCacheStoragePolicy policy)
{
nsCacheEntry * entry = nsnull;
if ((policy == nsICache::STORE_ANYWHERE) || (policy == nsICache::STORE_IN_MEMORY)) {
entry = mMemoryDevice->FindEntry(key);
}
if (!entry &&
((policy == nsICache::STORE_ANYWHERE) || (policy == nsICache::STORE_ON_DISK))) {
entry = mDiskDevice->FindEntry(key);
}
return entry;
}
nsresult
nsCacheService::BindEntry(nsCacheEntry * entry)
{
nsresult rv = NS_OK;
if (entry->IsStreamData() && entry->IsAllowedOnDisk()) {
//** disk (default)
rv = mDiskDevice->BindEntry(entry);
} else {
//** memory cache
//** assert entry->IsAllowedInMemory()
rv = mMemoryDevice->BindEntry(entry);
}
if (NS_FAILED(rv)) {
//** what to do?
}
return rv;
}
nsresult
nsCacheService::DoomEntry(nsCacheEntry * entry)
{
nsAutoLock lock(mCacheServiceLock);
return DoomEntry_Internal(entry);
}
nsresult
nsCacheService::DoomEntry_Internal(nsCacheEntry * entry)
{
if (entry->IsDoomed()) return NS_OK;
nsresult rv = NS_OK;
entry->MarkDoomed();
nsCacheDevice * device = entry->CacheDevice();
if (device) {
rv = device->DoomEntry(entry);
//** check rv, but what can we really do...
}
// remove from active entries
rv = mActiveEntries.RemoveEntry(entry);
if (NS_FAILED(rv)) {
//** what to do
}
// put on doom list to wait for descriptors to close
NS_ASSERTION(PR_CLIST_IS_EMPTY(entry->GetListNode()),
"doomed entry still on device list");
PR_APPEND_LINK(entry->GetListNode(), &mDoomedEntries);
return rv;
}
nsresult
nsCacheService::GetTransportForEntry(nsCacheEntry * entry, nsITransport **result)
{
nsAutoLock lock(mCacheServiceLock);
nsresult rv = NS_OK;
nsCacheDevice * device = entry->CacheDevice();
if (!device) {
rv = BindEntry(entry);
if (NS_FAILED(rv)) return rv;
device = entry->CacheDevice();
}
return device->GetTransportForEntry(entry, result);
}
void
nsCacheService::CloseDescriptor(nsCacheEntryDescriptor * descriptor)
{
nsAutoLock lock(mCacheServiceLock);
// ask entry to remove descriptor
nsCacheEntry * entry = descriptor->CacheEntry();
PRBool stillActive = entry->RemoveDescriptor(descriptor);
if (!stillActive) {
DeactivateEntry(entry);
}
}
void
nsCacheService::DeactivateEntry(nsCacheEntry * entry)
{
//** check if entry is doomed
//** remove from hashtable
nsCacheDevice * device = entry->CacheDevice();
if (device) {
nsresult rv = device->DeactivateEntry(entry);
if (NS_FAILED(rv)) {
//** what do we do on errors?
}
} else {
//** increment deactivating unbound entry statistic
}
}