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