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 id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdwitte, blocking-final
bugs586859
milestone2.0b9pre
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 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;