From 84bba7528a81428de4d46479f8f34f521d7ebb6b Mon Sep 17 00:00:00 2001 From: Michal Novotny Date: Thu, 29 Jul 2010 16:23:10 -0700 Subject: [PATCH] Bug 513008 - Eliminate synchronous reads from cache. r=jduell.mcbugs@gmail.com, cbiesinger@gmail.com, sr=shaver@mozilla.org --- netwerk/cache/nsCacheRequest.h | 1 + netwerk/cache/nsCacheService.cpp | 93 +++++- netwerk/cache/nsCacheService.h | 3 + netwerk/cache/nsICacheService.idl | 8 +- netwerk/protocol/http/nsHttpChannel.cpp | 391 +++++++++++++++--------- netwerk/protocol/http/nsHttpChannel.h | 17 +- netwerk/protocol/http/nsHttpHandler.cpp | 21 -- netwerk/protocol/http/nsHttpHandler.h | 2 - 8 files changed, 367 insertions(+), 169 deletions(-) diff --git a/netwerk/cache/nsCacheRequest.h b/netwerk/cache/nsCacheRequest.h index 19b869625fc4..9ccdad7747ed 100644 --- a/netwerk/cache/nsCacheRequest.h +++ b/netwerk/cache/nsCacheRequest.h @@ -54,6 +54,7 @@ class nsCacheRequest : public PRCList private: friend class nsCacheService; friend class nsCacheEntry; + friend class nsProcessRequestEvent; nsCacheRequest( nsCString * key, nsICacheListener * listener, diff --git a/netwerk/cache/nsCacheService.cpp b/netwerk/cache/nsCacheService.cpp index dc39d3983fcc..1135af402425 100644 --- a/netwerk/cache/nsCacheService.cpp +++ b/netwerk/cache/nsCacheService.cpp @@ -614,6 +614,45 @@ nsCacheProfilePrefObserver::MemoryCacheCapacity() return capacity; } + +/****************************************************************************** + * nsProcessRequestEvent + *****************************************************************************/ + +class nsProcessRequestEvent : public nsRunnable { +public: + nsProcessRequestEvent(nsCacheRequest *aRequest) + { + mRequest = aRequest; + } + + NS_IMETHOD Run() + { + nsresult rv; + + NS_ASSERTION(mRequest->mListener, + "Sync OpenCacheEntry() posted to background thread!"); + + nsCacheServiceAutoLock lock; + rv = nsCacheService::gService->ProcessRequest(mRequest, + PR_FALSE, + nsnull); + + // Don't delete the request if it was queued + if (rv != NS_ERROR_CACHE_WAIT_FOR_VALIDATION) + delete mRequest; + + return NS_OK; + } + +protected: + virtual ~nsProcessRequestEvent() {} + +private: + nsCacheRequest *mRequest; +}; + + /****************************************************************************** * nsCacheService *****************************************************************************/ @@ -681,8 +720,13 @@ nsCacheService::Init() CACHE_LOG_INIT(); + nsresult rv = NS_NewThread(getter_AddRefs(mCacheIOThread)); + if (NS_FAILED(rv)) { + NS_WARNING("Can't create cache IO thread"); + } + // initialize hashtable for active cache entries - nsresult rv = mActiveEntries.Init(); + rv = mActiveEntries.Init(); if (NS_FAILED(rv)) return rv; // create profile/preference observer @@ -703,6 +747,9 @@ nsCacheService::Init() void nsCacheService::Shutdown() { + nsCOMPtr cacheIOThread; + + { nsCacheServiceAutoLock lock; NS_ASSERTION(mInitialized, "can't shutdown nsCacheService unless it has been initialized."); @@ -734,7 +781,13 @@ nsCacheService::Shutdown() #if defined(NECKO_DISK_CACHE) && defined(PR_LOGGING) LogCacheStatistics(); #endif + + mCacheIOThread.swap(cacheIOThread); } + } // lock + + if (cacheIOThread) + cacheIOThread->Shutdown(); } @@ -947,6 +1000,17 @@ NS_IMETHODIMP nsCacheService::EvictEntries(nsCacheStoragePolicy storagePolicy) return EvictEntriesForClient(nsnull, storagePolicy); } +NS_IMETHODIMP nsCacheService::GetCacheIOTarget(nsIEventTarget * *aCacheIOTarget) +{ + nsCacheServiceAutoLock lock; + + if (!mCacheIOThread) + return NS_ERROR_NOT_AVAILABLE; + + NS_ADDREF(*aCacheIOTarget = mCacheIOThread); + return NS_OK; +} + /** * Internal Methods */ @@ -1235,11 +1299,28 @@ nsCacheService::OpenCacheEntry(nsCacheSession * session, CACHE_LOG_DEBUG(("Created request %p\n", request)); - rv = gService->ProcessRequest(request, PR_TRUE, result); + // Process the request on the background thread if we are on the main thread + // and the the request is asynchronous + if (NS_IsMainThread() && listener && gService->mCacheIOThread) { + nsCOMPtr ev = + new nsProcessRequestEvent(request); + if (ev) { + rv = gService->mCacheIOThread->Dispatch(ev, NS_DISPATCH_NORMAL); + } else { + rv = NS_ERROR_OUT_OF_MEMORY; + } - // delete requests that have completed - if (!(listener && (rv == NS_ERROR_CACHE_WAIT_FOR_VALIDATION))) - delete request; + // delete request if we didn't post the event + if (NS_FAILED(rv)) + delete request; + } + else { + rv = gService->ProcessRequest(request, PR_TRUE, result); + + // delete requests that have completed + if (!(listener && (rv == NS_ERROR_CACHE_WAIT_FOR_VALIDATION))) + delete request; + } return rv; } @@ -1779,7 +1860,7 @@ nsCacheService::ReleaseObject_Locked(nsISupports * obj, NS_ASSERTION(gService->mLockedThread == PR_GetCurrentThread(), "oops"); PRBool isCur; - if (!target || NS_SUCCEEDED(target->IsOnCurrentThread(&isCur)) && isCur) { + if (!target || (NS_SUCCEEDED(target->IsOnCurrentThread(&isCur)) && isCur)) { gService->mDoomedObjects.AppendElement(obj); } else { NS_ProxyRelease(target, obj); diff --git a/netwerk/cache/nsCacheService.h b/netwerk/cache/nsCacheService.h index d808f7055658..8ca2e5dfe3ab 100644 --- a/netwerk/cache/nsCacheService.h +++ b/netwerk/cache/nsCacheService.h @@ -166,6 +166,7 @@ public: private: friend class nsCacheServiceAutoLock; friend class nsOfflineCacheDevice; + friend class nsProcessRequestEvent; /** * Internal Methods @@ -245,6 +246,8 @@ private: PRThread * mLockedThread; // The thread holding mLock #endif + nsCOMPtr mCacheIOThread; + nsTArray mDoomedObjects; PRBool mInitialized; diff --git a/netwerk/cache/nsICacheService.idl b/netwerk/cache/nsICacheService.idl index 0bc46b6230b6..baa18d809cc4 100644 --- a/netwerk/cache/nsICacheService.idl +++ b/netwerk/cache/nsICacheService.idl @@ -47,8 +47,9 @@ interface nsISimpleEnumerator; interface nsICacheListener; interface nsICacheSession; interface nsICacheVisitor; +interface nsIEventTarget; -[scriptable, uuid(de114eb4-29fc-4959-b2f7-2d03eb9bc771)] +[scriptable, uuid(14dbe1e9-f3bc-45af-92f4-2c574fcd4e39)] interface nsICacheService : nsISupports { /** @@ -87,6 +88,11 @@ interface nsICacheService : nsISupports * everything. */ void evictEntries(in nsCacheStoragePolicy storagePolicy); + + /** + * Event target which is used for I/O operations + */ + readonly attribute nsIEventTarget cacheIOTarget; }; %{C++ diff --git a/netwerk/protocol/http/nsHttpChannel.cpp b/netwerk/protocol/http/nsHttpChannel.cpp index bf2230a3c743..156dcdffbe36 100644 --- a/netwerk/protocol/http/nsHttpChannel.cpp +++ b/netwerk/protocol/http/nsHttpChannel.cpp @@ -86,6 +86,8 @@ nsHttpChannel::nsHttpChannel() , mCacheAccess(0) , mPostID(0) , mRequestTime(0) + , mOnCacheEntryAvailableCallback(nsnull) + , mAsyncCacheOpen(PR_FALSE) , mStartPos(LL_MAXUINT) , mPendingAsyncCallOnResume(nsnull) , mSuspendCount(0) @@ -172,8 +174,6 @@ nsHttpChannel::Connect(PRBool firstTime) // true when called from AsyncOpen if (firstTime) { - PRBool delayed = PR_FALSE; - // are we offline? PRBool offline = gIOService->IsOffline(); if (offline) @@ -188,7 +188,7 @@ nsHttpChannel::Connect(PRBool firstTime) } // open a cache entry for this channel... - rv = OpenCacheEntry(offline, &delayed); + rv = OpenCacheEntry(); if (NS_FAILED(rv)) { LOG(("OpenCacheEntry failed [rv=%x]\n", rv)); @@ -212,7 +212,7 @@ nsHttpChannel::Connect(PRBool firstTime) if (NS_FAILED(rv)) return rv; } - if (NS_SUCCEEDED(rv) && delayed) + if (NS_SUCCEEDED(rv) && mAsyncCacheOpen) return NS_OK; } @@ -245,6 +245,14 @@ nsHttpChannel::Connect(PRBool firstTime) return NS_ERROR_DOCUMENT_NOT_CACHED; } } + else if (mLoadFlags & LOAD_ONLY_FROM_CACHE) { + // If we have a fallback URI (and we're not already + // falling back), process the fallback asynchronously. + if (!mFallbackChannel && !mFallbackKey.IsEmpty()) { + return AsyncCall(&nsHttpChannel::HandleAsyncFallback); + } + return NS_ERROR_DOCUMENT_NOT_CACHED; + } // check to see if authorization headers should be included mAuthProvider->AddAuthorizationHeaders(); @@ -1645,11 +1653,11 @@ IsSubRangeRequest(nsHttpRequestHead &aRequestHead) } nsresult -nsHttpChannel::OpenCacheEntry(PRBool offline, PRBool *delayed) +nsHttpChannel::OpenCacheEntry() { nsresult rv; - *delayed = PR_FALSE; + mAsyncCacheOpen = PR_FALSE; mLoadedFromApplicationCache = PR_FALSE; LOG(("nsHttpChannel::OpenCacheEntry [this=%p]", this)); @@ -1685,23 +1693,10 @@ nsHttpChannel::OpenCacheEntry(PRBool offline, PRBool *delayed) GenerateCacheKey(mPostID, cacheKey); - // Get a cache session with appropriate storage policy - nsCacheStoragePolicy storagePolicy = DetermineStoragePolicy(); - // Set the desired cache access mode accordingly... nsCacheAccessMode accessRequested; - if (offline || (mLoadFlags & INHIBIT_CACHING)) { - // If we have been asked to bypass the cache and not write to the - // cache, then don't use the cache at all. Unless we're actually - // offline, which takes precedence over BYPASS_LOCAL_CACHE. - if (BYPASS_LOCAL_CACHE(mLoadFlags) && !offline) - return NS_ERROR_NOT_AVAILABLE; - accessRequested = nsICache::ACCESS_READ; - } - else if (BYPASS_LOCAL_CACHE(mLoadFlags)) - accessRequested = nsICache::ACCESS_WRITE; // replace cache entry - else - accessRequested = nsICache::ACCESS_READ_WRITE; // normal browsing + rv = DetermineCacheAccess(&accessRequested); + if NS_FAILED(rv) return rv; if (!mApplicationCache && mInheritApplicationCache) { // Pick up an application cache from the notification @@ -1730,10 +1725,6 @@ nsHttpChannel::OpenCacheEntry(PRBool offline, PRBool *delayed) nsCOMPtr session; - // Will be set to true if we've found the right session, but need - // to open the cache entry asynchronously. - PRBool waitingForValidation = PR_FALSE; - // If we have an application cache, we check it first. if (mApplicationCache) { nsCAutoString appCacheClientID; @@ -1749,107 +1740,217 @@ nsHttpChannel::OpenCacheEntry(PRBool offline, PRBool *delayed) getter_AddRefs(session)); NS_ENSURE_SUCCESS(rv, rv); - // we'll try to synchronously open the cache entry... however, - // it may be in use and not yet validated, in which case we'll - // try asynchronously opening the cache entry. - // - // We open with ACCESS_READ only, because we don't want to - // overwrite the offline cache entry non-atomically. - // ACCESS_READ will prevent us from writing to the offline - // cache as a normal cache entry. - rv = session->OpenCacheEntry(cacheKey, - nsICache::ACCESS_READ, PR_FALSE, - getter_AddRefs(mCacheEntry)); - if (rv == NS_ERROR_CACHE_WAIT_FOR_VALIDATION) { - accessRequested = nsICache::ACCESS_READ; - waitingForValidation = PR_TRUE; - rv = NS_OK; + if (mLoadFlags & LOAD_BYPASS_LOCAL_CACHE_IF_BUSY) { + // must use synchronous open for LOAD_BYPASS_LOCAL_CACHE_IF_BUSY + rv = session->OpenCacheEntry(cacheKey, + nsICache::ACCESS_READ, PR_FALSE, + getter_AddRefs(mCacheEntry)); + if (NS_SUCCEEDED(rv)) { + mCacheEntry->GetAccessGranted(&mCacheAccess); + LOG(("nsHttpChannel::OpenCacheEntry [this=%p grantedAccess=%d]", + this, mCacheAccess)); + mLoadedFromApplicationCache = PR_TRUE; + return NS_OK; + } else if (rv == NS_ERROR_CACHE_WAIT_FOR_VALIDATION) { + LOG(("bypassing local cache since it is busy\n")); + // Don't try to load normal cache entry + return NS_ERROR_NOT_AVAILABLE; + } + } else { + mOnCacheEntryAvailableCallback = + &nsHttpChannel::OnOfflineCacheEntryAvailable; + // We open with ACCESS_READ only, because we don't want to + // overwrite the offline cache entry non-atomically. + // ACCESS_READ will prevent us from writing to the offline + // cache as a normal cache entry. + rv = session->AsyncOpenCacheEntry(cacheKey, + nsICache::ACCESS_READ, + this); + + if (NS_SUCCEEDED(rv)) { + mAsyncCacheOpen = PR_TRUE; + return NS_OK; + } } - if (NS_FAILED(rv) && !mCacheForOfflineUse && !mFallbackChannel) { - // Check for namespace match. - nsCOMPtr namespaceEntry; - rv = mApplicationCache->GetMatchingNamespace - (cacheKey, getter_AddRefs(namespaceEntry)); + // sync or async opening failed + return OnOfflineCacheEntryAvailable(nsnull, nsICache::ACCESS_NONE, + rv, PR_TRUE); + } + + return OpenNormalCacheEntry(PR_TRUE); +} + +nsresult +nsHttpChannel::OnOfflineCacheEntryAvailable(nsICacheEntryDescriptor *aEntry, + nsCacheAccessMode aAccess, + nsresult aEntryStatus, + PRBool aIsSync) +{ + nsresult rv; + + if (NS_SUCCEEDED(aEntryStatus)) { + // We successfully opened an offline cache session and the entry, + // so indicate we will load from the offline cache. + mLoadedFromApplicationCache = PR_TRUE; + mCacheEntry = aEntry; + mCacheAccess = aAccess; + } + + if (mCanceled && NS_FAILED(mStatus)) { + LOG(("channel was canceled [this=%p status=%x]\n", this, mStatus)); + return mStatus; + } + + if (NS_SUCCEEDED(aEntryStatus)) + // Called from OnCacheEntryAvailable, advance to the next state + return Connect(PR_FALSE); + + if (!mCacheForOfflineUse && !mFallbackChannel) { + nsCAutoString cacheKey; + GenerateCacheKey(mPostID, cacheKey); + + // Check for namespace match. + nsCOMPtr namespaceEntry; + rv = mApplicationCache->GetMatchingNamespace + (cacheKey, getter_AddRefs(namespaceEntry)); + if (NS_FAILED(rv) && !aIsSync) + return Connect(PR_FALSE); + NS_ENSURE_SUCCESS(rv, rv); + + PRUint32 namespaceType = 0; + if (!namespaceEntry || + NS_FAILED(namespaceEntry->GetItemType(&namespaceType)) || + (namespaceType & + (nsIApplicationCacheNamespace::NAMESPACE_FALLBACK | + nsIApplicationCacheNamespace::NAMESPACE_OPPORTUNISTIC | + nsIApplicationCacheNamespace::NAMESPACE_BYPASS)) == 0) { + // When loading from an application cache, only items + // on the whitelist or matching a + // fallback/opportunistic namespace should hit the + // network... + mLoadFlags |= LOAD_ONLY_FROM_CACHE; + + // ... and if there were an application cache entry, + // we would have found it earlier. + return aIsSync ? NS_ERROR_CACHE_KEY_NOT_FOUND : Connect(PR_FALSE); + } + + if (namespaceType & + nsIApplicationCacheNamespace::NAMESPACE_FALLBACK) { + rv = namespaceEntry->GetData(mFallbackKey); + if (NS_FAILED(rv) && !aIsSync) + return Connect(PR_FALSE); NS_ENSURE_SUCCESS(rv, rv); - - PRUint32 namespaceType = 0; - if (!namespaceEntry || - NS_FAILED(namespaceEntry->GetItemType(&namespaceType)) || - (namespaceType & - (nsIApplicationCacheNamespace::NAMESPACE_FALLBACK | - nsIApplicationCacheNamespace::NAMESPACE_OPPORTUNISTIC | - nsIApplicationCacheNamespace::NAMESPACE_BYPASS)) == 0) { - // When loading from an application cache, only items - // on the whitelist or matching a - // fallback/opportunistic namespace should hit the - // network... - mLoadFlags |= LOAD_ONLY_FROM_CACHE; - - // ... and if there were an application cache entry, - // we would have found it earlier. - return NS_ERROR_CACHE_KEY_NOT_FOUND; - } - - if (namespaceType & - nsIApplicationCacheNamespace::NAMESPACE_FALLBACK) { - rv = namespaceEntry->GetData(mFallbackKey); - NS_ENSURE_SUCCESS(rv, rv); - } - - if ((namespaceType & - nsIApplicationCacheNamespace::NAMESPACE_OPPORTUNISTIC) && - mLoadFlags & LOAD_DOCUMENT_URI) { - // Document loads for items in an opportunistic namespace - // should be placed in the offline cache. - nsCString clientID; - mApplicationCache->GetClientID(clientID); - - mCacheForOfflineUse = !clientID.IsEmpty(); - SetOfflineCacheClientID(clientID); - mCachingOpportunistically = PR_TRUE; - } } - else if (NS_SUCCEEDED(rv)) { - // We successfully opened an offline cache session and the entry, - // now indiciate we load from the offline cache. - mLoadedFromApplicationCache = PR_TRUE; + + if ((namespaceType & + nsIApplicationCacheNamespace::NAMESPACE_OPPORTUNISTIC) && + mLoadFlags & LOAD_DOCUMENT_URI) { + // Document loads for items in an opportunistic namespace + // should be placed in the offline cache. + nsCString clientID; + mApplicationCache->GetClientID(clientID); + + mCacheForOfflineUse = !clientID.IsEmpty(); + SetOfflineCacheClientID(clientID); + mCachingOpportunistically = PR_TRUE; } } - if (!mCacheEntry && !waitingForValidation) { - rv = gHttpHandler->GetCacheSession(storagePolicy, - getter_AddRefs(session)); - if (NS_FAILED(rv)) return rv; + return OpenNormalCacheEntry(aIsSync); +} + +nsresult +nsHttpChannel::OpenNormalCacheEntry(PRBool aIsSync) +{ + NS_ASSERTION(!mCacheEntry, "We have already mCacheEntry"); + + nsresult rv; + + nsCAutoString cacheKey; + GenerateCacheKey(mPostID, cacheKey); + + nsCacheStoragePolicy storagePolicy = DetermineStoragePolicy(); + + nsCOMPtr session; + rv = gHttpHandler->GetCacheSession(storagePolicy, + getter_AddRefs(session)); + if (NS_FAILED(rv)) return rv; + + nsCacheAccessMode accessRequested; + rv = DetermineCacheAccess(&accessRequested); + if NS_FAILED(rv) return rv; + + if (mLoadFlags & LOAD_BYPASS_LOCAL_CACHE_IF_BUSY) { + if (!aIsSync) { + // Unexpected state: we were called from OnCacheEntryAvailable(), + // so LOAD_BYPASS_LOCAL_CACHE_IF_BUSY shouldn't be set. Unless + // somebody altered mLoadFlags between OpenCacheEntry() and + // OnCacheEntryAvailable()... + NS_WARNING( + "OpenNormalCacheEntry() called from OnCacheEntryAvailable() " + "when LOAD_BYPASS_LOCAL_CACHE_IF_BUSY was specified"); + } + + // must use synchronous open for LOAD_BYPASS_LOCAL_CACHE_IF_BUSY rv = session->OpenCacheEntry(cacheKey, accessRequested, PR_FALSE, getter_AddRefs(mCacheEntry)); - if (rv == NS_ERROR_CACHE_WAIT_FOR_VALIDATION) { - waitingForValidation = PR_TRUE; - rv = NS_OK; + if (NS_SUCCEEDED(rv)) { + mCacheEntry->GetAccessGranted(&mCacheAccess); + LOG(("nsHttpChannel::OpenCacheEntry [this=%p grantedAccess=%d]", + this, mCacheAccess)); + } + else if (rv == NS_ERROR_CACHE_WAIT_FOR_VALIDATION) { + LOG(("bypassing local cache since it is busy\n")); + rv = NS_ERROR_NOT_AVAILABLE; + } + } + else { + mOnCacheEntryAvailableCallback = + &nsHttpChannel::OnNormalCacheEntryAvailable; + rv = session->AsyncOpenCacheEntry(cacheKey, accessRequested, this); + if (NS_SUCCEEDED(rv)) { + mAsyncCacheOpen = PR_TRUE; + return NS_OK; } - if (NS_FAILED(rv)) return rv; } - if (waitingForValidation) { - // access to the cache entry has been denied (because the - // cache entry is probably in use by another channel). - if (mLoadFlags & LOAD_BYPASS_LOCAL_CACHE_IF_BUSY) { - LOG(("bypassing local cache since it is busy\n")); - return NS_ERROR_NOT_AVAILABLE; - } - rv = session->AsyncOpenCacheEntry(cacheKey, accessRequested, this); - if (NS_FAILED(rv)) return rv; - // we'll have to wait for the cache entry - *delayed = PR_TRUE; - } - else if (NS_SUCCEEDED(rv)) { - mCacheEntry->GetAccessGranted(&mCacheAccess); - LOG(("nsHttpChannel::OpenCacheEntry [this=%p grantedAccess=%d]", this, mCacheAccess)); - } + if (!aIsSync) + // Called from OnCacheEntryAvailable, advance to the next state + rv = Connect(PR_FALSE); + return rv; } +nsresult +nsHttpChannel::OnNormalCacheEntryAvailable(nsICacheEntryDescriptor *aEntry, + nsCacheAccessMode aAccess, + nsresult aEntryStatus, + PRBool aIsSync) +{ + NS_ASSERTION(!aIsSync, "aIsSync should be false"); + + if (NS_SUCCEEDED(aEntryStatus)) { + mCacheEntry = aEntry; + mCacheAccess = aAccess; + } + + if (mCanceled && NS_FAILED(mStatus)) { + LOG(("channel was canceled [this=%p status=%x]\n", this, mStatus)); + return mStatus; + } + + if ((mLoadFlags & LOAD_ONLY_FROM_CACHE) && NS_FAILED(aEntryStatus)) + // if this channel is only allowed to pull from the cache, then + // we must fail if we were unable to open a cache entry. + return NS_ERROR_DOCUMENT_NOT_CACHED; + + // advance to the next state... + return Connect(PR_FALSE); +} + nsresult nsHttpChannel::OpenOfflineCacheEntryForWriting() @@ -2676,20 +2777,26 @@ nsHttpChannel::InstallCacheListener(PRUint32 offset) do_CreateInstance(kStreamListenerTeeCID, &rv); if (NS_FAILED(rv)) return rv; + nsCOMPtr serv = + do_GetService(NS_CACHESERVICE_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr cacheIOTarget; + serv->GetCacheIOTarget(getter_AddRefs(cacheIOTarget)); + nsCacheStoragePolicy policy; rv = mCacheEntry->GetStoragePolicy(&policy); - if (!gHttpHandler->mCacheWriteThread || - NS_FAILED(rv) || - policy == nsICache::STORE_ON_DISK_AS_FILE) { - LOG(("nsHttpChannel::InstallCacheListener sync tee %p\n", tee.get())); + if (NS_FAILED(rv) || policy == nsICache::STORE_ON_DISK_AS_FILE || + !cacheIOTarget) { + LOG(("nsHttpChannel::InstallCacheListener sync tee %p rv=%x policy=%d " + "cacheIOTarget=%p", tee.get(), rv, policy, cacheIOTarget.get())); rv = tee->Init(mListener, out, nsnull); } else { - LOG(("nsHttpChannel::InstallCacheListener async tee %p\n", - tee.get())); - rv = tee->InitAsync(mListener, gHttpHandler->mCacheWriteThread, out, nsnull); + LOG(("nsHttpChannel::InstallCacheListener async tee %p", tee.get())); + rv = tee->InitAsync(mListener, cacheIOTarget, out, nsnull); } - + if (NS_FAILED(rv)) return rv; mListener = tee; return NS_OK; @@ -4184,6 +4291,8 @@ nsHttpChannel::OnCacheEntryAvailable(nsICacheEntryDescriptor *entry, nsCacheAccessMode access, nsresult status) { + nsresult rv; + LOG(("nsHttpChannel::OnCacheEntryAvailable [this=%p entry=%p " "access=%x status=%x]\n", this, entry, access, status)); @@ -4192,27 +4301,12 @@ nsHttpChannel::OnCacheEntryAvailable(nsICacheEntryDescriptor *entry, if (!mIsPending) return NS_OK; - // otherwise, we have to handle this event. - if (NS_SUCCEEDED(status)) { - mCacheEntry = entry; - mCacheAccess = access; - } + nsOnCacheEntryAvailableCallback callback = mOnCacheEntryAvailableCallback; + mOnCacheEntryAvailableCallback = nsnull; - nsresult rv; - - if (mCanceled && NS_FAILED(mStatus)) { - LOG(("channel was canceled [this=%p status=%x]\n", this, mStatus)); - rv = mStatus; - } - else if ((mLoadFlags & LOAD_ONLY_FROM_CACHE) && NS_FAILED(status)) - // if this channel is only allowed to pull from the cache, then - // we must fail if we were unable to open a cache entry. - rv = NS_ERROR_DOCUMENT_NOT_CACHED; - else - // advance to the next state... - rv = Connect(PR_FALSE); - - // a failure from Connect means that we have to abort the channel. + NS_ASSERTION(callback, + "nsHttpChannel::OnCacheEntryAvailable called without callback"); + rv = ((*this).*callback)(entry, access, status, PR_FALSE); if (NS_FAILED(rv)) { CloseCacheEntry(PR_TRUE); AsyncAbort(rv); @@ -4675,6 +4769,27 @@ nsHttpChannel::DetermineStoragePolicy() return policy; } +nsresult +nsHttpChannel::DetermineCacheAccess(nsCacheAccessMode *_retval) +{ + PRBool offline = gIOService->IsOffline(); + + if (offline || (mLoadFlags & INHIBIT_CACHING)) { + // If we have been asked to bypass the cache and not write to the + // cache, then don't use the cache at all. Unless we're actually + // offline, which takes precedence over BYPASS_LOCAL_CACHE. + if (BYPASS_LOCAL_CACHE(mLoadFlags) && !offline) + return NS_ERROR_NOT_AVAILABLE; + *_retval = nsICache::ACCESS_READ; + } + else if (BYPASS_LOCAL_CACHE(mLoadFlags)) + *_retval = nsICache::ACCESS_WRITE; // replace cache entry + else + *_retval = nsICache::ACCESS_READ_WRITE; // normal browsing + + return NS_OK; +} + void nsHttpChannel::AsyncOnExamineCachedResponse() { diff --git a/netwerk/protocol/http/nsHttpChannel.h b/netwerk/protocol/http/nsHttpChannel.h index fa69d6562831..da77d0f919f3 100644 --- a/netwerk/protocol/http/nsHttpChannel.h +++ b/netwerk/protocol/http/nsHttpChannel.h @@ -217,7 +217,16 @@ private: nsresult ResolveProxy(); // cache specific methods - nsresult OpenCacheEntry(PRBool offline, PRBool *delayed); + nsresult OpenCacheEntry(); + nsresult OnOfflineCacheEntryAvailable(nsICacheEntryDescriptor *aEntry, + nsCacheAccessMode aAccess, + nsresult aResult, + PRBool aSync); + nsresult OpenNormalCacheEntry(PRBool aSync); + nsresult OnNormalCacheEntryAvailable(nsICacheEntryDescriptor *aEntry, + nsCacheAccessMode aAccess, + nsresult aResult, + PRBool aSync); nsresult OpenOfflineCacheEntryForWriting(); nsresult GenerateCacheKey(PRUint32 postID, nsACString &key); nsresult UpdateExpirationTime(); @@ -235,6 +244,7 @@ private: nsresult InstallOfflineCacheListener(); void MaybeInvalidateCacheEntryForSubsequentGet(); nsCacheStoragePolicy DetermineStoragePolicy(); + nsresult DetermineCacheAccess(nsCacheAccessMode *_retval); void AsyncOnExamineCachedResponse(); // Handle the bogus Content-Encoding Apache sometimes sends @@ -265,6 +275,11 @@ private: PRUint32 mPostID; PRUint32 mRequestTime; + typedef nsresult (nsHttpChannel:: *nsOnCacheEntryAvailableCallback)( + nsICacheEntryDescriptor *, nsCacheAccessMode, nsresult, PRBool); + nsOnCacheEntryAvailableCallback mOnCacheEntryAvailableCallback; + PRBool mAsyncCacheOpen; + nsCOMPtr mOfflineCacheEntry; nsCacheAccessMode mOfflineCacheAccess; nsCString mOfflineCacheClientID; diff --git a/netwerk/protocol/http/nsHttpHandler.cpp b/netwerk/protocol/http/nsHttpHandler.cpp index 3cc978e5786e..38fcd821efc4 100644 --- a/netwerk/protocol/http/nsHttpHandler.cpp +++ b/netwerk/protocol/http/nsHttpHandler.cpp @@ -291,14 +291,6 @@ nsHttpHandler::Init() rv = InitConnectionMgr(); if (NS_FAILED(rv)) return rv; - rv = NS_NewThread(getter_AddRefs(mCacheWriteThread)); - if (NS_FAILED(rv)) { - mCacheWriteThread = nsnull; - LOG(("Failed creating cache-write thread - writes will be synchronous")); - } else { - LOG(("Created cache-write thread = %p", mCacheWriteThread.get())); - } - nsCOMPtr appInfo = do_GetService("@mozilla.org/xre/app-info;1"); if (appInfo) @@ -318,7 +310,6 @@ nsHttpHandler::Init() mObserverService->AddObserver(this, "profile-change-net-restore", PR_TRUE); mObserverService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, PR_TRUE); mObserverService->AddObserver(this, "net:clear-active-logins", PR_TRUE); - mObserverService->AddObserver(this, "xpcom-shutdown-threads", PR_TRUE); } StartPruneDeadConnectionsTimer(); @@ -1773,18 +1764,6 @@ nsHttpHandler::Observe(nsISupports *subject, else if (strcmp(topic, "net:clear-active-logins") == 0) { mAuthCache.ClearAll(); } - else if (strcmp(topic, "xpcom-shutdown-threads") == 0) { - // Shutdown the cache write thread. This must be done after shutting down - // the cache service, because the (memory) cache entries' storage streams - // get released on the thread on which they were first written to, which - // is this thread. - if (mCacheWriteThread) { - LOG((" shutting down cache-write thread...\n")); - mCacheWriteThread->Shutdown(); - LOG((" cache-write thread shutdown complete\n")); - mCacheWriteThread = nsnull; - } - } return NS_OK; } diff --git a/netwerk/protocol/http/nsHttpHandler.h b/netwerk/protocol/http/nsHttpHandler.h index f56c1db2cc75..aec75a59bb77 100644 --- a/netwerk/protocol/http/nsHttpHandler.h +++ b/netwerk/protocol/http/nsHttpHandler.h @@ -210,8 +210,6 @@ public: static nsresult GenerateHostPort(const nsCString& host, PRInt32 port, nsCString& hostLine); - // The thread used to implement async cache-writes - nsCOMPtr mCacheWriteThread; private: //