Bug 1006217 - Assertion failure: mValidityMap.Length() == 0, at netwerk/cache2/CacheFileChunk.cpp:392, r=honzab
authorMichal Novotny <michal.novotny@gmail.com>
Thu, 08 May 2014 16:15:24 +0200
changeset 182152 2c8666fc34b3ef1b76f1506241cb89ad08f87d79
parent 182151 cf209153f20e164d108184cffec8b2d400b34edd
child 182153 40cc7a7f6cc0ee081c0d58b1423c4840442b9e3a
push id26748
push userryanvm@gmail.com
push dateThu, 08 May 2014 19:44:34 +0000
treeherdermozilla-central@4cafec48a1f0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewershonzab
bugs1006217
milestone32.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 1006217 - Assertion failure: mValidityMap.Length() == 0, at netwerk/cache2/CacheFileChunk.cpp:392, r=honzab
netwerk/cache2/CacheFileChunk.cpp
netwerk/cache2/CacheFileChunk.h
netwerk/cache2/CacheFileUtils.cpp
netwerk/cache2/CacheFileUtils.h
--- a/netwerk/cache2/CacheFileChunk.cpp
+++ b/netwerk/cache2/CacheFileChunk.cpp
@@ -2,18 +2,16 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "CacheLog.h"
 #include "CacheFileChunk.h"
 
 #include "CacheFile.h"
 #include "nsThreadUtils.h"
