tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

CacheFileChunk.cpp (23578B)


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