zipreader-cache
author Benjamin Smedberg <benjamin@smedbergs.us>
Sat, 26 Jul 2008 22:49:39 -0400
changeset 167 a4da40849f5436e629c5732f4368c6c48189637f
parent 83 7a9dda4b3dc53f333d2413af2b9cab15c58eb6d6
permissions -rw-r--r--
State as of now

diff --git a/modules/libjar/nsJAR.cpp b/modules/libjar/nsJAR.cpp
--- a/modules/libjar/nsJAR.cpp
+++ b/modules/libjar/nsJAR.cpp
@@ -88,7 +88,7 @@ public:
   char*               storedEntryDigest;
 
   nsJARManifestItem();
-  virtual ~nsJARManifestItem();
+  ~nsJARManifestItem();
 };
 
 //-------------------------------------------------
@@ -112,17 +112,9 @@ nsJARManifestItem::~nsJARManifestItem()
 //----------------------------------------------
 // nsJAR constructor/destructor
 //----------------------------------------------
-PR_STATIC_CALLBACK(PRBool)
-DeleteManifestEntry(nsHashKey* aKey, void* aData, void* closure)
-{
-//-- deletes an entry in  mManifestData.
-  delete (nsJARManifestItem*)aData;
-  return PR_TRUE;
-}
 
 // The following initialization makes a guess of 10 entries per jarfile.
-nsJAR::nsJAR(): mManifestData(nsnull, nsnull, DeleteManifestEntry, nsnull, 10),
-                mParsedManifest(PR_FALSE),
+nsJAR::nsJAR(): mParsedManifest(PR_FALSE),
                 mGlobalStatus(JAR_MANIFEST_NOT_PARSED),
                 mReleaseTime(PR_INTERVAL_NO_TIMEOUT), 
                 mCache(nsnull), 
