Bug 1009500 - HTTP cache v2: respect OPEN_BYPASS_IF_BUSY, r=michal
authorHonza Bambas <honzab.moz@firemni.cz>
Wed, 14 May 2014 23:12:38 +0200
changeset 195765 fde411a82da7012a55bb2a888c74c04ddf7b88f5
parent 195764 0f5952a674944bda8b73b669bf75920d041716c1
child 195766 e0fa93595164f3b340f43a82d4d5e401b58ffd6c
push id5990
push userasasaki@mozilla.com
push dateMon, 09 Jun 2014 21:40:24 +0000
treeherdermozilla-aurora@0796197efbc9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmichal
bugs1009500
milestone32.0a1
Bug 1009500 - HTTP cache v2: respect OPEN_BYPASS_IF_BUSY, r=michal
netwerk/cache2/CacheEntry.cpp
netwerk/cache2/CacheEntry.h
netwerk/test/unit/head_cache2.js
netwerk/test/unit/test_cache2-01e-basic-bypass-if-busy.js
netwerk/test/unit/xpcshell.ini
--- a/netwerk/cache2/CacheEntry.cpp
+++ b/netwerk/cache2/CacheEntry.cpp
@@ -259,28 +259,29 @@ nsresult CacheEntry::HashingKey(nsCSubst
 }
 
 void CacheEntry::AsyncOpen(nsICacheEntryOpenCallback* aCallback, uint32_t aFlags)
 {
   LOG(("CacheEntry::AsyncOpen [this=%p, state=%s, flags=%d, callback=%p]",
     this, StateString(mState), aFlags, aCallback));
 
   bool readonly = aFlags & nsICacheStorage::OPEN_READONLY;
+  bool bypassIfBusy = aFlags & nsICacheStorage::OPEN_BYPASS_IF_BUSY;
   bool truncate = aFlags & nsICacheStorage::OPEN_TRUNCATE;
   bool priority = aFlags & nsICacheStorage::OPEN_PRIORITY;
   bool multithread = aFlags & nsICacheStorage::CHECK_MULTITHREADED;
 
   MOZ_ASSERT(!readonly || !truncate, "Bad flags combination");
   MOZ_ASSERT(!(truncate && mState > LOADING), "Must not call truncate on already loaded entry");
 
   Callback callback(this, aCallback, readonly, multithread);
 
   mozilla::MutexAutoLock lock(mLock);
 
-  RememberCallback(callback);
+  RememberCallback(callback, bypassIfBusy);
 
   // Load() opens the lock
   if (Load(truncate, priority)) {
     // Loading is in progress...
     return;
   }
 
   InvokeCallbacks();
@@ -486,21 +487,29 @@ void CacheEntry::TransferCallbacks(Cache
     // Carry the entry reference (unfortunatelly, needs to be done manually...)
     for (uint32_t i = 0; i < callbacksLength; ++i)
       mCallbacks[i].ExchangeEntry(this);
 
     BackgroundOp(Ops::CALLBACKS, true);
   }
 }
 
-void CacheEntry::RememberCallback(Callback const& aCallback)
+void CacheEntry::RememberCallback(Callback & aCallback, bool aBypassIfBusy)
 {
-  LOG(("CacheEntry::RememberCallback [this=%p, cb=%p]", this, aCallback.mCallback.get()));
+  mLock.AssertCurrentThreadOwns();
+
+  LOG(("CacheEntry::RememberCallback [this=%p, cb=%p, state=%s]",
+    this, aCallback.mCallback.get(), StateString(mState)));
 
-  mLock.AssertCurrentThreadOwns();
+  if (aBypassIfBusy && (mState == WRITING || mState == REVALIDATING)) {
+    LOG(("  writing or revalidating, callback wants to bypass cache"));
+    aCallback.mNotWanted = true;
+    InvokeAvailableCallback(aCallback);
+    return;
+  }
 
   mCallbacks.AppendElement(aCallback);
 }
 
 void CacheEntry::InvokeCallbacksLock()
 {
   mozilla::MutexAutoLock lock(mLock);
   InvokeCallbacks();
--- a/netwerk/cache2/CacheEntry.h
+++ b/netwerk/cache2/CacheEntry.h
@@ -205,17 +205,17 @@ private:
     nsRefPtr<CacheEntry> mEntry;
     nsresult mRv;
   };
 
   // Loads from disk asynchronously
   bool Load(bool aTruncate, bool aPriority);
   void OnLoaded();
 