-#include "nsAlgorithm.h"
-#include <algorithm>
 
 namespace mozilla {
 namespace net {
 
 #define kMinBufSize        512
 
 class NotifyUpdateListenerEvent : public nsRunnable {
 public:
@@ -43,65 +41,16 @@ public:
   }
 
 protected:
   nsCOMPtr<CacheFileChunkListener> mCallback;
   nsRefPtr<CacheFileChunk>         mChunk;
 };
 
 
-class ValidityPair {
-public:
-  ValidityPair(uint32_t aOffset, uint32_t aLen)
-    : mOffset(aOffset), mLen(aLen)
-  {}
-
-  ValidityPair& operator=(const ValidityPair& aOther) {
-    mOffset = aOther.mOffset;
-    mLen = aOther.mLen;
-    return *this;
-  }
-
-  bool Overlaps(const ValidityPair& aOther) const {
-    if ((mOffset <= aOther.mOffset && mOffset + mLen >= aOther.mOffset) ||
-        (aOther.mOffset <= mOffset && aOther.mOffset + mLen >= mOffset))
-      return true;
-
-    return false;
-  }
-
-  bool LessThan(const ValidityPair& aOther) const {
-    if (mOffset < aOther.mOffset)
-      return true;
-
-    if (mOffset == aOther.mOffset && mLen < aOther.mLen)
-      return true;
-
-    return false;
-  }
-
-  void Merge(const ValidityPair& aOther) {
-    MOZ_ASSERT(Overlaps(aOther));
-
-    uint32_t offset = std::min(mOffset, aOther.mOffset);
-    uint32_t end = std::max(mOffset + mLen, aOther.mOffset + aOther.mLen);
-
-    mOffset = offset;
-    mLen = end - offset;
-  }
-
-  uint32_t Offset() { return mOffset; }
-  uint32_t Len()    { return mLen; }
-
-private:
-  uint32_t mOffset;
-  uint32_t mLen;
-};
-
-
 NS_IMPL_ADDREF(CacheFileChunk)
 NS_IMETHODIMP_(MozExternalRefCountType)
 CacheFileChunk::Release()
 {
   NS_PRECONDITION(0 != mRefCnt, "dup release");
   nsrefcnt count = --mRefCnt;
   NS_LOG_RELEASE(this, count, "CacheFileChunk");
 
@@ -362,16 +311,17 @@ CacheFileChunk::DataSize()
 
 void
 CacheFileChunk::UpdateDataSize(uint32_t aOffset, uint32_t aLen, bool aEOF)
 {
   mFile->AssertOwnsLock();
 
   MOZ_ASSERT(!aEOF, "Implement me! What to do with opened streams?");
   MOZ_ASSERT(aOffset <= mDataSize);
+  MOZ_ASSERT(aLen != 0);
 
   // UpdateDataSize() is called only when we've written some data to the chunk
   // and we never write data anymore once some error occurs.
   MOZ_ASSERT(mState != ERROR);
 
   LOG(("CacheFileChunk::UpdateDataSize() [this=%p, offset=%d, len=%d, EOF=%d]",
        this, aOffset, aLen, aEOF));
 
@@ -386,73 +336,33 @@ CacheFileChunk::UpdateDataSize(uint32_t 
   if (aOffset + aLen > mDataSize) {
     mDataSize = aOffset + aLen;
     notify = true;
   }
 
   if (mState == READY || mState == WRITING) {
     MOZ_ASSERT(mValidityMap.Length() == 0);
 
-    if (notify)
+    if (notify) {
       NotifyUpdateListeners();
+    }
 
     return;
   }
 
   // We're still waiting for data from the disk. This chunk cannot be used by
   // input stream, so there must be no update listener. We also need to keep
   // track of where the data is written so that we can correctly merge the new
   // data with the old one.
 
   MOZ_ASSERT(mUpdateListeners.Length() == 0);
   MOZ_ASSERT(mState == READING);
 
-  ValidityPair pair(aOffset, aLen);
-
-  if (mValidityMap.Length() == 0) {
-    mValidityMap.AppendElement(pair);
-    return;
-  }
-
-
-  // Find out where to place this pair into the map, it can overlap with
-  // one preceding pair and all subsequent pairs.
-  uint32_t pos = 0;
-  for (pos = mValidityMap.Length() ; pos > 0 ; pos--) {
-    if (mValidityMap[pos-1].LessThan(pair)) {
-      if (mValidityMap[pos-1].Overlaps(pair)) {
-        // Merge with the preceding pair
-        mValidityMap[pos-1].Merge(pair);
-        pos--; // Point to the updated pair
-      }
-      else {
-        if (pos == mValidityMap.Length())
-          mValidityMap.AppendElement(pair);
-        else
-          mValidityMap.InsertElementAt(pos, pair);
-      }
-
-      break;
-    }
-  }
-
-  if (!pos)
-    mValidityMap.InsertElementAt(0, pair);
-
-  // Now pos points to merged or inserted pair, check whether it overlaps with
-  // subsequent pairs.
-  while (pos + 1 < mValidityMap.Length()) {
-    if (mValidityMap[pos].Overlaps(mValidityMap[pos + 1])) {
-      mValidityMap[pos].Merge(mValidityMap[pos + 1]);
-      mValidityMap.RemoveElementAt(pos + 1);
-    }
-    else {
-      break;
-    }
-  }
+  mValidityMap.AddPair(aOffset, aLen);
+  mValidityMap.Log();
 }
 
 nsresult
 CacheFileChunk::OnFileOpened(CacheFileHandle *aHandle, nsresult aResult)
 {
   MOZ_CRASH("CacheFileChunk::OnFileOpened should not be called!");
   return NS_ERROR_UNEXPECTED;
 }
@@ -525,26 +435,31 @@ CacheFileChunk::OnDataRead(CacheFileHand
         if (!mBuf) {
           // Just swap the buffers if we don't have mBuf yet
           MOZ_ASSERT(mDataSize == mRWBufSize);
           mBuf = mRWBuf;
           mBufSize = mRWBufSize;
           mRWBuf = nullptr;
           mRWBufSize = 0;
         } else {
+          LOG(("CacheFileChunk::OnDataRead() - Merging buffers. [this=%p]",
+               this));
+
           // Merge data with write buffer
           if (mRWBufSize < mBufSize) {
             mRWBuf = static_cast<char *>(moz_xrealloc(mRWBuf, mBufSize));
             mRWBufSize = mBufSize;
           }
 
+          mValidityMap.Log();
           for (uint32_t i = 0 ; i < mValidityMap.Length() ; i++) {
             memcpy(mRWBuf + mValidityMap[i].Offset(),
                    mBuf + mValidityMap[i].Offset(), mValidityMap[i].Len());
           }
+          mValidityMap.Clear();
 
           free(mBuf);
           mBuf = mRWBuf;
           mBufSize = mRWBufSize;
           mRWBuf = nullptr;
           mRWBufSize = 0;
 
           DoMemoryReport(MemorySize());
--- a/netwerk/cache2/CacheFileChunk.h
+++ b/netwerk/cache2/CacheFileChunk.h
@@ -3,28 +3,28 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef CacheFileChunk__h__
 #define CacheFileChunk__h__
 
 #include "CacheFileIOManager.h"
 #include "CacheStorageService.h"
 #include "CacheHashUtils.h"
+#include "CacheFileUtils.h"
 #include "nsAutoPtr.h"
 #include "mozilla/Mutex.h"
 
 namespace mozilla {
 namespace net {
 
 #define kChunkSize        (256 * 1024)
 #define kEmptyChunkHash   0x1826
 
 class CacheFileChunk;
 class CacheFile;
-class ValidityPair;
 
 
 #define CACHEFILECHUNKLISTENER_IID \
 { /* baf16149-2ab5-499c-a9c2-5904eb95c288 */       \
   0xbaf16149,                                      \
   0x2ab5,                                          \
   0x499c,                                          \
   {0xa9, 0xc2, 0x59, 0x04, 0xeb, 0x95, 0xc2, 0x88} \
@@ -137,16 +137,16 @@ private:
   char               *mRWBuf;
   uint32_t            mRWBufSize;
   CacheHash::Hash16_t mReadHash;
 
   nsRefPtr<CacheFile>              mFile; // is null if chunk is cached to
                                           // prevent reference cycles
   nsCOMPtr<CacheFileChunkListener> mListener;
   nsTArray<ChunkListenerItem *>    mUpdateListeners;
-  nsTArray<ValidityPair>           mValidityMap;
+  CacheFileUtils::ValidityMap      mValidityMap;
 };
 
 
 } // net
 } // mozilla
 
 #endif
--- a/netwerk/cache2/CacheFileUtils.cpp
+++ b/netwerk/cache2/CacheFileUtils.cpp
@@ -3,16 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "CacheLog.h"
 #include "CacheFileUtils.h"
 #include "LoadContextInfo.h"
 #include "nsCOMPtr.h"
 #include "nsAutoPtr.h"
 #include "nsString.h"
+#include <algorithm>
 
 
 namespace mozilla {
 namespace net {
 namespace CacheFileUtils {
 
 namespace { // anon
 
@@ -280,11 +281,149 @@ KeyMatchesLoadContextInfo(const nsACStri
   if (!info) {
     return NS_ERROR_FAILURE;
   }
 
   *_retval = info->Equals(aInfo);
   return NS_OK;
 }
 
+ValidityPair::ValidityPair(uint32_t aOffset, uint32_t aLen)
+  : mOffset(aOffset), mLen(aLen)
+{}
+
+ValidityPair&
+ValidityPair::operator=(const ValidityPair& aOther)
+{
+  mOffset = aOther.mOffset;
+  mLen = aOther.mLen;
+  return *this;
+}
+
+bool
+ValidityPair::CanBeMerged(const ValidityPair& aOther) const
+{
+  // The pairs can be merged into a single one if the start of one of the pairs
+  // is placed anywhere in the validity interval of other pair or exactly after
+  // its end.
+  return IsInOrFollows(aOther.mOffset) || aOther.IsInOrFollows(mOffset);
+}
+
+bool
+ValidityPair::IsInOrFollows(uint32_t aOffset) const
+{
+  return mOffset <= aOffset && mOffset + mLen >= aOffset;
+}
+
+bool
+ValidityPair::LessThan(const ValidityPair& aOther) const
+{
+  if (mOffset < aOther.mOffset) {
+    return true;
+  }
+
+  if (mOffset == aOther.mOffset && mLen < aOther.mLen) {
+    return true;
+  }
+
+  return false;
+}
+
+void
+ValidityPair::Merge(const ValidityPair& aOther)
+{
+  MOZ_ASSERT(CanBeMerged(aOther));
+
+  uint32_t offset = std::min(mOffset, aOther.mOffset);
+  uint32_t end = std::max(mOffset + mLen, aOther.mOffset + aOther.mLen);
+
+  mOffset = offset;
+  mLen = end - offset;
+}
+
+void
+ValidityMap::Log() const
+{
+  LOG(("ValidityMap::Log() - number of pairs: %u", mMap.Length()));
+  for (uint32_t i=0; i<mMap.Length(); i++) {
+    LOG(("    (%u, %u)", mMap[i].Offset() + 0, mMap[i].Len() + 0));
+  }
+}
+
+uint32_t
+ValidityMap::Length() const
+{
+  return mMap.Length();
+}
+
+void
+ValidityMap::AddPair(uint32_t aOffset, uint32_t aLen)
+{
+  ValidityPair pair(aOffset, aLen);
+
+  if (mMap.Length() == 0) {
+    mMap.AppendElement(pair);
+    return;
+  }
+
+  // Find out where to place this pair into the map, it can overlap only with
+  // one preceding pair and all subsequent pairs.
+  uint32_t pos = 0;
+  for (pos = mMap.Length(); pos > 0; ) {
+    --pos;
+
+    if (mMap[pos].LessThan(pair)) {
+      // The new pair should be either inserted after pos or merged with it.
+      if (mMap[pos].CanBeMerged(pair)) {
+        // Merge with the preceding pair
+        mMap[pos].Merge(pair);
+      } else {
+        // They don't overlap, element must be placed after pos element
+        ++pos;
+        if (pos == mMap.Length()) {
+          mMap.AppendElement(pair);
+        } else {
+          mMap.InsertElementAt(pos, pair);
+        }
+      }
+
+      break;
+    }
+
+    if (pos == 0) {
+      // The new pair should be placed in front of all existing pairs.
+      mMap.InsertElementAt(0, pair);
+    }
+  }
+
+  // pos now points to merged or inserted pair, check whether it overlaps with
+  // subsequent pairs.
+  while (pos + 1 < mMap.Length()) {
+    if (mMap[pos].CanBeMerged(mMap[pos + 1])) {
+      mMap[pos].Merge(mMap[pos + 1]);
+      mMap.RemoveElementAt(pos + 1);
+    } else {
+      break;
+    }
+  }
+}
+
+void
+ValidityMap::Clear()
+{
+  mMap.Clear();
+}
+
+size_t
+ValidityMap::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
+{
+  return mMap.SizeOfExcludingThis(mallocSizeOf);
+}
+
+ValidityPair&
+ValidityMap::operator[](uint32_t aIdx)
+{
+  return mMap.ElementAt(aIdx);
+}
+
 } // CacheFileUtils
 } // net
 } // mozilla
--- a/netwerk/cache2/CacheFileUtils.h
+++ b/netwerk/cache2/CacheFileUtils.h
@@ -3,16 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef CacheFileUtils__h__
 #define CacheFileUtils__h__
 
 #include "nsError.h"
 #include "nsCOMPtr.h"
 #include "nsString.h"
+#include "nsTArray.h"
 
 class nsILoadContextInfo;
 class nsACString;
 
 namespace mozilla {
 namespace net {
 namespace CacheFileUtils {
 
@@ -27,13 +28,66 @@ AppendKeyPrefix(nsILoadContextInfo *aInf
 void
 AppendTagWithValue(nsACString & aTarget, char const aTag, nsCSubstring const & aValue);
 
 nsresult
 KeyMatchesLoadContextInfo(const nsACString &aKey,
                           nsILoadContextInfo *aInfo,
                           bool *_retval);
 
+class ValidityPair {
+public:
+  ValidityPair(uint32_t aOffset, uint32_t aLen);
+
+  ValidityPair& operator=(const ValidityPair& aOther);
+
+  // Returns true when two pairs can be merged, i.e. they do overlap or the one
+  // ends exactly where the other begins.
+  bool CanBeMerged(const ValidityPair& aOther) const;
+
+  // Returns true when aOffset is placed anywhere in the validity interval or
+  // exactly after its end.
+  bool IsInOrFollows(uint32_t aOffset) const;
+
+  // Returns true when this pair has lower offset than the other pair. In case
+  // both pairs have the same offset it returns true when this pair has a
+  // shorter length.
+  bool LessThan(const ValidityPair& aOther) const;
+
+  // Merges two pair into one.
+  void Merge(const ValidityPair& aOther);
+
+  uint32_t Offset() const { return mOffset; }
+  uint32_t Len() const    { return mLen; }
+
+private:
+  uint32_t mOffset;
+  uint32_t mLen;
+};
+
+class ValidityMap {
+public:
+  // Prints pairs in the map into log.
+  void Log() const;
+
+  // Returns number of pairs in the map.
+  uint32_t Length() const;
+
+  // Adds a new pair to the map. It keeps the pairs ordered and merges pairs
+  // when possible.
+  void AddPair(uint32_t aOffset, uint32_t aLen);
+
+  // Removes all pairs from the map.
+  void Clear();
+
+  size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
+
+  ValidityPair& operator[](uint32_t aIdx);
+
+private:
+  nsTArray<ValidityPair> mMap;
+};
+
 } // CacheFileUtils
 } // net
 } // mozilla
 
 #endif