@@ -133,7 +125,6 @@ nsJAR::nsJAR(): mManifestData(nsnull, ns
 
 nsJAR::~nsJAR()
 {
-  mCache->ReleaseZip(this);
   Close();
 }
 
@@ -166,7 +157,6 @@ nsJAR::GetFile(nsIFile* *result)
 nsJAR::GetFile(nsIFile* *result)
 {
   *result = mZipFile;
-  NS_IF_ADDREF(*result);
   return NS_OK;
 }
 
@@ -199,7 +189,7 @@ nsJAR::Extract(const char *zipEntry, nsI
   nsAutoLock lock(mLock);
 
   nsresult rv;
-  nsCOMPtr<nsILocalFile> localFile = do_QueryInterface(outFile, &rv);
+  nsILocalFile* localFile = do_QueryInterface(outFile, &rv);
   if (NS_FAILED(rv)) return rv;
 
   nsZipItem *item = mZip.GetItem(zipEntry);
@@ -262,7 +252,7 @@ nsJAR::GetEntry(const char *aEntryName, 
   nsJARItem* jarItem = new nsJARItem(zipItem);
   NS_ENSURE_TRUE(jarItem, NS_ERROR_OUT_OF_MEMORY);
 
-  NS_ADDREF(*result = jarItem);
+  *result = jarItem;
   return NS_OK;
 }
 
@@ -288,7 +278,7 @@ nsJAR::FindEntries(const char *aPattern,
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
-  NS_ADDREF(*result = zipEnum);
+  *result = zipEnum;
   return NS_OK;
 }
 
@@ -315,7 +305,7 @@ nsJAR::GetInputStreamWithSpec(const nsAC
   nsJARInputStream* jis = new nsJARInputStream();
   // addref now so we can call InitFile/InitDirectory()
   NS_ENSURE_TRUE(jis, NS_ERROR_OUT_OF_MEMORY);
-  NS_ADDREF(*result = jis);
+  *result = jis;
 
   nsresult rv = NS_OK;
   if (!item || item->isDirectory) {
@@ -353,7 +343,7 @@ nsJAR::GetCertificatePrincipal(const cha
 
   //-- Get the signature verifier service
   nsresult rv;
-  nsCOMPtr<nsISignatureVerifier> verifier = 
+  nsISignatureVerifier* verifier = 
            do_GetService(SIGNATURE_VERIFIER_CONTRACTID, &rv);
   if (NS_FAILED(rv)) // No signature verifier available
     return NS_OK;
@@ -368,10 +358,10 @@ nsJAR::GetCertificatePrincipal(const cha
   if (aFilename)
   {
     //-- Find the item
-    nsCStringKey key(aFilename);
-    nsJARManifestItem* manItem = static_cast<nsJARManifestItem*>(mManifestData.Get(&key));
-    if (!manItem)
+    nsJARManifestItem* manItem;
+    if (!mManifestData.Get(nsDependentCString(aFilename), &manItem))
       return NS_OK;
+
     //-- Verify the item against the manifest
     if (!manItem->entryVerified)
     {
@@ -392,7 +382,6 @@ nsJAR::GetCertificatePrincipal(const cha
   else // Valid signature
   {
     *aPrincipal = mPrincipal;
-    NS_IF_ADDREF(*aPrincipal);
   }
   return NS_OK;
 }
@@ -416,7 +405,7 @@ nsJAR::OpenFile()
 nsJAR::OpenFile()
 {
   nsresult rv;
-  nsCOMPtr<nsILocalFile> localFile = do_QueryInterface(mZipFile, &rv);
+  nsILocalFile* localFile = do_QueryInterface(mZipFile, &rv);
   if (NS_FAILED(rv)) return nsnull;
 
   PRFileDesc* fd;
@@ -434,8 +423,8 @@ nsJAR::LoadEntry(const char* aFilename, 
 {
   //-- Get a stream for reading the file
   nsresult rv;
-  nsCOMPtr<nsIInputStream> manifestStream;
-  rv = GetInputStream(aFilename, getter_AddRefs(manifestStream));
+  nsIInputStream* manifestStream = nsnull;
+  rv = GetInputStream(aFilename, &manifestStream);
   if (NS_FAILED(rv)) return NS_ERROR_FILE_TARGET_DOES_NOT_EXIST;
   
   //-- Read the manifest file into memory
@@ -506,8 +495,8 @@ nsJAR::ParseManifest(nsISignatureVerifie
   if (mParsedManifest)
     return NS_OK;
   //-- (1)Manifest (MF) file
-  nsCOMPtr<nsIUTF8StringEnumerator> files;
-  nsresult rv = FindEntries(JAR_MF_SEARCH_STRING, getter_AddRefs(files));
+  nsIUTF8StringEnumerator* files = nsnull;
+  nsresult rv = FindEntries(JAR_MF_SEARCH_STRING, &files);
   if (!files) rv = NS_ERROR_FAILURE;
   if (NS_FAILED(rv)) return rv;
 
@@ -546,7 +535,7 @@ nsJAR::ParseManifest(nsISignatureVerifie
 
   //-- (2)Signature (SF) file
   // If there are multiple signatures, we select one.
-  rv = FindEntries(JAR_SF_SEARCH_STRING, getter_AddRefs(files));
+  rv = FindEntries(JAR_SF_SEARCH_STRING, &files);
   if (!files) rv = NS_ERROR_FAILURE;
   if (NS_FAILED(rv)) return rv;
   //-- Get an SF file
@@ -669,8 +658,7 @@ nsJAR::ParseOneFile(const char* filebuf,
                 curItemMF->mType = JAR_INVALID;
             }
             //-- Check for duplicates
-            nsCStringKey key(curItemName);
-            if (mManifestData.Exists(&key))
+            if (mManifestData.Get(curItemName, nsnull))
               curItemMF->mType = JAR_INVALID;
           }
         }
@@ -683,8 +671,7 @@ nsJAR::ParseOneFile(const char* filebuf,
           CalculateDigest(sectionStart, sectionLength,
                           &(curItemMF->calculatedSectionDigest));
           //-- Save item in the hashtable
-          nsCStringKey itemKey(curItemName);
-          mManifestData.Put(&itemKey, (void*)curItemMF);
+          mManifestData.Put(curItemName, curItemMF);
         }
         if (nextLineStart == nsnull) // end-of-file
           break;
@@ -700,8 +687,7 @@ nsJAR::ParseOneFile(const char* filebuf,
         if (foundName)
         {
           nsJARManifestItem* curItemSF;
-          nsCStringKey key(curItemName);
-          curItemSF = (nsJARManifestItem*)mManifestData.Get(&key);
+          mManifestData.Get(curItemName, &curItemSF);
           if(curItemSF)
           {
             NS_ASSERTION(curItemSF->status == JAR_NOT_SIGNED,
@@ -854,7 +840,7 @@ void nsJAR::ReportError(const char* aFil
   }
   
   // Report error in JS console
-  nsCOMPtr<nsIConsoleService> console(do_GetService("@mozilla.org/consoleservice;1"));
+  nsIConsoleService* console(do_GetService("@mozilla.org/consoleservice;1"));
   if (console)
   {
     console->LogStringMessage(message.get());
@@ -875,7 +861,7 @@ nsresult nsJAR::CalculateDigest(const ch
   nsresult rv;
   
 
-  nsCOMPtr<nsICryptoHash> hasher = do_CreateInstance("@mozilla.org/security/hash;1", &rv);
+  nsICryptoHash* hasher = do_CreateInstance("@mozilla.org/security/hash;1", &rv);
   if (NS_FAILED(rv)) return rv;
 
   rv = hasher->Init(nsICryptoHash::SHA1);
@@ -1039,14 +1025,13 @@ NS_IMPL_THREADSAFE_ISUPPORTS3(nsZipReade
 NS_IMPL_THREADSAFE_ISUPPORTS3(nsZipReaderCache, nsIZipReaderCache, nsIObserver, nsISupportsWeakReference)
 
 nsZipReaderCache::nsZipReaderCache()
-  : mLock(nsnull),
-    mZips(16)
+  : MMgc::GCCallback(NS_GetGC())
+  , mLock(nsnull)
 #ifdef ZIP_CACHE_HIT_RATE
-    ,
-    mZipCacheLookups(0),
-    mZipCacheHits(0),
-    mZipCacheFlushes(0),
-    mZipSyncMisses(0)
+  , mZipCacheLookups(0)
+  , mZipCacheHits(0)
+  , mZipCacheFlushes(0)
+  , mZipSyncMisses(0)
 #endif
 {
 }
@@ -1054,10 +1039,13 @@ NS_IMETHODIMP
 NS_IMETHODIMP
 nsZipReaderCache::Init(PRUint32 cacheSize)
 {
+  if (!mZips.Init(16))
+    return NS_ERROR_FAILURE;
+
   mCacheSize = cacheSize; 
   
 // Register as a memory pressure observer 
-  nsCOMPtr<nsIObserverService> os = 
+  nsIObserverService* os = 
            do_GetService("@mozilla.org/observer-service;1");
   if (os)
   {
@@ -1070,19 +1058,10 @@ nsZipReaderCache::Init(PRUint32 cacheSiz
   return mLock ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
 }
 
-static PRBool PR_CALLBACK
-DropZipReaderCache(nsHashKey *aKey, void *aData, void* closure)
-{
-  nsJAR* zip = (nsJAR*)aData;
-  zip->SetZipReaderCache(nsnull);
-  return PR_TRUE;
-}
-
 nsZipReaderCache::~nsZipReaderCache()
 {
   if (mLock)
     PR_DestroyLock(mLock);
-  mZips.Enumerate(DropZipReaderCache, nsnull);
 
 #ifdef ZIP_CACHE_HIT_RATE
   printf("nsZipReaderCache size=%d hits=%d lookups=%d rate=%f%% flushes=%d missed %d\n",
@@ -1106,8 +1085,8 @@ nsZipReaderCache::GetZip(nsIFile* zipFil
   rv = zipFile->GetNativePath(path);
   if (NS_FAILED(rv)) return rv;
 
-  nsCStringKey key(path);
-  nsJAR* zip = static_cast<nsJAR*>(static_cast<nsIZipReader*>(mZips.Get(&key))); // AddRefs
+  nsJAR* zip = nsnull;
+  mZips.Get(path, &zip);
   if (zip) {
 #ifdef ZIP_CACHE_HIT_RATE
     mZipCacheHits++;
@@ -1118,7 +1097,6 @@ nsZipReaderCache::GetZip(nsIFile* zipFil
     zip = new nsJAR();
     if (zip == nsnull)
         return NS_ERROR_OUT_OF_MEMORY;
-    NS_ADDREF(zip);
     zip->SetZipReaderCache(this);
 
     rv = zip->Open(zipFile);
@@ -1127,119 +1105,10 @@ nsZipReaderCache::GetZip(nsIFile* zipFil
       return rv;
     }
 
-    PRBool collision = mZips.Put(&key, static_cast<nsIZipReader*>(zip)); // AddRefs to 2
-    NS_ASSERTION(!collision, "horked");
+    mZips.Put(path, zip);
   }
   *result = zip;
   return rv;
-}
-
-static PRBool PR_CALLBACK
-FindOldestZip(nsHashKey *aKey, void *aData, void* closure)
-{
-  nsJAR** oldestPtr = (nsJAR**)closure;
-  nsJAR* oldest = *oldestPtr;
-  nsJAR* current = (nsJAR*)aData;
-  PRIntervalTime currentReleaseTime = current->GetReleaseTime();
-  if (currentReleaseTime != PR_INTERVAL_NO_TIMEOUT) {
-    if (oldest == nsnull ||
-        currentReleaseTime < oldest->GetReleaseTime()) {
-      *oldestPtr = current;
-    }    
-  }
-  return PR_TRUE;
-}
-
-struct ZipFindData {nsJAR* zip; PRBool found;}; 
-
-static PRBool PR_CALLBACK
-FindZip(nsHashKey *aKey, void *aData, void* closure)
-{
-  ZipFindData* find_data = (ZipFindData*)closure;
-
-  if (find_data->zip == (nsJAR*)aData) {
-    find_data->found = PR_TRUE; 
-    return PR_FALSE;
-  }
-  return PR_TRUE;
-}
-
-nsresult
-nsZipReaderCache::ReleaseZip(nsJAR* zip)
-{
-  nsresult rv;
-  nsAutoLock lock(mLock);
-
-  // It is possible that two thread compete for this zip. The dangerous 
-  // case is where one thread Releases the zip and discovers that the ref
-  // count has gone to one. Before it can call this ReleaseZip method
-  // another thread calls our GetZip method. The ref count goes to two. That
-  // second thread then Releases the zip and the ref count goes to one. It
-  // then tries to enter this ReleaseZip method and blocks while the first
-  // thread is still here. The first thread continues and remove the zip from 
-  // the cache and calls its Release method sending the ref count to 0 and
-  // deleting the zip. However, the second thread is still blocked at the
-  // start of ReleaseZip, but the 'zip' param now hold a reference to a
-  // deleted zip!
-  // 
-  // So, we are going to try safeguarding here by searching our hashtable while
-  // locked here for the zip. We return fast if it is not found. 
-
-  ZipFindData find_data = {zip, PR_FALSE};
-  mZips.Enumerate(FindZip, &find_data);
-  if (!find_data.found) {
-#ifdef ZIP_CACHE_HIT_RATE
-    mZipSyncMisses++;
-#endif
-    return NS_OK;
-  }
-
-  zip->SetReleaseTime();
-
-  if (mZips.Count() <= mCacheSize)
-    return NS_OK;
-
-  nsJAR* oldest = nsnull;
-  mZips.Enumerate(FindOldestZip, &oldest);
-  
-  // Because of the craziness above it is possible that there is no zip that
-  // needs removing. 
-  if (!oldest)
-    return NS_OK;
-
-#ifdef ZIP_CACHE_HIT_RATE
-    mZipCacheFlushes++;
-#endif
-
-  // Clear the cache pointer in case we gave out this oldest guy while
-  // his Release call was being made. Otherwise we could nest on ReleaseZip
-  // when the second owner calls Release and we are still here in this lock.
-  oldest->SetZipReaderCache(nsnull);
-
-  // remove from hashtable
-  nsCAutoString path;
-  rv = oldest->GetJarPath(path);
-  if (NS_FAILED(rv)) return rv;
-
-  nsCStringKey key(path);
-  PRBool removed = mZips.Remove(&key);  // Releases
-  NS_ASSERTION(removed, "botched");
-
-  return NS_OK;
-}
-
-static PRBool PR_CALLBACK
-FindFlushableZip(nsHashKey *aKey, void *aData, void* closure)
-{
-  nsHashKey** flushableKeyPtr = (nsHashKey**)closure;
-  nsJAR* current = (nsJAR*)aData;
-  
-  if (current->GetReleaseTime() != PR_INTERVAL_NO_TIMEOUT) {
-    *flushableKeyPtr = aKey;
-    current->SetZipReaderCache(nsnull);
-    return PR_FALSE;
-  }
-  return PR_TRUE;
 }
 
 NS_IMETHODIMP
@@ -1247,26 +1116,25 @@ nsZipReaderCache::Observe(nsISupports *a
                           const char *aTopic, 
                           const PRUnichar *aSomeData)
 {
-  if (strcmp(aTopic, "memory-pressure") == 0) {
-    nsAutoLock lock(mLock);
-    while (PR_TRUE) {
-      nsHashKey* flushable = nsnull;
-      mZips.Enumerate(FindFlushableZip, &flushable); 
-      if ( ! flushable )
-        break;
-      PRBool removed = mZips.Remove(flushable);  // Releases
-      NS_ASSERTION(removed, "botched");
-
-#ifdef xDEBUG_jband
-      printf("flushed something from the jar cache\n");
-#endif
-    }
-  }
-  else if (strcmp(aTopic, "chrome-flush-caches") == 0) {
-    mZips.Enumerate(DropZipReaderCache, nsnull);
-    mZips.Reset();
+  if (strcmp(aTopic, "chrome-flush-caches") == 0) {
+    mZips.Clear();
   }
   return NS_OK;
+}
+
+static PLDHashOperator
+SweepDyingJARs(const nsACString& aKey, nsJAR *&aJAR, void *arg)
+{
+  if (!NS_GetGC()->GetMark(aJAR))
+    return PL_DHASH_REMOVE;
+
+  return PL_DHASH_NEXT;
+}
+
+void
+nsZipReaderCache::presweep()
+{
+  mZips.Enumerate(SweepDyingJARs, nsnull);
 }
 
 PRTime GetModTime(PRUint16 aDate, PRUint16 aTime)
diff --git a/modules/libjar/nsJAR.h b/modules/libjar/nsJAR.h
--- a/modules/libjar/nsJAR.h
+++ b/modules/libjar/nsJAR.h
@@ -57,7 +57,6 @@
 #include "nsString.h"
 #include "nsIFile.h"
 #include "nsStringEnumerator.h"
-#include "nsHashtable.h"
 #include "nsAutoLock.h"
 #include "nsIZipReader.h"
 #include "nsIJAR.h"
@@ -68,6 +67,8 @@
 #include "nsIObserverService.h"
 #include "nsWeakReference.h"
 #include "nsIObserver.h"
+#include "nsClassHashtable.h"
+#include "nsDataHashtable.h"
 
 class nsIInputStream;
 class nsJARManifestItem;
@@ -93,7 +94,7 @@ PRTime GetModTime(PRUint16 aDate, PRUint
  * nsJAR serves as an XPCOM wrapper for nsZipArchive with the addition of 
  * JAR manifest file parsing. 
  *------------------------------------------------------------------------*/
-class nsJAR : public nsIZipReader, public nsIJAR
+class nsJAR : public XPCOMGCFinalizedObject, public nsIZipReader, public nsIJAR
 {
   // Allows nsJARInputStream to call the verification functions
   friend class nsJARInputStream;
@@ -137,7 +138,8 @@ class nsJAR : public nsIZipReader, publi
     //-- Private data members
     nsCOMPtr<nsIFile>        mZipFile;        // The zip/jar file on disk
     nsZipArchive             mZip;            // The underlying zip archive
-    nsObjectHashtable        mManifestData;   // Stores metadata for each entry
+
+    nsClassHashtable<nsCStringHashKey, nsJARManifestItem> mManifestData;
     PRBool                   mParsedManifest; // True if manifest has been parsed
     nsCOMPtr<nsIPrincipal>   mPrincipal;      // The entity which signed this file
     PRInt16                  mGlobalStatus;   // Global signature verification status
@@ -171,7 +173,7 @@ class nsJAR : public nsIZipReader, publi
  * An individual JAR entry. A set of nsJARItems macthing a
  * supplied pattern are returned in a nsJAREnumerator.
  */
-class nsJARItem : public nsIZipEntry
+class nsJARItem : public XPCOMGCFinalizedObject, public nsIZipEntry
 {
 public:
     NS_DECL_ISUPPORTS
@@ -197,7 +199,7 @@ private:
  * Enumerates a list of files in a zip archive 
  * (based on a pattern match in its member nsZipFind).
  */
-class nsJAREnumerator : public nsIUTF8StringEnumerator
+class nsJAREnumerator : public XPCOMGCFinalizedObject, public nsIUTF8StringEnumerator
 {
 public:
     NS_DECL_ISUPPORTS
@@ -220,8 +222,9 @@ private:
 #define ZIP_CACHE_HIT_RATE
 #endif
 
-class nsZipReaderCache : public nsIZipReaderCache, public nsIObserver,
-                         public nsSupportsWeakReference
+class nsZipReaderCache : public XPCOMGCFinalizedObject, public nsIZipReaderCache, public nsIObserver,
+                         public nsSupportsWeakReference,
+                         private MMgc::GCCallback
 {
 public:
   NS_DECL_ISUPPORTS
@@ -231,12 +234,15 @@ public:
   nsZipReaderCache();
   virtual ~nsZipReaderCache();
 
-  nsresult ReleaseZip(nsJAR* reader);
+  // Invalidate dying members of mZips in presweep
+  virtual void presweep();
 
 protected:
   PRLock*               mLock;
   PRInt32               mCacheSize;
-  nsSupportsHashtable   mZips;
+
+  // This is *not* a strong reference to the zips
+  nsDataHashtable<nsCStringHashKey, nsJAR*, CAllocator> mZips;
 
 #ifdef ZIP_CACHE_HIT_RATE
   PRUint32              mZipCacheLookups;