netwerk/cache2/CacheFileChunk.cpp

Wed, 31 Dec 2014 06:55:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:50 +0100
changeset 2
7e26c7da4463
permissions
-rw-r--r--

Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this
     3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 #include "CacheLog.h"
     6 #include "CacheFileChunk.h"
     8 #include "CacheFile.h"
     9 #include "nsThreadUtils.h"
    10 #include "nsAlgorithm.h"
    11 #include <algorithm>
    13 namespace mozilla {
    14 namespace net {
    16 #define kMinBufSize        512
    18 class NotifyUpdateListenerEvent : public nsRunnable {
    19 public:
    20   NotifyUpdateListenerEvent(CacheFileChunkListener *aCallback,
    21                             CacheFileChunk *aChunk)
    22     : mCallback(aCallback)
    23     , mChunk(aChunk)
    24   {
    25     LOG(("NotifyUpdateListenerEvent::NotifyUpdateListenerEvent() [this=%p]",
    26          this));
    27     MOZ_COUNT_CTOR(NotifyUpdateListenerEvent);
    28   }
    30   ~NotifyUpdateListenerEvent()
    31   {
    32     LOG(("NotifyUpdateListenerEvent::~NotifyUpdateListenerEvent() [this=%p]",
    33          this));
    34     MOZ_COUNT_DTOR(NotifyUpdateListenerEvent);
    35   }
    37   NS_IMETHOD Run()
    38   {
    39     LOG(("NotifyUpdateListenerEvent::Run() [this=%p]", this));
    41     mCallback->OnChunkUpdated(mChunk);
    42     return NS_OK;
    43   }
    45 protected:
    46   nsCOMPtr<CacheFileChunkListener> mCallback;
    47   nsRefPtr<CacheFileChunk>         mChunk;
    48 };
    51 class ValidityPair {
    52 public:
    53   ValidityPair(uint32_t aOffset, uint32_t aLen)
    54     : mOffset(aOffset), mLen(aLen)
    55   {}
    57   ValidityPair& operator=(const ValidityPair& aOther) {
    58     mOffset = aOther.mOffset;
    59     mLen = aOther.mLen;
    60     return *this;
    61   }
    63   bool Overlaps(const ValidityPair& aOther) const {
    64     if ((mOffset <= aOther.mOffset && mOffset + mLen >= aOther.mOffset) ||
    65         (aOther.mOffset <= mOffset && aOther.mOffset + mLen >= mOffset))
    66       return true;
    68     return false;
    69   }
    71   bool LessThan(const ValidityPair& aOther) const {
    72     if (mOffset < aOther.mOffset)
    73       return true;
    75     if (mOffset == aOther.mOffset && mLen < aOther.mLen)
    76       return true;
    78     return false;
    79   }
    81   void Merge(const ValidityPair& aOther) {
    82     MOZ_ASSERT(Overlaps(aOther));
    84     uint32_t offset = std::min(mOffset, aOther.mOffset);
    85     uint32_t end = std::max(mOffset + mLen, aOther.mOffset + aOther.mLen);
    87     mOffset = offset;
    88     mLen = end - offset;
    89   }
    91   uint32_t Offset() { return mOffset; }
    92   uint32_t Len()    { return mLen; }
    94 private:
    95   uint32_t mOffset;
    96   uint32_t mLen;
    97 };
   100 NS_IMPL_ADDREF(CacheFileChunk)
   101 NS_IMETHODIMP_(MozExternalRefCountType)
   102 CacheFileChunk::Release()
   103 {
   104   NS_PRECONDITION(0 != mRefCnt, "dup release");
   105   nsrefcnt count = --mRefCnt;
   106   NS_LOG_RELEASE(this, count, "CacheFileChunk");
   108   if (0 == count) {
   109     mRefCnt = 1;
   110     delete (this);
   111     return 0;
   112   }
   114   if (!mRemovingChunk && count == 1) {
   115     mFile->RemoveChunk(this);
   116   }
   118   return count;
   119 }
   121 NS_INTERFACE_MAP_BEGIN(CacheFileChunk)
   122   NS_INTERFACE_MAP_ENTRY(mozilla::net::CacheFileIOListener)
   123   NS_INTERFACE_MAP_ENTRY(nsISupports)
   124 NS_INTERFACE_MAP_END_THREADSAFE
   126 CacheFileChunk::CacheFileChunk(CacheFile *aFile, uint32_t aIndex)
   127   : CacheMemoryConsumer(aFile->mOpenAsMemoryOnly ? MEMORY_ONLY : DONT_REPORT)
   128   , mIndex(aIndex)
   129   , mState(INITIAL)
   130   , mStatus(NS_OK)
   131   , mIsDirty(false)
   132   , mRemovingChunk(false)
   133   , mDataSize(0)
   134   , mBuf(nullptr)
   135   , mBufSize(0)
   136   , mRWBuf(nullptr)
   137   , mRWBufSize(0)
   138   , mReadHash(0)
   139   , mFile(aFile)
   140 {
   141   LOG(("CacheFileChunk::CacheFileChunk() [this=%p]", this));
   142   MOZ_COUNT_CTOR(CacheFileChunk);
   143 }
   145 CacheFileChunk::~CacheFileChunk()
   146 {
   147   LOG(("CacheFileChunk::~CacheFileChunk() [this=%p]", this));
   148   MOZ_COUNT_DTOR(CacheFileChunk);
   150   if (mBuf) {
   151     free(mBuf);
   152     mBuf = nullptr;
   153     mBufSize = 0;
   154   }
   156   if (mRWBuf) {
   157     free(mRWBuf);
   158     mRWBuf = nullptr;
   159     mRWBufSize = 0;
   160   }
   161 }
   163 void
   164 CacheFileChunk::InitNew(CacheFileChunkListener *aCallback)
   165 {
   166   mFile->AssertOwnsLock();
   168   LOG(("CacheFileChunk::InitNew() [this=%p, listener=%p]", this, aCallback));
   170   MOZ_ASSERT(mState == INITIAL);
   171   MOZ_ASSERT(!mBuf);
   172   MOZ_ASSERT(!mRWBuf);
   174   mBuf = static_cast<char *>(moz_xmalloc(kMinBufSize));
   175   mBufSize = kMinBufSize;
   176   mDataSize = 0;
   177   mState = READY;
   178   mIsDirty = true;
   180   DoMemoryReport(MemorySize());
   181 }
   183 nsresult
   184 CacheFileChunk::Read(CacheFileHandle *aHandle, uint32_t aLen,
   185                      CacheHash::Hash16_t aHash,
   186                      CacheFileChunkListener *aCallback)
   187 {
   188   mFile->AssertOwnsLock();
   190   LOG(("CacheFileChunk::Read() [this=%p, handle=%p, len=%d, listener=%p]",
   191        this, aHandle, aLen, aCallback));
   193   MOZ_ASSERT(mState == INITIAL);
   194   MOZ_ASSERT(!mBuf);
   195   MOZ_ASSERT(!mRWBuf);
   196   MOZ_ASSERT(aLen);
   198   nsresult rv;
   200   mRWBuf = static_cast<char *>(moz_xmalloc(aLen));
   201   mRWBufSize = aLen;
   203   DoMemoryReport(MemorySize());
   205   rv = CacheFileIOManager::Read(aHandle, mIndex * kChunkSize, mRWBuf, aLen,
   206                                 true, this);
   207   if (NS_WARN_IF(NS_FAILED(rv))) {
   208     rv = mIndex ? NS_ERROR_FILE_CORRUPTED : NS_ERROR_FILE_NOT_FOUND;
   209     SetError(rv);
   210   } else {
   211     mState = READING;
   212     mListener = aCallback;
   213     mDataSize = aLen;
   214     mReadHash = aHash;
   215   }
   217   return rv;
   218 }
   220 nsresult
   221 CacheFileChunk::Write(CacheFileHandle *aHandle,
   222                       CacheFileChunkListener *aCallback)
   223 {
   224   mFile->AssertOwnsLock();
   226   LOG(("CacheFileChunk::Write() [this=%p, handle=%p, listener=%p]",
   227        this, aHandle, aCallback));
   229   MOZ_ASSERT(mState == READY);
   230   MOZ_ASSERT(!mRWBuf);
   231   MOZ_ASSERT(mBuf);
   232   MOZ_ASSERT(mDataSize); // Don't write chunk when it is empty
   234   nsresult rv;
   236   mRWBuf = mBuf;
   237   mRWBufSize = mBufSize;
   238   mBuf = nullptr;
   239   mBufSize = 0;
   241   rv = CacheFileIOManager::Write(aHandle, mIndex * kChunkSize, mRWBuf,
   242                                  mDataSize, false, this);
   243   if (NS_WARN_IF(NS_FAILED(rv))) {
   244     SetError(rv);
   245   } else {
   246     mState = WRITING;
   247     mListener = aCallback;
   248     mIsDirty = false;
   249   }
   251   return rv;
   252 }
   254 void
   255 CacheFileChunk::WaitForUpdate(CacheFileChunkListener *aCallback)
   256 {
   257   mFile->AssertOwnsLock();
   259   LOG(("CacheFileChunk::WaitForUpdate() [this=%p, listener=%p]",
   260        this, aCallback));
   262   MOZ_ASSERT(mFile->mOutput);
   263   MOZ_ASSERT(IsReady());
   265 #ifdef DEBUG
   266   for (uint32_t i = 0 ; i < mUpdateListeners.Length() ; i++) {
   267     MOZ_ASSERT(mUpdateListeners[i]->mCallback != aCallback);
   268   }
   269 #endif
   271   ChunkListenerItem *item = new ChunkListenerItem();
   272   item->mTarget = NS_GetCurrentThread();
   273   item->mCallback = aCallback;
   275   mUpdateListeners.AppendElement(item);
   276 }
   278 nsresult
   279 CacheFileChunk::CancelWait(CacheFileChunkListener *aCallback)
   280 {
   281   mFile->AssertOwnsLock();
   283   LOG(("CacheFileChunk::CancelWait() [this=%p, listener=%p]", this, aCallback));
   285   MOZ_ASSERT(IsReady());
   287   uint32_t i;
   288   for (i = 0 ; i < mUpdateListeners.Length() ; i++) {
   289     ChunkListenerItem *item = mUpdateListeners[i];
   291     if (item->mCallback == aCallback) {
   292       mUpdateListeners.RemoveElementAt(i);
   293       delete item;
   294       break;
   295     }
   296   }
   298 #ifdef DEBUG
   299   for ( ; i < mUpdateListeners.Length() ; i++) {
   300     MOZ_ASSERT(mUpdateListeners[i]->mCallback != aCallback);
   301   }
   302 #endif
   304   return NS_OK;
   305 }
   307 nsresult
   308 CacheFileChunk::NotifyUpdateListeners()
   309 {
   310   mFile->AssertOwnsLock();
   312   LOG(("CacheFileChunk::NotifyUpdateListeners() [this=%p]", this));
   314   MOZ_ASSERT(IsReady());
   316   nsresult rv, rv2;
   318   rv = NS_OK;
   319   for (uint32_t i = 0 ; i < mUpdateListeners.Length() ; i++) {
   320     ChunkListenerItem *item = mUpdateListeners[i];
   322     LOG(("CacheFileChunk::NotifyUpdateListeners() - Notifying listener %p "
   323          "[this=%p]", item->mCallback.get(), this));
   325     nsRefPtr<NotifyUpdateListenerEvent> ev;
   326     ev = new NotifyUpdateListenerEvent(item->mCallback, this);
   327     rv2 = item->mTarget->Dispatch(ev, NS_DISPATCH_NORMAL);
   328     if (NS_FAILED(rv2) && NS_SUCCEEDED(rv))
   329       rv = rv2;
   330     delete item;
   331   }
   333   mUpdateListeners.Clear();
   335   return rv;
   336 }
   338 uint32_t
   339 CacheFileChunk::Index()
   340 {
   341   return mIndex;
   342 }
   344 CacheHash::Hash16_t
   345 CacheFileChunk::Hash()
   346 {
   347   mFile->AssertOwnsLock();
   349   MOZ_ASSERT(mBuf);
   350   MOZ_ASSERT(!mListener);
   351   MOZ_ASSERT(IsReady());
   353   return CacheHash::Hash16(BufForReading(), mDataSize);
   354 }
   356 uint32_t
   357 CacheFileChunk::DataSize()
   358 {
   359   mFile->AssertOwnsLock();
   360   return mDataSize;
   361 }
   363 void
   364 CacheFileChunk::UpdateDataSize(uint32_t aOffset, uint32_t aLen, bool aEOF)
   365 {
   366   mFile->AssertOwnsLock();
   368   MOZ_ASSERT(!aEOF, "Implement me! What to do with opened streams?");
   369   MOZ_ASSERT(aOffset <= mDataSize);
   371   // UpdateDataSize() is called only when we've written some data to the chunk
   372   // and we never write data anymore once some error occurs.
   373   MOZ_ASSERT(mState != ERROR);
   375   LOG(("CacheFileChunk::UpdateDataSize() [this=%p, offset=%d, len=%d, EOF=%d]",
   376        this, aOffset, aLen, aEOF));
   378   mIsDirty = true;
   380   int64_t fileSize = kChunkSize * mIndex + aOffset + aLen;
   381   bool notify = false;
   383   if (fileSize > mFile->mDataSize)
   384     mFile->mDataSize = fileSize;
   386   if (aOffset + aLen > mDataSize) {
   387     mDataSize = aOffset + aLen;
   388     notify = true;
   389   }
   391   if (mState == READY || mState == WRITING) {
   392     MOZ_ASSERT(mValidityMap.Length() == 0);
   394     if (notify)
   395       NotifyUpdateListeners();
   397     return;
   398   }
   400   // We're still waiting for data from the disk. This chunk cannot be used by
   401   // input stream, so there must be no update listener. We also need to keep
   402   // track of where the data is written so that we can correctly merge the new
   403   // data with the old one.
   405   MOZ_ASSERT(mUpdateListeners.Length() == 0);
   406   MOZ_ASSERT(mState == READING);
   408   ValidityPair pair(aOffset, aLen);
   410   if (mValidityMap.Length() == 0) {
   411     mValidityMap.AppendElement(pair);
   412     return;
   413   }
   416   // Find out where to place this pair into the map, it can overlap with
   417   // one preceding pair and all subsequent pairs.
   418   uint32_t pos = 0;
   419   for (pos = mValidityMap.Length() ; pos > 0 ; pos--) {
   420     if (mValidityMap[pos-1].LessThan(pair)) {
   421       if (mValidityMap[pos-1].Overlaps(pair)) {
   422         // Merge with the preceding pair
   423         mValidityMap[pos-1].Merge(pair);
   424         pos--; // Point to the updated pair
   425       }
   426       else {
   427         if (pos == mValidityMap.Length())
   428           mValidityMap.AppendElement(pair);
   429         else
   430           mValidityMap.InsertElementAt(pos, pair);
   431       }
   433       break;
   434     }
   435   }
   437   if (!pos)
   438     mValidityMap.InsertElementAt(0, pair);
   440   // Now pos points to merged or inserted pair, check whether it overlaps with
   441   // subsequent pairs.
   442   while (pos + 1 < mValidityMap.Length()) {
   443     if (mValidityMap[pos].Overlaps(mValidityMap[pos + 1])) {
   444       mValidityMap[pos].Merge(mValidityMap[pos + 1]);
   445       mValidityMap.RemoveElementAt(pos + 1);
   446     }
   447     else {
   448       break;
   449     }
   450   }
   451 }
   453 nsresult
   454 CacheFileChunk::OnFileOpened(CacheFileHandle *aHandle, nsresult aResult)
   455 {
   456   MOZ_CRASH("CacheFileChunk::OnFileOpened should not be called!");
   457   return NS_ERROR_UNEXPECTED;
   458 }
   460 nsresult
   461 CacheFileChunk::OnDataWritten(CacheFileHandle *aHandle, const char *aBuf,
   462                               nsresult aResult)
   463 {
   464   LOG(("CacheFileChunk::OnDataWritten() [this=%p, handle=%p, result=0x%08x]",
   465        this, aHandle, aResult));
   467   nsCOMPtr<CacheFileChunkListener> listener;
   469   {
   470     CacheFileAutoLock lock(mFile);
   472     MOZ_ASSERT(mState == WRITING);
   473     MOZ_ASSERT(mListener);
   475     if (NS_WARN_IF(NS_FAILED(aResult))) {
   476       SetError(aResult);
   477     } else {
   478       mState = READY;
   479     }
   481     if (!mBuf) {
   482       mBuf = mRWBuf;
   483       mBufSize = mRWBufSize;
   484     } else {
   485       free(mRWBuf);
   486     }
   488     mRWBuf = nullptr;
   489     mRWBufSize = 0;
   491     DoMemoryReport(MemorySize());
   493     mListener.swap(listener);
   494   }
   496   listener->OnChunkWritten(aResult, this);
   498   return NS_OK;
   499 }
   501 nsresult
   502 CacheFileChunk::OnDataRead(CacheFileHandle *aHandle, char *aBuf,
   503                            nsresult aResult)
   504 {
   505   LOG(("CacheFileChunk::OnDataRead() [this=%p, handle=%p, result=0x%08x]",
   506        this, aHandle, aResult));
   508   nsCOMPtr<CacheFileChunkListener> listener;
   510   {
   511     CacheFileAutoLock lock(mFile);
   513     MOZ_ASSERT(mState == READING);
   514     MOZ_ASSERT(mListener);
   516     if (NS_SUCCEEDED(aResult)) {
   517       CacheHash::Hash16_t hash = CacheHash::Hash16(mRWBuf, mRWBufSize);
   518       if (hash != mReadHash) {
   519         LOG(("CacheFileChunk::OnDataRead() - Hash mismatch! Hash of the data is"
   520              " %hx, hash in metadata is %hx. [this=%p, idx=%d]",
   521              hash, mReadHash, this, mIndex));
   522         aResult = NS_ERROR_FILE_CORRUPTED;
   523       }
   524       else {
   525         if (!mBuf) {
   526           // Just swap the buffers if we don't have mBuf yet
   527           MOZ_ASSERT(mDataSize == mRWBufSize);
   528           mBuf = mRWBuf;
   529           mBufSize = mRWBufSize;
   530           mRWBuf = nullptr;
   531           mRWBufSize = 0;
   532         } else {
   533           // Merge data with write buffer
   534           if (mRWBufSize < mBufSize) {
   535             mRWBuf = static_cast<char *>(moz_xrealloc(mRWBuf, mBufSize));
   536             mRWBufSize = mBufSize;
   537           }
   539           for (uint32_t i = 0 ; i < mValidityMap.Length() ; i++) {
   540             memcpy(mRWBuf + mValidityMap[i].Offset(),
   541                    mBuf + mValidityMap[i].Offset(), mValidityMap[i].Len());
   542           }
   544           free(mBuf);
   545           mBuf = mRWBuf;
   546           mBufSize = mRWBufSize;
   547           mRWBuf = nullptr;
   548           mRWBufSize = 0;
   550           DoMemoryReport(MemorySize());
   551         }
   552       }
   553     }
   555     if (NS_FAILED(aResult)) {
   556       aResult = mIndex ? NS_ERROR_FILE_CORRUPTED : NS_ERROR_FILE_NOT_FOUND;
   557       SetError(aResult);
   558       mDataSize = 0;
   559     } else {
   560       mState = READY;
   561     }
   563     mListener.swap(listener);
   564   }
   566   listener->OnChunkRead(aResult, this);
   568   return NS_OK;
   569 }
   571 nsresult
   572 CacheFileChunk::OnFileDoomed(CacheFileHandle *aHandle, nsresult aResult)
   573 {
   574   MOZ_CRASH("CacheFileChunk::OnFileDoomed should not be called!");
   575   return NS_ERROR_UNEXPECTED;
   576 }
   578 nsresult
   579 CacheFileChunk::OnEOFSet(CacheFileHandle *aHandle, nsresult aResult)
   580 {
   581   MOZ_CRASH("CacheFileChunk::OnEOFSet should not be called!");
   582   return NS_ERROR_UNEXPECTED;
   583 }
   585 nsresult
   586 CacheFileChunk::OnFileRenamed(CacheFileHandle *aHandle, nsresult aResult)
   587 {
   588   MOZ_CRASH("CacheFileChunk::OnFileRenamed should not be called!");
   589   return NS_ERROR_UNEXPECTED;
   590 }
   592 bool
   593 CacheFileChunk::IsReady() const
   594 {
   595   mFile->AssertOwnsLock();
   597   return (NS_SUCCEEDED(mStatus) && (mState == READY || mState == WRITING));
   598 }
   600 bool
   601 CacheFileChunk::IsDirty() const
   602 {
   603   mFile->AssertOwnsLock();
   605   return mIsDirty;
   606 }
   608 nsresult
   609 CacheFileChunk::GetStatus()
   610 {
   611   mFile->AssertOwnsLock();
   613   return mStatus;
   614 }
   616 void
   617 CacheFileChunk::SetError(nsresult aStatus)
   618 {
   619   if (NS_SUCCEEDED(mStatus)) {
   620     MOZ_ASSERT(mState != ERROR);
   621     mStatus = aStatus;
   622     mState = ERROR;
   623   } else {
   624     MOZ_ASSERT(mState == ERROR);
   625   }
   626 }
   628 char *
   629 CacheFileChunk::BufForWriting() const
   630 {
   631   mFile->AssertOwnsLock();
   633   MOZ_ASSERT(mBuf); // Writer should always first call EnsureBufSize()
   635   MOZ_ASSERT((mState == READY && !mRWBuf) ||
   636              (mState == WRITING && mRWBuf) ||
   637              (mState == READING && mRWBuf));
   639   return mBuf;
   640 }
   642 const char *
   643 CacheFileChunk::BufForReading() const
   644 {
   645   mFile->AssertOwnsLock();
   647   MOZ_ASSERT((mState == READY && mBuf && !mRWBuf) ||
   648              (mState == WRITING && mRWBuf));
   650   return mBuf ? mBuf : mRWBuf;
   651 }
   653 void
   654 CacheFileChunk::EnsureBufSize(uint32_t aBufSize)
   655 {
   656   mFile->AssertOwnsLock();
   658   // EnsureBufSize() is called only when we want to write some data to the chunk
   659   // and we never write data anymore once some error occurs.
   660   MOZ_ASSERT(mState != ERROR);
   662   if (mBufSize >= aBufSize)
   663     return;
   665   bool copy = false;
   666   if (!mBuf && mState == WRITING) {
   667     // We need to duplicate the data that is being written on the background
   668     // thread, so make sure that all the data fits into the new buffer.
   669     copy = true;
   671     if (mRWBufSize > aBufSize)
   672       aBufSize = mRWBufSize;
   673   }
   675   // find smallest power of 2 greater than or equal to aBufSize
   676   aBufSize--;
   677   aBufSize |= aBufSize >> 1;
   678   aBufSize |= aBufSize >> 2;
   679   aBufSize |= aBufSize >> 4;
   680   aBufSize |= aBufSize >> 8;
   681   aBufSize |= aBufSize >> 16;
   682   aBufSize++;
   684   const uint32_t minBufSize = kMinBufSize;
   685   const uint32_t maxBufSize = kChunkSize;
   686   aBufSize = clamped(aBufSize, minBufSize, maxBufSize);
   688   mBuf = static_cast<char *>(moz_xrealloc(mBuf, aBufSize));
   689   mBufSize = aBufSize;
   691   if (copy)
   692     memcpy(mBuf, mRWBuf, mRWBufSize);
   694   DoMemoryReport(MemorySize());
   695 }
   697 // Memory reporting
   699 size_t
   700 CacheFileChunk::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
   701 {
   702   size_t n = 0;
   703   n += mallocSizeOf(mBuf);
   704   n += mallocSizeOf(mRWBuf);
   705   n += mValidityMap.SizeOfExcludingThis(mallocSizeOf);
   707   return n;
   708 }
   710 size_t
   711 CacheFileChunk::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
   712 {
   713   return mallocSizeOf(this) + SizeOfExcludingThis(mallocSizeOf);
   714 }
   716 } // net
   717 } // mozilla

mercurial