-  void RememberCallback(Callback const & aCallback);
+  void RememberCallback(Callback & aCallback, bool aBypassIfBusy);
   void InvokeCallbacksLock();
   void InvokeCallbacks();
   bool InvokeCallbacks(bool aReadOnly);
   bool InvokeCallback(Callback & aCallback);
   void InvokeAvailableCallback(Callback const & aCallback);
 
   nsresult OpenOutputStreamInternal(int64_t offset, nsIOutputStream * *_retval);
 
--- a/netwerk/test/unit/head_cache2.js
+++ b/netwerk/test/unit/head_cache2.js
@@ -38,16 +38,18 @@ const WAITFORWRITE =    1 << 8;
 // Don't write data (i.e. don't open output stream)
 const METAONLY =        1 << 9;
 // Do recreation of an existing cache entry
 const RECREATE =        1 << 10;
 // Do not give me the entry
 const NOTWANTED =       1 << 11;
 // Tell the cache to wait for the entry to be completely written first
 const COMPLETE =        1 << 12;
+// Don't write meta/data and don't set valid in the callback, consumer will do it manually
+const DONTFILL =        1 << 13;
 
 var log_c2 = true;
 function LOG_C2(o, m)
 {
   if (!log_c2) return;
   if (!m)
     dump("TEST-INFO | CACHE2: " + o + "\n");
   else
@@ -174,16 +176,21 @@ OpenCallback.prototype =
       if (!(this.behavior & PARTIAL)) {
         try {
           entry.getMetaDataElement("meto");
           do_check_true(false);
         }
         catch (ex) {}
       }
 
+      if (this.behavior & DONTFILL) {
+        do_check_false(this.behavior & WAITFORWRITE);
+        return;
+      }
+
       var self = this;
       do_execute_soon(function() { // emulate network latency
         entry.setMetaDataElement("meto", self.workingMetadata);
         entry.metaDataReady();
         if (self.behavior & METAONLY) {
           // Since forcing GC/CC doesn't trigger OnWriterClosed, we have to set the entry valid manually :(
           entry.setValid();
           entry.close();
new file mode 100644
--- /dev/null
+++ b/netwerk/test/unit/test_cache2-01e-basic-bypass-if-busy.js
@@ -0,0 +1,35 @@
+function run_test()
+{
+  do_get_profile();
+
+  if (!newCacheBackEndUsed()) {
+    do_check_true(true, "This test doesn't run when the old cache back end is used since the behavior is different");
+    return;
+  }
+
+  // Open for write, delay the actual write
+  asyncOpenCacheEntry("http://a/", "disk", Ci.nsICacheStorage.OPEN_NORMALLY, null,
+    new OpenCallback(NEW|DONTFILL, "a1m", "a1d", function(entry) {
+      var bypassed = false;
+
+      // Open and bypass
+      asyncOpenCacheEntry("http://a/", "disk", Ci.nsICacheStorage.OPEN_BYPASS_IF_BUSY, null,
+        new OpenCallback(NOTFOUND, "", "", function(entry) {
+          do_check_false(bypassed);
+          bypassed = true;
+        })
+      );
+
+      // do_execute_soon for two reasons:
+      // 1. we want finish_cache2_test call for sure after do_test_pending, but all the callbacks here
+      //    may invoke synchronously
+      // 2. precaution when the OPEN_BYPASS_IF_BUSY invocation become a post one day
+      do_execute_soon(function() {
+        do_check_true(bypassed);
+        finish_cache2_test();
+      });
+    })
+  );
+
+  do_test_pending();
+}
--- a/netwerk/test/unit/xpcshell.ini
+++ b/netwerk/test/unit/xpcshell.ini
@@ -19,16 +19,17 @@ support-files =
   test_link.url
 
 [test_nsIBufferedOutputStream_writeFrom_block.js]
 [test_cache2-01-basic.js]
 [test_cache2-01a-basic-readonly.js]
 [test_cache2-01b-basic-datasize.js]
 [test_cache2-01c-basic-hasmeta-only.js]
 [test_cache2-01d-basic-not-wanted.js]
+[test_cache2-01e-basic-bypass-if-busy.js]
 [test_cache2-02-open-non-existing.js]
 [test_cache2-03-oncacheentryavail-throws.js]
 [test_cache2-04-oncacheentryavail-throws2x.js]
 [test_cache2-05-visit.js]
 [test_cache2-06-pb-mode.js]
 # Bug 675039, comment 6: "The difference is that the memory cache is disabled in Armv6 builds."
 skip-if = os == "android"
 [test_cache2-07-visit-memory.js]