Bug 586859: prepare startup cache for off-main thread writing r=dwitte a=blocking-final
authorTaras Glek <tglek@mozilla.com>
Fri, 07 Jan 2011 10:55:14 -0800
changeset 60133 908f598737d76952428b1d62eeacca896a7168fb
parent 60132 c1b1fde638f90968b9db2224ac83a264df21e3ac
child 60134 c2cc892c5d5313e8888f64b13b3b3eb17cf9d2ab
push idunknown
push userunknown
push dateunknown
reviewersdwitte, blocking-final
bugs586859
milestone2.0b9pre
Bug 586859: prepare startup cache for off-main thread writing r=dwitte a=blocking-final
startupcache/StartupCache.cpp
startupcache/StartupCache.h
--- a/startupcache/StartupCache.cpp
+++ b/startupcache/StartupCache.cpp
@@ -136,18 +136,16 @@ nsresult
 StartupCache::Init() 
 {
   nsresult rv;
   mTable.Init();
 #ifdef DEBUG
   mWriteObjectMap.Init();
 #endif
 
-  mZipW = do_CreateInstance("@mozilla.org/zipwriter;1", &rv);
-  NS_ENSURE_SUCCESS(rv, rv);
   nsCOMPtr<nsIFile> file;
   rv = NS_GetSpecialDirectory("ProfLDS",
                               getter_AddRefs(file));
   if (NS_FAILED(rv)) {
     // return silently, this will fail in mochitests's xpcshell process.
     return rv;
   }
 
@@ -183,24 +181,17 @@ StartupCache::Init()
   rv = LoadArchive();
   
   // Sometimes we don't have a cache yet, that's ok.
   // If it's corrupted, just remove it and start over.
   if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND) {
     NS_WARNING("Failed to load startupcache file correctly, removing!");
     InvalidateCache();
   }
