LCOV - code coverage report
Current view: top level - netwerk/cache2 - CacheFileChunk.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 288 439 65.6 %
Date: 2017-07-14 16:53:18 Functions: 43 60 71.7 %
Legend: Lines: hit not hit

          Line data    Source code
       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/. */
       4             : 
       5             : #include "CacheLog.h"
       6             : #include "CacheFileChunk.h"
       7             : 
       8             : #include "CacheFile.h"
       9             : #include "nsThreadUtils.h"
      10             : 
      11             : #include "mozilla/IntegerPrintfMacros.h"
      12             : 
      13             : namespace mozilla {
      14             : namespace net {
      15             : 
      16             : #define kMinBufSize        512
      17             : 
      18           8 : CacheFileChunkBuffer::CacheFileChunkBuffer(CacheFileChunk *aChunk)
      19             :   : mChunk(aChunk)
      20             :   , mBuf(nullptr)
      21             :   , mBufSize(0)
      22             :   , mDataSize(0)
      23             :   , mReadHandlesCount(0)
      24           8 :   , mWriteHandleExists(false)
      25             : {
      26           8 : }
      27             : 
      28          16 : CacheFileChunkBuffer::~CacheFileChunkBuffer()
      29             : {
      30           8 :   if (mBuf) {
      31           4 :     CacheFileUtils::FreeBuffer(mBuf);
      32           4 :     mBuf = nullptr;
      33           4 :     mChunk->BuffersAllocationChanged(mBufSize, 0);
      34           4 :     mBufSize = 0;
      35             :   }
      36           8 : }
      37             : 
      38             : void
      39           0 : CacheFileChunkBuffer::CopyFrom(CacheFileChunkBuffer *aOther)
      40             : {
      41           0 :   MOZ_RELEASE_ASSERT(mBufSize >= aOther->mDataSize);
      42           0 :   mDataSize = aOther->mDataSize;
      43           0 :   memcpy(mBuf, aOther->mBuf, mDataSize);
      44           0 : }
      45             : 
      46             : nsresult
      47           0 : CacheFileChunkBuffer::FillInvalidRanges(CacheFileChunkBuffer *aOther,
      48             :                                         CacheFileUtils::ValidityMap *aMap)
      49             : {
      50             :   nsresult rv;
      51             : 
      52           0 :   rv = EnsureBufSize(aOther->mDataSize);
      53           0 :   if (NS_FAILED(rv)) {
      54           0 :     return rv;
      55             :   }
      56             : 
      57           0 :   uint32_t invalidOffset = 0;
      58             :   uint32_t invalidLength;
      59             : 
      60           0 :   for (uint32_t i = 0; i < aMap->Length(); ++i) {
      61           0 :     uint32_t validOffset = (*aMap)[i].Offset();
      62           0 :     uint32_t validLength = (*aMap)[i].Len();
      63             : 
      64           0 :     MOZ_RELEASE_ASSERT(invalidOffset <= validOffset);
      65           0 :     invalidLength = validOffset - invalidOffset;
      66           0 :     if (invalidLength > 0) {
      67           0 :       MOZ_RELEASE_ASSERT(invalidOffset + invalidLength <= aOther->mDataSize);
      68           0 :       memcpy(mBuf + invalidOffset, aOther->mBuf + invalidOffset, invalidLength);
      69             :     }
      70           0 :     invalidOffset = validOffset + validLength;
      71             :   }
      72             : 
      73           0 :   if (invalidOffset < aOther->mDataSize) {
      74           0 :     invalidLength = aOther->mDataSize - invalidOffset;
      75           0 :     memcpy(mBuf + invalidOffset, aOther->mBuf + invalidOffset, invalidLength);
      76             :   }
      77             : 
      78           0 :   return NS_OK;
      79             : }
      80             : 
      81             : MOZ_MUST_USE nsresult
      82           4 : CacheFileChunkBuffer::EnsureBufSize(uint32_t aBufSize)
      83             : {
      84           4 :   AssertOwnsLock();
      85             : 
      86           4 :   if (mBufSize >= aBufSize) {
      87           0 :     return NS_OK;
      88             :   }
      89             : 
      90             :   // find smallest power of 2 greater than or equal to aBufSize
      91           4 :   aBufSize--;
      92           4 :   aBufSize |= aBufSize >> 1;
      93           4 :   aBufSize |= aBufSize >> 2;
      94           4 :   aBufSize |= aBufSize >> 4;
      95           4 :   aBufSize |= aBufSize >> 8;
      96           4 :   aBufSize |= aBufSize >> 16;
      97           4 :   aBufSize++;
      98             : 
      99           4 :   const uint32_t minBufSize = kMinBufSize;
     100           4 :   const uint32_t maxBufSize = kChunkSize;
     101           4 :   aBufSize = clamped(aBufSize, minBufSize, maxBufSize);
     102             : 
     103           4 :   if (!mChunk->CanAllocate(aBufSize - mBufSize)) {
     104           0 :     return NS_ERROR_OUT_OF_MEMORY;
     105             :   }
     106             : 
     107           4 :   char *newBuf = static_cast<char *>(realloc(mBuf, aBufSize));
     108           4 :   if (!newBuf) {
     109           0 :     return NS_ERROR_OUT_OF_MEMORY;
     110             :   }
     111             : 
     112           4 :   mChunk->BuffersAllocationChanged(mBufSize, aBufSize);
     113           4 :   mBuf = newBuf;
     114           4 :   mBufSize = aBufSize;
     115             : 
     116           4 :   return NS_OK;
     117             : }
     118             : 
     119             : void
     120           4 : CacheFileChunkBuffer::SetDataSize(uint32_t aDataSize)
     121             : {
     122           4 :   MOZ_RELEASE_ASSERT(
     123             :     // EnsureBufSize must be called before SetDataSize, so the new data size
     124             :     // is guaranteed to be smaller than or equal to mBufSize.
     125             :     aDataSize <= mBufSize ||
     126             :     // The only exception is an optimization when we read the data from the
     127             :     // disk. The data is read to a separate buffer and CacheFileChunk::mBuf is
     128             :     // empty (see CacheFileChunk::Read). We need to set mBuf::mDataSize
     129             :     // accordingly so that DataSize() methods return correct value, but we don't
     130             :     // want to allocate the buffer since it wouldn't be used in most cases.
     131             :     (mBufSize == 0 && mChunk->mState == CacheFileChunk::READING));
     132             : 
     133           4 :   mDataSize = aDataSize;
     134           4 : }
     135             : 
     136             : void
     137          51 : CacheFileChunkBuffer::AssertOwnsLock() const
     138             : {
     139          51 :   mChunk->AssertOwnsLock();
     140          51 : }
     141             : 
     142             : void
     143          13 : CacheFileChunkBuffer::RemoveReadHandle()
     144             : {
     145          13 :   AssertOwnsLock();
     146          13 :   MOZ_RELEASE_ASSERT(mReadHandlesCount);
     147          13 :   MOZ_RELEASE_ASSERT(!mWriteHandleExists);
     148          13 :   mReadHandlesCount--;
     149             : 
     150          13 :   if (mReadHandlesCount == 0 && mChunk->mBuf != this) {
     151           0 :     DebugOnly<bool> removed = mChunk->mOldBufs.RemoveElement(this);
     152           0 :     MOZ_ASSERT(removed);
     153             :   }
     154          13 : }
     155             : 
     156             : void
     157           2 : CacheFileChunkBuffer::RemoveWriteHandle()
     158             : {
     159           2 :   AssertOwnsLock();
     160           2 :   MOZ_RELEASE_ASSERT(mReadHandlesCount == 0);
     161           2 :   MOZ_RELEASE_ASSERT(mWriteHandleExists);
     162           2 :   mWriteHandleExists = false;
     163           2 : }
     164             : 
     165             : size_t
     166           0 : CacheFileChunkBuffer::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
     167             : {
     168           0 :   size_t n = mallocSizeOf(this);
     169             : 
     170           0 :   if (mBuf) {
     171           0 :     n += mallocSizeOf(mBuf);
     172             :   }
     173             : 
     174           0 :   return n;
     175             : }
     176             : 
     177             : uint32_t
     178          15 : CacheFileChunkHandle::DataSize()
     179             : {
     180          15 :   MOZ_ASSERT(mBuf, "Unexpected call on dummy handle");
     181          15 :   mBuf->AssertOwnsLock();
     182          15 :   return mBuf->mDataSize;
     183             : }
     184             : 
     185             : uint32_t
     186          17 : CacheFileChunkHandle::Offset()
     187             : {
     188          17 :   MOZ_ASSERT(mBuf, "Unexpected call on dummy handle");
     189          17 :   mBuf->AssertOwnsLock();
     190          17 :   return mBuf->mChunk->Index() * kChunkSize;
     191             : }
     192             : 
     193          13 : CacheFileChunkReadHandle::CacheFileChunkReadHandle(CacheFileChunkBuffer *aBuf)
     194             : {
     195          13 :   mBuf = aBuf;
     196          13 :   mBuf->mReadHandlesCount++;
     197          13 : }
     198             : 
     199          26 : CacheFileChunkReadHandle::~CacheFileChunkReadHandle()
     200             : {
     201          13 :   mBuf->RemoveReadHandle();
     202          13 : }
     203             : 
     204             : const char *
     205           8 : CacheFileChunkReadHandle::Buf()
     206             : {
     207           8 :   return mBuf->mBuf;
     208             : }
     209             : 
     210           2 : CacheFileChunkWriteHandle::CacheFileChunkWriteHandle(CacheFileChunkBuffer *aBuf)
     211             : {
     212           2 :   mBuf = aBuf;
     213           2 :   if (mBuf) {
     214           2 :     MOZ_ASSERT(!mBuf->mWriteHandleExists);
     215           2 :     mBuf->mWriteHandleExists = true;
     216             :   }
     217           2 : }
     218             : 
     219           4 : CacheFileChunkWriteHandle::~CacheFileChunkWriteHandle()
     220             : {
     221           2 :   if (mBuf) {
     222           2 :     mBuf->RemoveWriteHandle();
     223             :   }
     224           2 : }
     225             : 
     226             : char *
     227           4 : CacheFileChunkWriteHandle::Buf()
     228             : {
     229           4 :   return mBuf ? mBuf->mBuf : nullptr;
     230             : }
     231             : 
     232             : void
     233           2 : CacheFileChunkWriteHandle::UpdateDataSize(uint32_t aOffset, uint32_t aLen)
     234             : {
     235           2 :   MOZ_ASSERT(mBuf, "Write performed on dummy handle?");
     236           2 :   MOZ_ASSERT(aOffset <= mBuf->mDataSize);
     237           2 :   MOZ_ASSERT(aOffset + aLen <= mBuf->mBufSize);
     238             : 
     239           2 :   if (aOffset + aLen > mBuf->mDataSize) {
     240           2 :     mBuf->mDataSize = aOffset + aLen;
     241             :   }
     242             : 
     243           2 :   mBuf->mChunk->UpdateDataSize(aOffset, aLen);
     244           2 : }
     245             : 
     246             : 
     247             : class NotifyUpdateListenerEvent : public Runnable {
     248             : public:
     249           0 :   NotifyUpdateListenerEvent(CacheFileChunkListener* aCallback,
     250             :                             CacheFileChunk* aChunk)
     251           0 :     : Runnable("net::NotifyUpdateListenerEvent")
     252             :     , mCallback(aCallback)
     253           0 :     , mChunk(aChunk)
     254             :   {
     255           0 :     LOG(("NotifyUpdateListenerEvent::NotifyUpdateListenerEvent() [this=%p]",
     256             :          this));
     257           0 :   }
     258             : 
     259             : protected:
     260           0 :   ~NotifyUpdateListenerEvent()
     261           0 :   {
     262           0 :     LOG(("NotifyUpdateListenerEvent::~NotifyUpdateListenerEvent() [this=%p]",
     263             :          this));
     264           0 :   }
     265             : 
     266             : public:
     267           0 :   NS_IMETHOD Run() override
     268             :   {
     269           0 :     LOG(("NotifyUpdateListenerEvent::Run() [this=%p]", this));
     270             : 
     271           0 :     mCallback->OnChunkUpdated(mChunk);
     272           0 :     return NS_OK;
     273             :   }
     274             : 
     275             : protected:
     276             :   nsCOMPtr<CacheFileChunkListener> mCallback;
     277             :   RefPtr<CacheFileChunk>           mChunk;
     278             : };
     279             : 
     280             : bool
     281          53 : CacheFileChunk::DispatchRelease()
     282             : {
     283          53 :   if (NS_IsMainThread()) {
     284          38 :     return false;
     285             :   }
     286             : 
     287          30 :   NS_DispatchToMainThread(NewNonOwningRunnableMethod(
     288          15 :     "net::CacheFileChunk::Release", this, &CacheFileChunk::Release));
     289             : 
     290          15 :   return true;
     291             : }
     292             : 
     293          38 : NS_IMPL_ADDREF(CacheFileChunk)
     294             : NS_IMETHODIMP_(MozExternalRefCountType)
     295          53 : CacheFileChunk::Release()
     296             : {
     297          53 :   nsrefcnt count = mRefCnt - 1;
     298          53 :   if (DispatchRelease()) {
     299             :     // Redispatched to the main thread.
     300          15 :     return count;
     301             :   }
     302             : 
     303          38 :   NS_PRECONDITION(0 != mRefCnt, "dup release");
     304          38 :   count = --mRefCnt;
     305          38 :   NS_LOG_RELEASE(this, count, "CacheFileChunk");
     306             : 
     307          38 :   if (0 == count) {
     308           4 :     mRefCnt = 1;
     309           4 :     delete (this);
     310           4 :     return 0;
     311             :   }
     312             : 
     313             :   // We can safely access this chunk after decreasing mRefCnt since we re-post
     314             :   // all calls to Release() happening off the main thread to the main thread.
     315             :   // I.e. no other Release() that would delete the object could be run before
     316             :   // we call CacheFile::DeactivateChunk().
     317             :   //
     318             :   // NOTE: we don't grab the CacheFile's lock, so the chunk might be addrefed
     319             :   // on another thread before CacheFile::DeactivateChunk() grabs the lock on
     320             :   // this thread. To make sure we won't deactivate chunk that was just returned
     321             :   // to a new consumer we check mRefCnt once again in
     322             :   // CacheFile::DeactivateChunk() after we grab the lock.
     323          34 :   if (mActiveChunk && count == 1) {
     324           5 :     mFile->DeactivateChunk(this);
     325             :   }
     326             : 
     327          34 :   return count;
     328             : }
     329             : 
     330           4 : NS_INTERFACE_MAP_BEGIN(CacheFileChunk)
     331           4 :   NS_INTERFACE_MAP_ENTRY(mozilla::net::CacheFileIOListener)
     332           0 :   NS_INTERFACE_MAP_ENTRY(nsISupports)
     333           0 : NS_INTERFACE_MAP_END_THREADSAFE
     334             : 
     335           4 : CacheFileChunk::CacheFileChunk(CacheFile *aFile, uint32_t aIndex,
     336           4 :                                bool aInitByWriter)
     337           4 :   : CacheMemoryConsumer(aFile->mOpenAsMemoryOnly ? MEMORY_ONLY : DONT_REPORT)
     338             :   , mIndex(aIndex)
     339             :   , mState(INITIAL)
     340             :   , mStatus(NS_OK)
     341             :   , mActiveChunk(false)
     342             :   , mIsDirty(false)
     343             :   , mDiscardedChunk(false)
     344             :   , mBuffersSize(0)
     345           4 :   , mLimitAllocation(!aFile->mOpenAsMemoryOnly && aInitByWriter)
     346           4 :   , mIsPriority(aFile->mPriority)
     347             :   , mExpectedHash(0)
     348          16 :   , mFile(aFile)
     349             : {
     350           4 :   LOG(("CacheFileChunk::CacheFileChunk() [this=%p, index=%u, initByWriter=%d]",
     351             :        this, aIndex, aInitByWriter));
     352           4 :   mBuf = new CacheFileChunkBuffer(this);
     353           4 : }
     354             : 
     355          12 : CacheFileChunk::~CacheFileChunk()
     356             : {
     357           4 :   LOG(("CacheFileChunk::~CacheFileChunk() [this=%p]", this));
     358          12 : }
     359             : 
     360             : void
     361          92 : CacheFileChunk::AssertOwnsLock() const
     362             : {
     363          92 :   mFile->AssertOwnsLock();
     364          92 : }
     365             : 
     366             : void
     367           2 : CacheFileChunk::InitNew()
     368             : {
     369           2 :   AssertOwnsLock();
     370             : 
     371           2 :   LOG(("CacheFileChunk::InitNew() [this=%p]", this));
     372             : 
     373           2 :   MOZ_ASSERT(mState == INITIAL);
     374           2 :   MOZ_ASSERT(NS_SUCCEEDED(mStatus));
     375           2 :   MOZ_ASSERT(!mBuf->Buf());
     376           2 :   MOZ_ASSERT(!mWritingStateHandle);
     377           2 :   MOZ_ASSERT(!mReadingStateBuf);
     378           2 :   MOZ_ASSERT(!mIsDirty);
     379             : 
     380           2 :   mBuf = new CacheFileChunkBuffer(this);
     381           2 :   mState = READY;
     382           2 : }
     383             : 
     384             : nsresult
     385           2 : CacheFileChunk::Read(CacheFileHandle *aHandle, uint32_t aLen,
     386             :                      CacheHash::Hash16_t aHash,
     387             :                      CacheFileChunkListener *aCallback)
     388             : {
     389           2 :   AssertOwnsLock();
     390             : 
     391           2 :   LOG(("CacheFileChunk::Read() [this=%p, handle=%p, len=%d, listener=%p]",
     392             :        this, aHandle, aLen, aCallback));
     393             : 
     394           2 :   MOZ_ASSERT(mState == INITIAL);
     395           2 :   MOZ_ASSERT(NS_SUCCEEDED(mStatus));
     396           2 :   MOZ_ASSERT(!mBuf->Buf());
     397           2 :   MOZ_ASSERT(!mWritingStateHandle);
     398           2 :   MOZ_ASSERT(!mReadingStateBuf);
     399           2 :   MOZ_ASSERT(aLen);
     400             : 
     401             :   nsresult rv;
     402             : 
     403           2 :   mState = READING;
     404             : 
     405           4 :   RefPtr<CacheFileChunkBuffer> tmpBuf = new CacheFileChunkBuffer(this);
     406           2 :   rv = tmpBuf->EnsureBufSize(aLen);
     407           2 :   if (NS_FAILED(rv)) {
     408           0 :     SetError(rv);
     409           0 :     return mStatus;
     410             :   }
     411           2 :   tmpBuf->SetDataSize(aLen);
     412             : 
     413           2 :   rv = CacheFileIOManager::Read(aHandle, mIndex * kChunkSize,
     414             :                                 tmpBuf->Buf(), aLen,
     415           2 :                                 this);
     416           2 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     417           0 :     rv = mIndex ? NS_ERROR_FILE_CORRUPTED : NS_ERROR_FILE_NOT_FOUND;
     418           0 :     SetError(rv);
     419             :   } else {
     420           2 :     mReadingStateBuf.swap(tmpBuf);
     421           2 :     mListener = aCallback;
     422             :     // mBuf contains no data but we set datasize to size of the data that will
     423             :     // be read from the disk. No handle is allowed to access the non-existent
     424             :     // data until reading finishes, but data can be appended or overwritten.
     425             :     // These pieces are tracked in mValidityMap and will be merged with the data
     426             :     // read from disk in OnDataRead().
     427           2 :     mBuf->SetDataSize(aLen);
     428           2 :     mExpectedHash = aHash;
     429             :   }
     430             : 
     431           2 :   return rv;
     432             : }
     433             : 
     434             : nsresult
     435           2 : CacheFileChunk::Write(CacheFileHandle *aHandle,
     436             :                       CacheFileChunkListener *aCallback)
     437             : {
     438           2 :   AssertOwnsLock();
     439             : 
     440           2 :   LOG(("CacheFileChunk::Write() [this=%p, handle=%p, listener=%p]",
     441             :        this, aHandle, aCallback));
     442             : 
     443           2 :   MOZ_ASSERT(mState == READY);
     444           2 :   MOZ_ASSERT(NS_SUCCEEDED(mStatus));
     445           2 :   MOZ_ASSERT(!mWritingStateHandle);
     446           2 :   MOZ_ASSERT(mBuf->DataSize()); // Don't write chunk when it is empty
     447           2 :   MOZ_ASSERT(mBuf->ReadHandlesCount() == 0);
     448           2 :   MOZ_ASSERT(!mBuf->WriteHandleExists());
     449             : 
     450             :   nsresult rv;
     451             : 
     452           2 :   mState = WRITING;
     453           4 :   mWritingStateHandle = new CacheFileChunkReadHandle(mBuf);
     454             : 
     455           4 :   rv = CacheFileIOManager::Write(aHandle, mIndex * kChunkSize,
     456             :                                  mWritingStateHandle->Buf(),
     457           2 :                                  mWritingStateHandle->DataSize(),
     458           2 :                                  false, false, this);
     459           2 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     460           0 :     mWritingStateHandle = nullptr;
     461           0 :     SetError(rv);
     462             :   } else {
     463           2 :     mListener = aCallback;
     464           2 :     mIsDirty = false;
     465             :   }
     466             : 
     467           2 :   return rv;
     468             : }
     469             : 
     470             : void
     471           0 : CacheFileChunk::WaitForUpdate(CacheFileChunkListener *aCallback)
     472             : {
     473           0 :   AssertOwnsLock();
     474             : 
     475           0 :   LOG(("CacheFileChunk::WaitForUpdate() [this=%p, listener=%p]",
     476             :        this, aCallback));
     477             : 
     478           0 :   MOZ_ASSERT(mFile->mOutput);
     479           0 :   MOZ_ASSERT(IsReady());
     480             : 
     481             : #ifdef DEBUG
     482           0 :   for (uint32_t i = 0 ; i < mUpdateListeners.Length() ; i++) {
     483           0 :     MOZ_ASSERT(mUpdateListeners[i]->mCallback != aCallback);
     484             :   }
     485             : #endif
     486             : 
     487           0 :   ChunkListenerItem *item = new ChunkListenerItem();
     488           0 :   item->mTarget = CacheFileIOManager::IOTarget();
     489           0 :   if (!item->mTarget) {
     490           0 :     LOG(("CacheFileChunk::WaitForUpdate() - Cannot get Cache I/O thread! Using "
     491             :          "main thread for callback."));
     492           0 :     item->mTarget = GetMainThreadEventTarget();
     493             :   }
     494           0 :   item->mCallback = aCallback;
     495           0 :   MOZ_ASSERT(item->mTarget);
     496           0 :   item->mCallback = aCallback;
     497             : 
     498           0 :   mUpdateListeners.AppendElement(item);
     499           0 : }
     500             : 
     501             : nsresult
     502           0 : CacheFileChunk::CancelWait(CacheFileChunkListener *aCallback)
     503             : {
     504           0 :   AssertOwnsLock();
     505             : 
     506           0 :   LOG(("CacheFileChunk::CancelWait() [this=%p, listener=%p]", this, aCallback));
     507             : 
     508           0 :   MOZ_ASSERT(IsReady());
     509             : 
     510             :   uint32_t i;
     511           0 :   for (i = 0 ; i < mUpdateListeners.Length() ; i++) {
     512           0 :     ChunkListenerItem *item = mUpdateListeners[i];
     513             : 
     514           0 :     if (item->mCallback == aCallback) {
     515           0 :       mUpdateListeners.RemoveElementAt(i);
     516           0 :       delete item;
     517           0 :       break;
     518             :     }
     519             :   }
     520             : 
     521             : #ifdef DEBUG
     522           0 :   for ( ; i < mUpdateListeners.Length() ; i++) {
     523           0 :     MOZ_ASSERT(mUpdateListeners[i]->mCallback != aCallback);
     524             :   }
     525             : #endif
     526             : 
     527           0 :   return NS_OK;
     528             : }
     529             : 
     530             : nsresult
     531           4 : CacheFileChunk::NotifyUpdateListeners()
     532             : {
     533           4 :   AssertOwnsLock();
     534             : 
     535           4 :   LOG(("CacheFileChunk::NotifyUpdateListeners() [this=%p]", this));
     536             : 
     537           4 :   MOZ_ASSERT(IsReady());
     538             : 
     539             :   nsresult rv, rv2;
     540             : 
     541           4 :   rv = NS_OK;
     542           4 :   for (uint32_t i = 0 ; i < mUpdateListeners.Length() ; i++) {
     543           0 :     ChunkListenerItem *item = mUpdateListeners[i];
     544             : 
     545           0 :     LOG(("CacheFileChunk::NotifyUpdateListeners() - Notifying listener %p "
     546             :          "[this=%p]", item->mCallback.get(), this));
     547             : 
     548           0 :     RefPtr<NotifyUpdateListenerEvent> ev;
     549           0 :     ev = new NotifyUpdateListenerEvent(item->mCallback, this);
     550           0 :     rv2 = item->mTarget->Dispatch(ev, NS_DISPATCH_NORMAL);
     551           0 :     if (NS_FAILED(rv2) && NS_SUCCEEDED(rv))
     552           0 :       rv = rv2;
     553           0 :     delete item;
     554             :   }
     555             : 
     556           4 :   mUpdateListeners.Clear();
     557             : 
     558           4 :   return rv;
     559             : }
     560             : 
     561             : uint32_t
     562          96 : CacheFileChunk::Index() const
     563             : {
     564          96 :   return mIndex;
     565             : }
     566             : 
     567             : CacheHash::Hash16_t
     568           4 : CacheFileChunk::Hash() const
     569             : {
     570           4 :   MOZ_ASSERT(IsReady());
     571             : 
     572           4 :   return CacheHash::Hash16(mBuf->Buf(), mBuf->DataSize());
     573             : }
     574             : 
     575             : uint32_t
     576           4 : CacheFileChunk::DataSize() const
     577             : {
     578           4 :   return mBuf->DataSize();
     579             : }
     580             : 
     581             : void
     582           2 : CacheFileChunk::UpdateDataSize(uint32_t aOffset, uint32_t aLen)
     583             : {
     584           2 :   AssertOwnsLock();
     585             : 
     586             :   // UpdateDataSize() is called only when we've written some data to the chunk
     587             :   // and we never write data anymore once some error occurs.
     588           2 :   MOZ_ASSERT(NS_SUCCEEDED(mStatus));
     589             : 
     590           2 :   LOG(("CacheFileChunk::UpdateDataSize() [this=%p, offset=%d, len=%d]",
     591             :        this, aOffset, aLen));
     592             : 
     593           2 :   mIsDirty = true;
     594             : 
     595           2 :   int64_t fileSize = static_cast<int64_t>(kChunkSize) * mIndex + aOffset + aLen;
     596           2 :   bool notify = false;
     597             : 
     598           2 :   if (fileSize > mFile->mDataSize) {
     599           2 :     mFile->mDataSize = fileSize;
     600           2 :     notify = true;
     601             :   }
     602             : 
     603           2 :   if (mState == READY || mState == WRITING) {
     604           2 :     MOZ_ASSERT(mValidityMap.Length() == 0);
     605             : 
     606           2 :     if (notify) {
     607           2 :       NotifyUpdateListeners();
     608             :     }
     609             : 
     610           2 :     return;
     611             :   }
     612             : 
     613             :   // We're still waiting for data from the disk. This chunk cannot be used by
     614             :   // input stream, so there must be no update listener. We also need to keep
     615             :   // track of where the data is written so that we can correctly merge the new
     616             :   // data with the old one.
     617             : 
     618           0 :   MOZ_ASSERT(mUpdateListeners.Length() == 0);
     619           0 :   MOZ_ASSERT(mState == READING);
     620             : 
     621           0 :   mValidityMap.AddPair(aOffset, aLen);
     622           0 :   mValidityMap.Log();
     623             : }
     624             : 
     625             : nsresult
     626           0 : CacheFileChunk::Truncate(uint32_t aOffset)
     627             : {
     628           0 :   MOZ_RELEASE_ASSERT(mState == READY || mState == WRITING || mState == READING);
     629             : 
     630           0 :   if (mState == READING) {
     631           0 :     mIsDirty = true;
     632             :   }
     633             : 
     634           0 :   mBuf->SetDataSize(aOffset);
     635           0 :   return NS_OK;
     636             : }
     637             : 
     638             : nsresult
     639           0 : CacheFileChunk::OnFileOpened(CacheFileHandle *aHandle, nsresult aResult)
     640             : {
     641           0 :   MOZ_CRASH("CacheFileChunk::OnFileOpened should not be called!");
     642             :   return NS_ERROR_UNEXPECTED;
     643             : }
     644             : 
     645             : nsresult
     646           2 : CacheFileChunk::OnDataWritten(CacheFileHandle *aHandle, const char *aBuf,
     647             :                               nsresult aResult)
     648             : {
     649           2 :   LOG(("CacheFileChunk::OnDataWritten() [this=%p, handle=%p, result=0x%08" PRIx32 "]",
     650             :        this, aHandle, static_cast<uint32_t>(aResult)));
     651             : 
     652           4 :   nsCOMPtr<CacheFileChunkListener> listener;
     653             : 
     654             :   {
     655           4 :     CacheFileAutoLock lock(mFile);
     656             : 
     657           2 :     MOZ_ASSERT(mState == WRITING);
     658           2 :     MOZ_ASSERT(mListener);
     659             : 
     660           2 :     mWritingStateHandle = nullptr;
     661             : 
     662           2 :     if (NS_WARN_IF(NS_FAILED(aResult))) {
     663           0 :       SetError(aResult);
     664             :     }
     665             : 
     666           2 :     mState = READY;
     667           2 :     mListener.swap(listener);
     668             :   }
     669             : 
     670           2 :   listener->OnChunkWritten(aResult, this);
     671             : 
     672           4 :   return NS_OK;
     673             : }
     674             : 
     675             : nsresult
     676           2 : CacheFileChunk::OnDataRead(CacheFileHandle *aHandle, char *aBuf,
     677             :                            nsresult aResult)
     678             : {
     679           2 :   LOG(("CacheFileChunk::OnDataRead() [this=%p, handle=%p, result=0x%08" PRIx32 "]",
     680             :        this, aHandle, static_cast<uint32_t>(aResult)));
     681             : 
     682           4 :   nsCOMPtr<CacheFileChunkListener> listener;
     683             : 
     684             :   {
     685           4 :     CacheFileAutoLock lock(mFile);
     686             : 
     687           2 :     MOZ_ASSERT(mState == READING);
     688           2 :     MOZ_ASSERT(mListener);
     689           2 :     MOZ_ASSERT(mReadingStateBuf);
     690           2 :     MOZ_RELEASE_ASSERT(mBuf->ReadHandlesCount() == 0);
     691           2 :     MOZ_RELEASE_ASSERT(!mBuf->WriteHandleExists());
     692             : 
     693           4 :     RefPtr<CacheFileChunkBuffer> tmpBuf;
     694           2 :     tmpBuf.swap(mReadingStateBuf);
     695             : 
     696           2 :     if (NS_SUCCEEDED(aResult)) {
     697           2 :       CacheHash::Hash16_t hash = CacheHash::Hash16(tmpBuf->Buf(),
     698           2 :                                                    tmpBuf->DataSize());
     699           2 :       if (hash != mExpectedHash) {
     700           0 :         LOG(("CacheFileChunk::OnDataRead() - Hash mismatch! Hash of the data is"
     701             :              " %hx, hash in metadata is %hx. [this=%p, idx=%d]",
     702             :              hash, mExpectedHash, this, mIndex));
     703           0 :         aResult = NS_ERROR_FILE_CORRUPTED;
     704             :       } else {
     705           2 :         if (mBuf->DataSize() < tmpBuf->DataSize()) {
     706             :           // Truncate() was called while the data was being read.
     707           0 :           tmpBuf->SetDataSize(mBuf->DataSize());
     708             :         }
     709             : 
     710           2 :         if (!mBuf->Buf()) {
     711             :           // Just swap the buffers if mBuf is still empty
     712           2 :           mBuf.swap(tmpBuf);
     713             :         } else {
     714           0 :           LOG(("CacheFileChunk::OnDataRead() - Merging buffers. [this=%p]",
     715             :                this));
     716             : 
     717           0 :           mValidityMap.Log();
     718           0 :           aResult = mBuf->FillInvalidRanges(tmpBuf, &mValidityMap);
     719           0 :           mValidityMap.Clear();
     720             :         }
     721             :       }
     722             :     }
     723             : 
     724           2 :     if (NS_FAILED(aResult)) {
     725           0 :       aResult = mIndex ? NS_ERROR_FILE_CORRUPTED : NS_ERROR_FILE_NOT_FOUND;
     726           0 :       SetError(aResult);
     727           0 :       mBuf->SetDataSize(0);
     728             :     }
     729             : 
     730           2 :     mState = READY;
     731           2 :     mListener.swap(listener);
     732             :   }
     733             : 
     734           2 :   listener->OnChunkRead(aResult, this);
     735             : 
     736           4 :   return NS_OK;
     737             : }
     738             : 
     739             : nsresult
     740           0 : CacheFileChunk::OnFileDoomed(CacheFileHandle *aHandle, nsresult aResult)
     741             : {
     742           0 :   MOZ_CRASH("CacheFileChunk::OnFileDoomed should not be called!");
     743             :   return NS_ERROR_UNEXPECTED;
     744             : }
     745             : 
     746             : nsresult
     747           0 : CacheFileChunk::OnEOFSet(CacheFileHandle *aHandle, nsresult aResult)
     748             : {
     749           0 :   MOZ_CRASH("CacheFileChunk::OnEOFSet should not be called!");
     750             :   return NS_ERROR_UNEXPECTED;
     751             : }
     752             : 
     753             : nsresult
     754           0 : CacheFileChunk::OnFileRenamed(CacheFileHandle *aHandle, nsresult aResult)
     755             : {
     756           0 :   MOZ_CRASH("CacheFileChunk::OnFileRenamed should not be called!");
     757             :   return NS_ERROR_UNEXPECTED;
     758             : }
     759             : 
     760             : bool
     761           6 : CacheFileChunk::IsKilled()
     762             : {
     763           6 :   return mFile->IsKilled();
     764             : }
     765             : 
     766             : bool
     767          21 : CacheFileChunk::IsReady() const
     768             : {
     769          21 :   return (NS_SUCCEEDED(mStatus) && (mState == READY || mState == WRITING));
     770             : }
     771             : 
     772             : bool
     773           9 : CacheFileChunk::IsDirty() const
     774             : {
     775           9 :   AssertOwnsLock();
     776             : 
     777           9 :   return mIsDirty;
     778             : }
     779             : 
     780             : nsresult
     781           7 : CacheFileChunk::GetStatus()
     782             : {
     783           7 :   AssertOwnsLock();
     784             : 
     785           7 :   return mStatus;
     786             : }
     787             : 
     788             : void
     789           0 : CacheFileChunk::SetError(nsresult aStatus)
     790             : {
     791           0 :   LOG(("CacheFileChunk::SetError() [this=%p, status=0x%08" PRIx32 "]",
     792             :        this, static_cast<uint32_t>(aStatus)));
     793             : 
     794           0 :   MOZ_ASSERT(NS_FAILED(aStatus));
     795             : 
     796           0 :   if (NS_FAILED(mStatus)) {
     797             :     // Remember only the first error code.
     798           0 :     return;
     799             :   }
     800             : 
     801           0 :   mStatus = aStatus;
     802             : }
     803             : 
     804             : CacheFileChunkReadHandle
     805          11 : CacheFileChunk::GetReadHandle()
     806             : {
     807          11 :   LOG(("CacheFileChunk::GetReadHandle() [this=%p]", this));
     808             : 
     809          11 :   AssertOwnsLock();
     810             : 
     811          11 :   MOZ_RELEASE_ASSERT(mState == READY || mState == WRITING);
     812             :   // We don't release the lock when writing the data and CacheFileOutputStream
     813             :   // doesn't get the read handle, so there cannot be a write handle when read
     814             :   // handle is obtained.
     815          11 :   MOZ_RELEASE_ASSERT(!mBuf->WriteHandleExists());
     816             : 
     817          11 :   return CacheFileChunkReadHandle(mBuf);
     818             : }
     819             : 
     820             : CacheFileChunkWriteHandle
     821           2 : CacheFileChunk::GetWriteHandle(uint32_t aEnsuredBufSize)
     822             : {
     823           2 :   LOG(("CacheFileChunk::GetWriteHandle() [this=%p, ensuredBufSize=%u]",
     824             :        this, aEnsuredBufSize));
     825             : 
     826           2 :   AssertOwnsLock();
     827             : 
     828           2 :   if (NS_FAILED(mStatus)) {
     829           0 :     return CacheFileChunkWriteHandle(nullptr); // dummy handle
     830             :   }
     831             : 
     832             :   nsresult rv;
     833             : 
     834             :   // We don't support multiple write handles
     835           2 :   MOZ_RELEASE_ASSERT(!mBuf->WriteHandleExists());
     836             : 
     837           2 :   if (mBuf->ReadHandlesCount()) {
     838           0 :     LOG(("CacheFileChunk::GetWriteHandle() - cloning buffer because of existing"
     839             :          " read handle"));
     840             : 
     841           0 :     MOZ_RELEASE_ASSERT(mState != READING);
     842           0 :     RefPtr<CacheFileChunkBuffer> newBuf = new CacheFileChunkBuffer(this);
     843           0 :     rv = newBuf->EnsureBufSize(std::max(aEnsuredBufSize, mBuf->DataSize()));
     844           0 :     if (NS_SUCCEEDED(rv)) {
     845           0 :       newBuf->CopyFrom(mBuf);
     846           0 :       mOldBufs.AppendElement(mBuf);
     847           0 :       mBuf = newBuf;
     848             :     }
     849             :   } else {
     850           2 :     rv = mBuf->EnsureBufSize(aEnsuredBufSize);
     851             :   }
     852             : 
     853           2 :   if (NS_FAILED(rv)) {
     854           0 :     SetError(NS_ERROR_OUT_OF_MEMORY);
     855           0 :     return CacheFileChunkWriteHandle(nullptr); // dummy handle
     856             :   }
     857             : 
     858           2 :   return CacheFileChunkWriteHandle(mBuf);
     859             : }
     860             : 
     861             : // Memory reporting
     862             : 
     863             : size_t
     864           0 : CacheFileChunk::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
     865             : {
     866           0 :   size_t n = mBuf->SizeOfIncludingThis(mallocSizeOf);
     867             : 
     868           0 :   if (mReadingStateBuf) {
     869           0 :     n += mReadingStateBuf->SizeOfIncludingThis(mallocSizeOf);
     870             :   }
     871             : 
     872           0 :   for (uint32_t i = 0; i < mOldBufs.Length(); ++i) {
     873           0 :     n += mOldBufs[i]->SizeOfIncludingThis(mallocSizeOf);
     874             :   }
     875             : 
     876           0 :   n += mValidityMap.SizeOfExcludingThis(mallocSizeOf);
     877             : 
     878           0 :   return n;
     879             : }
     880             : 
     881             : size_t
     882           0 : CacheFileChunk::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
     883             : {
     884           0 :   return mallocSizeOf(this) + SizeOfExcludingThis(mallocSizeOf);
     885             : }
     886             : 
     887             : bool
     888           4 : CacheFileChunk::CanAllocate(uint32_t aSize) const
     889             : {
     890           4 :   if (!mLimitAllocation) {
     891           2 :     return true;
     892             :   }
     893             : 
     894           2 :   LOG(("CacheFileChunk::CanAllocate() [this=%p, size=%u]", this, aSize));
     895             : 
     896           2 :   uint32_t limit = CacheObserver::MaxDiskChunksMemoryUsage(mIsPriority);
     897           2 :   if (limit == 0) {
     898           0 :     return true;
     899             :   }
     900             : 
     901           2 :   uint32_t usage = ChunksMemoryUsage();
     902           2 :   if (usage + aSize > limit) {
     903           0 :     LOG(("CacheFileChunk::CanAllocate() - Returning false. [this=%p]", this));
     904           0 :     return false;
     905             :   }
     906             : 
     907           2 :   return true;
     908             : }
     909             : 
     910             : void
     911           8 : CacheFileChunk::BuffersAllocationChanged(uint32_t aFreed, uint32_t aAllocated)
     912             : {
     913           8 :   uint32_t oldBuffersSize = mBuffersSize;
     914           8 :   mBuffersSize += aAllocated;
     915           8 :   mBuffersSize -= aFreed;
     916             : 
     917           8 :   DoMemoryReport(sizeof(CacheFileChunk) + mBuffersSize);
     918             : 
     919           8 :   if (!mLimitAllocation) {
     920           4 :     return;
     921             :   }
     922             : 
     923           4 :   ChunksMemoryUsage() -= oldBuffersSize;
     924           4 :   ChunksMemoryUsage() += mBuffersSize;
     925           4 :   LOG(("CacheFileChunk::BuffersAllocationChanged() - %s chunks usage %u "
     926             :        "[this=%p]", mIsPriority ? "Priority" : "Normal",
     927             :        static_cast<uint32_t>(ChunksMemoryUsage()), this));
     928             : }
     929             : 
     930          10 : mozilla::Atomic<uint32_t, ReleaseAcquire>& CacheFileChunk::ChunksMemoryUsage() const
     931             : {
     932             :   static mozilla::Atomic<uint32_t, ReleaseAcquire> chunksMemoryUsage(0);
     933             :   static mozilla::Atomic<uint32_t, ReleaseAcquire> prioChunksMemoryUsage(0);
     934          10 :   return mIsPriority ? prioChunksMemoryUsage : chunksMemoryUsage;
     935             : }
     936             : 
     937             : } // namespace net
     938             : } // namespace mozilla

Generated by: LCOV version 1.13