Bug 549767 - Write/flush cache metadata off main-thread. r=michal.novotny
authorBjarne <bjarne@runitsoft.com>
Sun, 17 Apr 2011 19:31:15 +0200
changeset 68224 1346f7ada9d59944701397419d7399f98c3d9c51
parent 68223 2e7f1fb3ce4c1bf7fcd60dbbeab1112a14c4c502
child 68225 56d482ecbbc6bd28ef8e230e4cd691f347e56bcf
push id19547
push userdgottwald@mozilla.com
push dateSun, 17 Apr 2011 17:32:02 +0000
treeherdermozilla-central@1346f7ada9d5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmichal
bugs549767
milestone6.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 549767 - Write/flush cache metadata off main-thread. r=michal.novotny
netwerk/cache/nsDiskCacheBinding.cpp
netwerk/cache/nsDiskCacheBinding.h
netwerk/cache/nsDiskCacheDevice.cpp
netwerk/cache/nsDiskCacheDevice.h
--- a/netwerk/cache/nsDiskCacheBinding.cpp
+++ b/netwerk/cache/nsDiskCacheBinding.cpp
@@ -112,16 +112,17 @@ GetCacheEntryBinding(nsCacheEntry * entr
  *  nsDiskCacheBinding
  *****************************************************************************/
 
 NS_IMPL_THREADSAFE_ISUPPORTS0(nsDiskCacheBinding)
 
 nsDiskCacheBinding::nsDiskCacheBinding(nsCacheEntry* entry, nsDiskCacheRecord * record)
     :   mCacheEntry(entry)
     ,   mStreamIO(nsnull)
+    ,   mDeactivateEvent(nsnull)
 {
     NS_ASSERTION(record->ValidRecord(), "bad record");
     PR_INIT_CLIST(this);
     mRecord     = *record;
     mDoomed     = entry->IsDoomed();
     mGeneration = record->Generation();    // 0 == uninitialized, or data & meta using block files
 }
 
--- a/netwerk/cache/nsDiskCacheBinding.h
+++ b/netwerk/cache/nsDiskCacheBinding.h
@@ -57,16 +57,18 @@
  *  nsDiskCacheBinding
  *
  *  Created for disk cache specific data and stored in nsCacheEntry.mData as
  *  an nsISupports.  Also stored in nsDiskCacheHashTable, with collisions
  *  linked by the PRCList.
  *
  *****************************************************************************/
 
+class nsDiskCacheDeviceDeactivateEntryEvent;
+
 class nsDiskCacheBinding : public nsISupports, public PRCList {
 public:
     NS_DECL_ISUPPORTS
 
     nsDiskCacheBinding(nsCacheEntry* entry, nsDiskCacheRecord * record);
     virtual ~nsDiskCacheBinding();
 
     nsresult EnsureStreamIO();
@@ -74,16 +76,22 @@ public:
 
 // XXX make friends
 public:
     nsCacheEntry*           mCacheEntry;    // back pointer to parent nsCacheEntry
     nsDiskCacheRecord       mRecord;
     nsDiskCacheStreamIO*    mStreamIO;      // strong reference
     PRBool                  mDoomed;        // record is not stored in cache map
     PRUint8                 mGeneration;    // possibly just reservation
+
+    // If set, points to a pending event which will deactivate |mCacheEntry|.
+    // If not set then either |mCacheEntry| is not deactivated, or it has been
+    // deactivated but the device returned it from FindEntry() before the event
+    // fired. In both two latter cases this binding is to be considered valid.
+    nsDiskCacheDeviceDeactivateEntryEvent *mDeactivateEvent;
 };
 
 
 /******************************************************************************
  *  Utility Functions
  *****************************************************************************/
 
 nsDiskCacheBinding *   GetCacheEntryBinding(nsCacheEntry * entry);
--- a/netwerk/cache/nsDiskCacheDevice.cpp
+++ b/netwerk/cache/nsDiskCacheDevice.cpp
@@ -75,16 +75,17 @@
 #include "nsReadableUtils.h"
 #include "nsIInputStream.h"
 #include "nsIOutputStream.h"
 #include "nsCRT.h"
 #include "nsCOMArray.h"
 #include "nsISimpleEnumerator.h"
 
 #include "mozilla/FunctionTimer.h"
+#include "nsThreadUtils.h"
 
 static const char DISK_CACHE_DEVICE_ID[] = { "disk" };
 
 
 /******************************************************************************
  *  nsDiskCacheEvictor
  *
  *  Helper class for nsDiskCacheDevice.
@@ -445,16 +446,48 @@ nsDiskCacheDevice::Shutdown_Private(PRBo
 
 
 const char *
 nsDiskCacheDevice::GetDeviceID()
 {
     return DISK_CACHE_DEVICE_ID;
 }
 
+class nsDiskCacheDeviceDeactivateEntryEvent : public nsRunnable {
+public:
+    nsDiskCacheDeviceDeactivateEntryEvent(nsDiskCacheDevice *device,
+                                          nsCacheEntry * entry,
+                                          nsDiskCacheBinding * binding)
+        : mCanceled(PR_FALSE),
+          mEntry(entry),
+          mDevice(device),
+          mBinding(binding)
+    {
+    }
+
+    NS_IMETHOD Run()
+    {
+        nsCacheServiceAutoLock lock;
+#ifdef PR_LOGGING
+        CACHE_LOG_DEBUG(("nsDiskCacheDeviceDeactivateEntryEvent[%p]\n", this));
+#endif
+        if (!mCanceled) {
+            (void) mDevice->DeactivateEntry_Private(mEntry, mBinding);
+        }
+        return NS_OK;
+    }
+    
+    void CancelEvent() { mCanceled = PR_TRUE; }
+private:
+    PRBool mCanceled;
+    nsCacheEntry *mEntry;
+    nsDiskCacheDevice *mDevice;
+    nsDiskCacheBinding *mBinding;
+};
+
 
 /**
  *  FindEntry -
  *
  *      cases:  key not in disk cache, hash number free
  *              key not in disk cache, hash number used
  *              key in disk cache
  *
@@ -469,16 +502,26 @@ nsDiskCacheDevice::FindEntry(nsCString *
     PLDHashNumber           hashNumber = nsDiskCache::Hash(key->get());
 
     *collision = PR_FALSE;
 
     binding = mBindery.FindActiveBinding(hashNumber);
     if (binding && !binding->mCacheEntry->Key()->Equals(*key)) {
         *collision = PR_TRUE;
         return nsnull;
+    } else if (binding && binding->mDeactivateEvent) {
+        binding->mDeactivateEvent->CancelEvent();
+        binding->mDeactivateEvent = nsnull;
+        CACHE_LOG_DEBUG(("CACHE: reusing deactivated entry %p " \
+                         "req-key=%s  entry-key=%s\n",
+                         binding->mCacheEntry, key, binding->mCacheEntry->Key()));
+
+        return binding->mCacheEntry; // just return this one, observing that
+                                     // FindActiveBinding() does not return
+                                     // bindings to doomed entries
     }
     binding = nsnull;
 
     // lookup hash number in cache map
     nsresult rv = mCacheMap.FindRecord(hashNumber, &record);
     if (NS_FAILED(rv))  return nsnull;  // XXX log error?
     
     nsDiskCacheEntry * diskEntry = mCacheMap.ReadDiskCacheEntry(&record);
@@ -506,22 +549,42 @@ nsDiskCacheDevice::FindEntry(nsCString *
 /**
  *  NOTE: called while holding the cache service lock
  */
 nsresult
 nsDiskCacheDevice::DeactivateEntry(nsCacheEntry * entry)
 {
     nsresult              rv = NS_OK;
     nsDiskCacheBinding * binding = GetCacheEntryBinding(entry);
-    NS_ASSERTION(binding, "DeactivateEntry: binding == nsnull");
-    if (!binding)  return NS_ERROR_UNEXPECTED;
+    if (!IsValidBinding(binding))
+        return NS_ERROR_UNEXPECTED;
 
     CACHE_LOG_DEBUG(("CACHE: disk DeactivateEntry [%p %x]\n",
         entry, binding->mRecord.HashNumber()));
 
+    nsDiskCacheDeviceDeactivateEntryEvent *event =
+        new nsDiskCacheDeviceDeactivateEntryEvent(this, entry, binding);
+
+    // ensure we can cancel the event via the binding later if necessary
+    binding->mDeactivateEvent = event;
+
+    rv = nsCacheService::DispatchToCacheIOThread(event);
+    NS_ASSERTION(NS_SUCCEEDED(rv), "DeactivateEntry: Failed dispatching "
+                                   "deactivation event");
+    return NS_OK;
+}
+
+/**
+ *  NOTE: called while holding the cache service lock
+ */
+nsresult
+nsDiskCacheDevice::DeactivateEntry_Private(nsCacheEntry * entry,
+                                           nsDiskCacheBinding * binding)
+{
+    nsresult rv = NS_OK;
     if (entry->IsDoomed()) {
         // delete data, entry, record from disk for entry
         rv = mCacheMap.DeleteStorage(&binding->mRecord);
 
     } else {
         // save stuff to disk for entry
         rv = mCacheMap.WriteDiskCacheEntry(binding);
         if (NS_FAILED(rv)) {
@@ -639,17 +702,18 @@ nsDiskCacheDevice::BindEntry(nsCacheEntr
  */
 void
 nsDiskCacheDevice::DoomEntry(nsCacheEntry * entry)
 {
     CACHE_LOG_DEBUG(("CACHE: disk DoomEntry [%p]\n", entry));
 
     nsDiskCacheBinding * binding = GetCacheEntryBinding(entry);
     NS_ASSERTION(binding, "DoomEntry: binding == nsnull");
-    if (!binding)  return;
+    if (!binding)
+        return;
 
     if (!binding->mDoomed) {
         // so it can't be seen by FindEntry() ever again.
 #ifdef DEBUG
         nsresult rv =
 #endif
             mCacheMap.DeleteRecord(&binding->mRecord);
         NS_ASSERTION(NS_SUCCEEDED(rv),"DeleteRecord failed.");
@@ -670,18 +734,19 @@ nsDiskCacheDevice::OpenInputStreamForEnt
     CACHE_LOG_DEBUG(("CACHE: disk OpenInputStreamForEntry [%p %x %u]\n",
         entry, mode, offset));
 
     NS_ENSURE_ARG_POINTER(entry);
     NS_ENSURE_ARG_POINTER(result);
 
     nsresult             rv;
     nsDiskCacheBinding * binding = GetCacheEntryBinding(entry);
-    NS_ENSURE_TRUE(binding, NS_ERROR_UNEXPECTED);
-    
+    if (!IsValidBinding(binding))
+        return NS_ERROR_UNEXPECTED;
+
     NS_ASSERTION(binding->mCacheEntry == entry, "binding & entry don't point to each other");
 
     rv = binding->EnsureStreamIO();
     if (NS_FAILED(rv)) return rv;
 
     return binding->mStreamIO->GetInputStream(offset, result);
 }
 
@@ -698,17 +763,18 @@ nsDiskCacheDevice::OpenOutputStreamForEn
     CACHE_LOG_DEBUG(("CACHE: disk OpenOutputStreamForEntry [%p %x %u]\n",
         entry, mode, offset));
  
     NS_ENSURE_ARG_POINTER(entry);
     NS_ENSURE_ARG_POINTER(result);
 
     nsresult             rv;
     nsDiskCacheBinding * binding = GetCacheEntryBinding(entry);
-    NS_ENSURE_TRUE(binding, NS_ERROR_UNEXPECTED);
+    if (!IsValidBinding(binding))
+        return NS_ERROR_UNEXPECTED;
     
     NS_ASSERTION(binding->mCacheEntry == entry, "binding & entry don't point to each other");
 
     rv = binding->EnsureStreamIO();
     if (NS_FAILED(rv)) return rv;
 
     return binding->mStreamIO->GetOutputStream(offset, result);
 }
@@ -722,21 +788,19 @@ nsDiskCacheDevice::GetFileForEntry(nsCac
                                    nsIFile **        result)
 {
     NS_ENSURE_ARG_POINTER(result);
     *result = nsnull;
 
     nsresult             rv;
         
     nsDiskCacheBinding * binding = GetCacheEntryBinding(entry);
-    if (!binding) {
-        NS_WARNING("GetFileForEntry: binding == nsnull");
+    if (!IsValidBinding(binding))
         return NS_ERROR_UNEXPECTED;
-    }
-    
+
     // check/set binding->mRecord for separate file, sync w/mCacheMap
     if (binding->mRecord.DataLocationInitialized()) {
         if (binding->mRecord.DataFile() != 0)
             return NS_ERROR_NOT_AVAILABLE;  // data not stored as separate file
 
         NS_ASSERTION(binding->mRecord.DataFileGeneration() == binding->mGeneration, "error generations out of sync");
     } else {
         binding->mRecord.SetDataFileGeneration(binding->mGeneration);
@@ -771,18 +835,18 @@ nsDiskCacheDevice::OnDataSizeChange(nsCa
     CACHE_LOG_DEBUG(("CACHE: disk OnDataSizeChange [%p %d]\n",
         entry, deltaSize));
 
     // If passed a negative value, then there's nothing to do.
     if (deltaSize < 0)
         return NS_OK;
 
     nsDiskCacheBinding * binding = GetCacheEntryBinding(entry);
-    NS_ASSERTION(binding, "OnDataSizeChange: binding == nsnull");
-    if (!binding)  return NS_ERROR_UNEXPECTED;
+    if (!IsValidBinding(binding))
+        return NS_ERROR_UNEXPECTED;
 
     NS_ASSERTION(binding->mRecord.ValidRecord(), "bad record");
 
     PRUint32  newSize = entry->DataSize() + deltaSize;
     PRUint32  newSizeK =  ((newSize + 0x3FF) >> 10);
 
     // If the new size is larger than max. file size or larger than
     // 1/8 the cache capacity (which is in KiB's), and the entry has
--- a/netwerk/cache/nsDiskCacheDevice.h
+++ b/netwerk/cache/nsDiskCacheDevice.h
@@ -100,23 +100,34 @@ public:
     void                    getCacheDirectory(nsILocalFile ** result);
     PRUint32                getCacheCapacity();
     PRUint32                getCacheSize();
     PRUint32                getEntryCount();
     
     nsDiskCacheMap *        CacheMap()    { return &mCacheMap; }
     
 private:    
+    friend class nsDiskCacheDeviceDeactivateEntryEvent;
     /**
      *  Private methods
      */
 
+    inline bool IsValidBinding(nsDiskCacheBinding *binding)
+    {
+        NS_ASSERTION(binding, "  binding == nsnull");
+        NS_ASSERTION(binding->mDeactivateEvent == nsnull,
+                     "  entry in process of deactivation");
+        return (binding && !binding->mDeactivateEvent);
+    }
+
     PRBool                  Initialized() { return mInitialized; }
 
     nsresult                Shutdown_Private(PRBool flush);
+    nsresult                DeactivateEntry_Private(nsCacheEntry * entry,
+                                                    nsDiskCacheBinding * binding);
 
     nsresult                OpenDiskCache();
     nsresult                ClearDiskCache();
 
     nsresult                EvictDiskCacheEntries(PRUint32  targetCapacity);
     
     /**
      *  Member variables