diff --git a/modules/libpr0n/src/imgCache.cpp b/modules/libpr0n/src/imgCache.cpp index 2046638584fe..6781ceb5bc07 100644 --- a/modules/libpr0n/src/imgCache.cpp +++ b/modules/libpr0n/src/imgCache.cpp @@ -144,7 +144,7 @@ PRBool imgCache::Put(nsIURI *aKey, imgRequest *request, nsICacheEntryDescriptor nsCOMPtr entry; - rv = ses->OpenCacheEntry(spec, nsICache::ACCESS_WRITE, getter_AddRefs(entry)); + rv = ses->OpenCacheEntry(spec, nsICache::ACCESS_WRITE, nsICache::BLOCKING, getter_AddRefs(entry)); if (NS_FAILED(rv) || !entry) return PR_FALSE; @@ -175,7 +175,7 @@ PRBool imgCache::Get(nsIURI *aKey, imgRequest **aRequest, nsICacheEntryDescripto nsCOMPtr entry; - rv = ses->OpenCacheEntry(spec, nsICache::ACCESS_READ, getter_AddRefs(entry)); + rv = ses->OpenCacheEntry(spec, nsICache::ACCESS_READ, nsICache::BLOCKING, getter_AddRefs(entry)); if (NS_FAILED(rv) || !entry) return PR_FALSE; @@ -209,7 +209,7 @@ PRBool imgCache::Remove(nsIURI *aKey) nsCOMPtr entry; - rv = ses->OpenCacheEntry(spec, nsICache::ACCESS_READ, getter_AddRefs(entry)); + rv = ses->OpenCacheEntry(spec, nsICache::ACCESS_READ, nsICache::BLOCKING, getter_AddRefs(entry)); if (NS_FAILED(rv) || !entry) return PR_FALSE; diff --git a/netwerk/cache/public/nsICache.idl b/netwerk/cache/public/nsICache.idl index 1b743d526b14..3fff812ba46c 100644 --- a/netwerk/cache/public/nsICache.idl +++ b/netwerk/cache/public/nsICache.idl @@ -139,6 +139,18 @@ interface nsICache */ const long NOT_STREAM_BASED = 0; const long STREAM_BASED = 1; + + /** + * The synchronous OpenCacheEntry() may be blocking or non-blocking. If a cache entry is + * waiting to be validated by another cache descriptor (so no new cache descriptors for that + * key can be created, OpenCacheEntry() will return NS_ERROR_CACHE_WAIT_FOR_VALIDATION in + * non-blocking mode. In blocking mode, it will wait until the cache entry for the key has + * been validated or doomed. If the cache entry is validated, then a descriptor for that + * entry will be created and returned. If the cache entry was doomed, then a descriptor + * will be created for a new cache entry for the key. + */ + const long NON_BLOCKING = 0; + const long BLOCKING = 1; }; @@ -147,12 +159,12 @@ interface nsICache /** * Cache specific nsresult error codes */ -#define NS_ERROR_CACHE_KEY_NOT_FOUND NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_NETWORK, 60) -#define NS_ERROR_CACHE_DATA_IS_STREAM NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_NETWORK, 61) -#define NS_ERROR_CACHE_DATA_IS_NOT_STREAM NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_NETWORK, 62) -#define NS_ERROR_CACHE_WAIT_FOR_VALIDATION NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_NETWORK, 63) -#define NS_ERROR_CACHE_ENTRY_DOOMED NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_NETWORK, 64) -#define NS_ERROR_CACHE_READ_ACCESS_DENIED NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_NETWORK, 65) -#define NS_ERROR_CACHE_WRITE_ACCESS_DENIED NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_NETWORK, 66) +#define NS_ERROR_CACHE_KEY_NOT_FOUND NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_NETWORK, 61) +#define NS_ERROR_CACHE_DATA_IS_STREAM NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_NETWORK, 62) +#define NS_ERROR_CACHE_DATA_IS_NOT_STREAM NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_NETWORK, 63) +#define NS_ERROR_CACHE_WAIT_FOR_VALIDATION NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_NETWORK, 64) +#define NS_ERROR_CACHE_ENTRY_DOOMED NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_NETWORK, 65) +#define NS_ERROR_CACHE_READ_ACCESS_DENIED NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_NETWORK, 66) +#define NS_ERROR_CACHE_WRITE_ACCESS_DENIED NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_NETWORK, 67) %} diff --git a/netwerk/cache/public/nsICacheSession.idl b/netwerk/cache/public/nsICacheSession.idl index c8b41476a442..40d2ed257e83 100644 --- a/netwerk/cache/public/nsICacheSession.idl +++ b/netwerk/cache/public/nsICacheSession.idl @@ -50,10 +50,11 @@ interface nsICacheSession : nsISupports * Synchronous cache access. This returns a unique descriptor each * time it is called, even if the same key is specified. When * called by multiple threads for write access, only one writable - * descriptor will be granted. + * descriptor will be granted. If 'blocking' is set to false, */ nsICacheEntryDescriptor openCacheEntry(in string key, - in nsCacheAccessMode accessRequested); + in nsCacheAccessMode accessRequested, + in boolean blockingMode); /** * Asynchronous cache access. Does not block the calling thread. diff --git a/netwerk/cache/src/nsCacheEntry.cpp b/netwerk/cache/src/nsCacheEntry.cpp index 5673f2ffd20f..474828a7d73c 100644 --- a/netwerk/cache/src/nsCacheEntry.cpp +++ b/netwerk/cache/src/nsCacheEntry.cpp @@ -97,8 +97,9 @@ nsCacheEntry::~nsCacheEntry() } nsISupports * data = mData; - NS_ADDREF(data); - + NS_ADDREF(data); // this reference will be owned by the event + mData = nsnull; // release our reference before switching threads + PL_InitEvent(event, data, CacheElementReleaseEventHandler, diff --git a/netwerk/cache/src/nsCacheRequest.h b/netwerk/cache/src/nsCacheRequest.h index f502854fcc0a..0a291efcb5f9 100644 --- a/netwerk/cache/src/nsCacheRequest.h +++ b/netwerk/cache/src/nsCacheRequest.h @@ -41,6 +41,7 @@ private: nsCacheRequest( nsCString * key, nsICacheListener * listener, nsCacheAccessMode accessRequested, + PRBool blockingMode, nsCacheSession * session) : mKey(key), mInfo(0), @@ -54,6 +55,7 @@ private: SetStoragePolicy(session->StoragePolicy()); if (session->IsStreamBased()) MarkStreamBased(); if (session->WillDoomEntriesIfExpired()) MarkDoomEntriesIfExpired(); + if (blockingMode == nsICache::BLOCKING) MarkBlockingMode(); MarkWaitingForValidation(); } @@ -72,7 +74,8 @@ private: eStoragePolicyMask = 0x000000FF, eStreamBasedMask = 0x00000100, eDoomEntriesIfExpiredMask = 0x00001000, - eWaitingForValidationMask = 0x00010000, + eBlockingModeMask = 0x00010000, + eWaitingForValidationMask = 0x00100000, eAccessRequestedMask = 0xFF000000 }; @@ -92,8 +95,12 @@ private: PRBool IsStreamBased() { return (mInfo & eStreamBasedMask) != 0; } - void MarkDoomEntriesIfExpired() { mInfo |= eDoomEntriesIfExpiredMask; } - PRBool WillDoomEntriesIfExpired() { return (mInfo & eDoomEntriesIfExpiredMask); } + void MarkDoomEntriesIfExpired() { mInfo |= eDoomEntriesIfExpiredMask; } + PRBool WillDoomEntriesIfExpired() { return (mInfo & eDoomEntriesIfExpiredMask); } + + void MarkBlockingMode() { mInfo |= eBlockingModeMask; } + PRBool IsBlocking() { return (mInfo & eBlockingModeMask); } + PRBool IsNonBlocking() { return !(mInfo & eBlockingModeMask); } void SetStoragePolicy(nsCacheStoragePolicy policy) { diff --git a/netwerk/cache/src/nsCacheService.cpp b/netwerk/cache/src/nsCacheService.cpp index 48efb42bfa90..7ecc001ed19a 100644 --- a/netwerk/cache/src/nsCacheService.cpp +++ b/netwerk/cache/src/nsCacheService.cpp @@ -490,6 +490,7 @@ nsresult nsCacheService::CreateRequest(nsCacheSession * session, const char * clientKey, nsCacheAccessMode accessRequested, + PRBool blockingMode, nsICacheListener * listener, nsCacheRequest ** request) { @@ -504,7 +505,7 @@ nsCacheService::CreateRequest(nsCacheSession * session, if (mMaxKeyLength < key->Length()) mMaxKeyLength = key->Length(); // create request - *request = new nsCacheRequest(key, listener, accessRequested, session); + *request = new nsCacheRequest(key, listener, accessRequested, blockingMode, session); if (!*request) { delete key; return NS_ERROR_OUT_OF_MEMORY; @@ -514,19 +515,13 @@ nsCacheService::CreateRequest(nsCacheSession * session, // get the nsIEventQueue for the request's thread nsresult rv; -#if 0 - // XXX can we just keep a reference so we don't have to do this everytime? - NS_WITH_SERVICE(nsIEventQueueService, eventQService, kEventQueueServiceCID, &rv); - if (NS_FAILED(rv)) goto error; -#endif - rv = mEventQService->ResolveEventQueue(NS_CURRENT_EVENTQ, getter_AddRefs((*request)->mEventQ)); if (NS_FAILED(rv)) goto error; if (!(*request)->mEventQ) { - rv = NS_ERROR_UNEXPECTED; // XXX what is the right error? + rv = NS_ERROR_NOT_AVAILABLE; goto error; } @@ -547,12 +542,6 @@ nsCacheService::NotifyListener(nsCacheRequest * request, { nsresult rv; -#if 0 - // XXX can we hold onto the proxy object manager? - NS_WITH_SERVICE(nsIProxyObjectManager, proxyObjMgr, kProxyObjectManagerCID, &rv); - if (NS_FAILED(rv)) return rv; -#endif - nsCOMPtr listenerProxy; NS_ASSERTION(request->mEventQ, "no event queue for async request!"); rv = mProxyObjectManager->GetProxyForObject(request->mEventQ, @@ -567,7 +556,8 @@ nsCacheService::NotifyListener(nsCacheRequest * request, nsresult -nsCacheService::ProcessRequest(nsCacheRequest * request, +nsCacheService::ProcessRequest(nsCacheRequest * request, + PRBool calledFromOpenCacheEntry, nsICacheEntryDescriptor ** result) { // !!! must be called with mCacheServiceLock held !!! @@ -587,15 +577,16 @@ nsCacheService::ProcessRequest(nsCacheRequest * request, if (rv != NS_ERROR_CACHE_WAIT_FOR_VALIDATION) break; if (request->mListener) // async exits - validate, doom, or close will resume - return rv; + return rv; - // XXX allocate condvar for request if necessary - PR_Unlock(mCacheServiceLock); - rv = request->WaitForValidation(); - PR_Lock(mCacheServiceLock); + if (request->IsBlocking()) { + PR_Unlock(mCacheServiceLock); + rv = request->WaitForValidation(); + PR_Lock(mCacheServiceLock); + } PR_REMOVE_AND_INIT_LINK(request); - if (NS_FAILED(rv)) break; + if (NS_FAILED(rv)) break; // non-blocking mode returns WAIT_FOR_VALIDATION error // okay, we're ready to process this request, request access again } if (rv != NS_ERROR_CACHE_ENTRY_DOOMED) break; @@ -613,6 +604,10 @@ nsCacheService::ProcessRequest(nsCacheRequest * request, rv = entry->CreateDescriptor(request, accessGranted, getter_AddRefs(descriptor)); if (request->mListener) { // Asynchronous + + if (NS_FAILED(rv) && calledFromOpenCacheEntry) + return rv; // skip notifying listener, just return rv to caller + // call listener to report error or descriptor nsresult rv2 = NotifyListener(request, descriptor, accessGranted, rv); if (NS_FAILED(rv2) && NS_SUCCEEDED(rv)) { @@ -629,6 +624,7 @@ nsresult nsCacheService::OpenCacheEntry(nsCacheSession * session, const char * key, nsCacheAccessMode accessRequested, + PRBool blockingMode, nsICacheListener * listener, nsICacheEntryDescriptor ** result) { @@ -638,11 +634,16 @@ nsCacheService::OpenCacheEntry(nsCacheSession * session, nsCacheRequest * request = nsnull; - nsresult rv = CreateRequest(session, key, accessRequested, listener, &request); + nsAutoLock lock(mCacheServiceLock); + nsresult rv = CreateRequest(session, + key, + accessRequested, + blockingMode, + listener, + &request); if (NS_FAILED(rv)) return rv; - nsAutoLock lock(mCacheServiceLock); - rv = ProcessRequest(request, result); + rv = ProcessRequest(request, PR_TRUE, result); // delete requests that have completed if (!(listener && (rv == NS_ERROR_CACHE_WAIT_FOR_VALIDATION))) @@ -1019,7 +1020,7 @@ nsCacheService::ProcessPendingRequests(nsCacheEntry * entry) PR_REMOVE_AND_INIT_LINK(request); if (entry->IsDoomed()) { - rv = ProcessRequest(request, nsnull); + rv = ProcessRequest(request, PR_FALSE, nsnull); if (rv == NS_ERROR_CACHE_WAIT_FOR_VALIDATION) rv = NS_OK; else diff --git a/netwerk/cache/src/nsCacheService.h b/netwerk/cache/src/nsCacheService.h index 4d1c0e42797b..2bfcfbf672c3 100644 --- a/netwerk/cache/src/nsCacheService.h +++ b/netwerk/cache/src/nsCacheService.h @@ -66,6 +66,7 @@ public: nsresult OpenCacheEntry(nsCacheSession * session, const char * key, nsCacheAccessMode accessRequested, + PRBool blockingMode, nsICacheListener * listener, nsICacheEntryDescriptor ** result); @@ -120,6 +121,7 @@ private: nsresult CreateRequest(nsCacheSession * session, const char * clientKey, nsCacheAccessMode accessRequested, + PRBool blockingMode, nsICacheListener * listener, nsCacheRequest ** request); @@ -136,7 +138,8 @@ private: void DeactivateEntry(nsCacheEntry * entry); - nsresult ProcessRequest(nsCacheRequest * request, + nsresult ProcessRequest(nsCacheRequest * request, + PRBool calledFromOpenCacheEntry, nsICacheEntryDescriptor ** result); nsresult ProcessPendingRequests(nsCacheEntry * entry); diff --git a/netwerk/cache/src/nsCacheSession.cpp b/netwerk/cache/src/nsCacheSession.cpp index 67193243c008..910e7789d7c0 100644 --- a/netwerk/cache/src/nsCacheSession.cpp +++ b/netwerk/cache/src/nsCacheSession.cpp @@ -67,13 +67,15 @@ NS_IMETHODIMP nsCacheSession::SetDoomEntriesIfExpired(PRBool doomEntriesIfExpire NS_IMETHODIMP nsCacheSession::OpenCacheEntry(const char * key, - nsCacheAccessMode accessRequested, + nsCacheAccessMode accessRequested, + PRBool blockingMode, nsICacheEntryDescriptor ** result) { nsresult rv; rv = nsCacheService::GlobalInstance()->OpenCacheEntry(this, key, accessRequested, + blockingMode, nsnull, // no listener result); return rv; @@ -88,6 +90,7 @@ NS_IMETHODIMP nsCacheSession::AsyncOpenCacheEntry(const char *key, rv = nsCacheService::GlobalInstance()->OpenCacheEntry(this, key, accessRequested, + nsICache::BLOCKING, listener, nsnull); // no result