-
-  mTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
-  NS_ENSURE_SUCCESS(rv, rv);
-  // Wait for 10 seconds, then write out the cache.
-  rv = mTimer->InitWithFuncCallback(StartupCache::WriteTimeout, this, 600000,
-                                    nsITimer::TYPE_ONE_SHOT);
-
-  return rv;
+  return NS_OK;
 }
 
 nsresult
 StartupCache::LoadArchive() 
 {
   PRBool exists;
   mArchive = NULL;
   nsresult rv = mFile->Exists(&exists);
@@ -239,86 +230,47 @@ StartupCache::GetBuffer(const char* id, 
 
   return NS_ERROR_NOT_AVAILABLE;
 }
 
 // Makes a copy of the buffer, client retains ownership of inbuf.
 nsresult
 StartupCache::PutBuffer(const char* id, const char* inbuf, PRUint32 len) 
 {
-  nsresult rv;
-
   if (StartupCache::gShutdownInitiated) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   nsAutoArrayPtr<char> data(new char[len]);
   memcpy(data, inbuf, len);
 
   nsDependentCString idStr(id);
-  if (!mStartupWriteInitiated) {
-    // Cache it for now, we'll write all together later.
-    CacheEntry* entry; 
-
+  // Cache it for now, we'll write all together later.
+  CacheEntry* entry; 
+  
 #ifdef DEBUG
-    mTable.Get(idStr, &entry);
-    NS_ASSERTION(entry == nsnull, "Existing entry in StartupCache.");
-
-    if (mArchive) {
-      nsZipItem* zipItem = mArchive->GetItem(id);
-      NS_ASSERTION(zipItem == nsnull, "Existing entry in disk StartupCache.");
-    }
-#endif
-
-    entry = new CacheEntry(data.forget(), len);
-    mTable.Put(idStr, entry);
-    return NS_OK;
-  }
+  mTable.Get(idStr, &entry);
+  NS_ASSERTION(entry == nsnull, "Existing entry in StartupCache.");
   
-  rv = mZipW->Open(mFile, PR_RDWR | PR_CREATE_FILE);
-  NS_ENSURE_SUCCESS(rv, rv);  
-
-  // XXX We need to think about whether to write this out every time,
-  // or somehow detect a good time to write.  We need to finish writing
-  // before shutdown though, and writing also requires a reload of the
-  // reader's archive, which probably can't handle having the underlying
-  // file change underneath it. Potentially could reload on the next
-  // read request, if this is a problem. See Bug 586859.
-#ifdef DEBUG
-  PRBool hasEntry;
-  rv = mZipW->HasEntry(idStr, &hasEntry);
-  NS_ENSURE_SUCCESS(rv, rv);
-  NS_ASSERTION(hasEntry == PR_FALSE, "Existing entry in disk StartupCache.");
+  if (mArchive) {
+    nsZipItem* zipItem = mArchive->GetItem(id);
+    NS_ASSERTION(zipItem == nsnull, "Existing entry in disk StartupCache.");
+  }
 #endif
-
-  nsCOMPtr<nsIStringInputStream> stream
-    = do_CreateInstance("@mozilla.org/io/string-input-stream;1",
-                        &rv);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = stream->AdoptData(data, len);
-  NS_ENSURE_SUCCESS(rv, rv);
-  data.forget();
   
-  rv = mZipW->AddEntryStream(idStr, 0, 0, stream, false);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // Close the archive so Windows doesn't choke.
-  mArchive = NULL;
-  rv = mZipW->Close();
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // our reader's view of the archive is outdated now, reload it.
-  return LoadArchive();
+  entry = new CacheEntry(data.forget(), len);
+  mTable.Put(idStr, entry);
+  return ResetStartupWriteTimer();
 }
 
 struct CacheWriteHolder
 {
   nsCOMPtr<nsIZipWriter> writer;
   nsCOMPtr<nsIStringInputStream> stream;
+  PRTime time;
 };
 
 PLDHashOperator
 CacheCloseHelper(const nsACString& key, nsAutoPtr<CacheEntry>& data, 
                  void* closure) 
 {
   nsresult rv;
  
@@ -329,71 +281,72 @@ CacheCloseHelper(const nsACString& key, 
   stream->ShareData(data->data, data->size);
 
 #ifdef DEBUG
   PRBool hasEntry;
   rv = writer->HasEntry(key, &hasEntry);
   NS_ASSERTION(NS_SUCCEEDED(rv) && hasEntry == PR_FALSE, 
                "Existing entry in disk StartupCache.");
 #endif
-  rv = writer->AddEntryStream(key, 0, 0, stream, false);
+  rv = writer->AddEntryStream(key, holder->time, PR_TRUE, stream, false);
   
   if (NS_FAILED(rv)) {
     NS_WARNING("cache entry deleted but not written to disk.");
   }
   return PL_DHASH_REMOVE;
 }
 
 void
 StartupCache::WriteToDisk() 
 {
   nsresult rv;
   mStartupWriteInitiated = PR_TRUE;
 
   if (mTable.Count() == 0)
     return;
 
-  rv = mZipW->Open(mFile, PR_RDWR | PR_CREATE_FILE);
+  nsCOMPtr<nsIZipWriter> zipW = do_CreateInstance("@mozilla.org/zipwriter;1");
+  if (!zipW)
+    return;
+
+  rv = zipW->Open(mFile, PR_RDWR | PR_CREATE_FILE);
   if (NS_FAILED(rv)) {
     NS_WARNING("could not open zipfile for write");
     return;
   } 
 
   nsCOMPtr<nsIStringInputStream> stream 
     = do_CreateInstance("@mozilla.org/io/string-input-stream;1", &rv);
   if (NS_FAILED(rv)) {
     NS_WARNING("Couldn't create string input stream.");
     return;
   }
 
   CacheWriteHolder holder;
   holder.stream = stream;
-  holder.writer = mZipW;
+  holder.writer = zipW;
+  holder.time = PR_Now();
 
   mTable.Enumerate(CacheCloseHelper, &holder);
 
   // Close the archive so Windows doesn't choke.
   mArchive = NULL;
-  mZipW->Close();
+  zipW->Close();
       
   // our reader's view of the archive is outdated now, reload it.
   LoadArchive();
   
   return;
 }
 
 void
 StartupCache::InvalidateCache() 
 {
   mTable.Clear();
   mArchive = NULL;
-
-  // This is usually closed, but it's possible to get into
-  // an inconsistent state.
-  mZipW->Close();
   mFile->Remove(false);
   LoadArchive();
 }
 
 void
 StartupCache::WriteTimeout(nsITimer *aTimer, void *aClosure)
 {
   StartupCache* sc = (StartupCache*) aClosure;
@@ -429,16 +382,32 @@ StartupCache::GetDebugObjectOutputStream
   NS_ADDREF(*aOutStream = stream);
 #else
   NS_ADDREF(*aOutStream = aStream);
 #endif
   
   return NS_OK;
 }
 
+nsresult
+StartupCache::ResetStartupWriteTimer()
+{
+  mStartupWriteInitiated = PR_FALSE;
+  nsresult rv;
+  if (!mTimer)
+    mTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
+  else
+    rv = mTimer->Cancel();
+  NS_ENSURE_SUCCESS(rv, rv);
+  // Wait for 10 seconds, then write out the cache.
+  mTimer->InitWithFuncCallback(StartupCache::WriteTimeout, this, 60000,
+                               nsITimer::TYPE_ONE_SHOT);
+  return NS_OK;
+}
+
 // StartupCacheDebugOutputStream implementation
 #ifdef DEBUG
 NS_IMPL_ISUPPORTS3(StartupCacheDebugOutputStream, nsIObjectOutputStream, 
                    nsIBinaryOutputStream, nsIOutputStream)
 
 PRBool
 StartupCacheDebugOutputStream::CheckReferences(nsISupports* aObject)
 {
@@ -581,39 +550,30 @@ StartupCacheWrapper::GetDebugObjectOutpu
   if (!sc) {
     return NS_ERROR_NOT_INITIALIZED;
   }
   return sc->GetDebugObjectOutputStream(stream, outStream);
 }
 
 nsresult
 StartupCacheWrapper::StartupWriteComplete(PRBool *complete)
-{  
+{
   StartupCache* sc = StartupCache::GetSingleton();
   if (!sc) {
     return NS_ERROR_NOT_INITIALIZED;
   }
   *complete = sc->mStartupWriteInitiated && sc->mTable.Count() == 0;
   return NS_OK;
 }
 
 nsresult
 StartupCacheWrapper::ResetStartupWriteTimer()
 {
   StartupCache* sc = StartupCache::GetSingleton();
-  if (!sc) {
-    return NS_ERROR_NOT_INITIALIZED;
-  }
-  sc->mStartupWriteInitiated = PR_FALSE;
-  
-  // Init with a shorter timer, for testing convenience.
-  sc->mTimer->Cancel();
-  sc->mTimer->InitWithFuncCallback(StartupCache::WriteTimeout, sc, 10000,
-                                   nsITimer::TYPE_ONE_SHOT);
-  return NS_OK;
+  return sc ? sc->ResetStartupWriteTimer() : NS_ERROR_NOT_INITIALIZED;
 }
 
 nsresult
 StartupCacheWrapper::GetObserver(nsIObserver** obv) {
   StartupCache* sc = StartupCache::GetSingleton();
   if (!sc) {
     return NS_ERROR_NOT_INITIALIZED;
   }
--- a/startupcache/StartupCache.h
+++ b/startupcache/StartupCache.h
@@ -150,22 +150,22 @@ public:
 
 private:
   StartupCache();
   ~StartupCache();
 
   nsresult LoadArchive();
   nsresult Init();
   void WriteToDisk();
+  nsresult ResetStartupWriteTimer();
 
   static nsresult InitSingleton();
   static void WriteTimeout(nsITimer *aTimer, void *aClosure);
 
   nsClassHashtable<nsCStringHashKey, CacheEntry> mTable;
-  nsCOMPtr<nsIZipWriter> mZipW;
   nsAutoPtr<nsZipArchive> mArchive;
   nsCOMPtr<nsILocalFile> mFile;
   
   nsCOMPtr<nsIObserverService> mObserverService;
   nsRefPtr<StartupCacheListener> mListener;
   nsCOMPtr<nsITimer> mTimer;
 
   PRBool mStartupWriteInitiated;