Bug 1866777 - Disable test_race_cache_with_network.js on windows opt for frequent...
[gecko.git] / netwerk / cache2 / CacheFileChunk.cpp
blob7dc0d9078c48afa16ba38a61ab56b8d1d14ddf45
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #include "CacheLog.h"
6 #include "CacheFileChunk.h"
8 #include "CacheFile.h"
9 #include "nsThreadUtils.h"
11 #include "mozilla/IntegerPrintfMacros.h"
13 namespace mozilla::net {
15 #define kMinBufSize 512
17 CacheFileChunkBuffer::CacheFileChunkBuffer(CacheFileChunk* aChunk)
18 : mChunk(aChunk),
19 mBuf(nullptr),
20 mBufSize(0),
21 mDataSize(0),
22 mReadHandlesCount(0),
23 mWriteHandleExists(false) {}
25 CacheFileChunkBuffer::~CacheFileChunkBuffer() {
26 if (mBuf) {
27 CacheFileUtils::FreeBuffer(mBuf);
28 mBuf = nullptr;
29 mChunk->BuffersAllocationChanged(mBufSize, 0);
30 mBufSize = 0;
34 void CacheFileChunkBuffer::CopyFrom(CacheFileChunkBuffer* aOther) {
35 MOZ_RELEASE_ASSERT(mBufSize >= aOther->mDataSize);
36 mDataSize = aOther->mDataSize;
37 memcpy(mBuf, aOther->mBuf, mDataSize);
40 nsresult CacheFileChunkBuffer::FillInvalidRanges(
41 CacheFileChunkBuffer* aOther, CacheFileUtils::ValidityMap* aMap) {
42 nsresult rv;
44 rv = EnsureBufSize(aOther->mDataSize);
45 if (NS_FAILED(rv)) {
46 return rv;
49 uint32_t invalidOffset = 0;
50 uint32_t invalidLength;
52 for (uint32_t i = 0; i < aMap->Length(); ++i) {
53 uint32_t validOffset = (*aMap)[i].Offset();
54 uint32_t validLength = (*aMap)[i].Len();
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);
62 invalidOffset = validOffset + validLength;
65 if (invalidOffset < aOther->mDataSize) {
66 invalidLength = aOther->mDataSize - invalidOffset;
67 memcpy(mBuf + invalidOffset, aOther->mBuf + invalidOffset, invalidLength);
70 return NS_OK;
73 [[nodiscard]] nsresult CacheFileChunkBuffer::EnsureBufSize(uint32_t aBufSize) {
74 AssertOwnsLock();
76 if (mBufSize >= aBufSize) {
77 return NS_OK;
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++;
89 const uint32_t minBufSize = kMinBufSize;
90 const uint32_t maxBufSize = kChunkSize;
91 aBufSize = clamped(aBufSize, minBufSize, maxBufSize);
93 if (!mChunk->CanAllocate(aBufSize - mBufSize)) {
94 return NS_ERROR_OUT_OF_MEMORY;
97 char* newBuf = static_cast<char*>(realloc(mBuf, aBufSize));
98 if (!newBuf) {
99 return NS_ERROR_OUT_OF_MEMORY;
102 mChunk->BuffersAllocationChanged(mBufSize, aBufSize);
103 mBuf = newBuf;
104 mBufSize = aBufSize;
106 return NS_OK;
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));
122 mDataSize = aDataSize;
125 void CacheFileChunkBuffer::AssertOwnsLock() const { mChunk->AssertOwnsLock(); }
127 void CacheFileChunkBuffer::RemoveReadHandle() {
128 AssertOwnsLock();
129 MOZ_RELEASE_ASSERT(mReadHandlesCount);
130 MOZ_RELEASE_ASSERT(!mWriteHandleExists);
131 mReadHandlesCount--;
133 if (mReadHandlesCount == 0 && mChunk->mBuf != this) {
134 DebugOnly<bool> removed = mChunk->mOldBufs.RemoveElement(this);
135 MOZ_ASSERT(removed);
139 void CacheFileChunkBuffer::RemoveWriteHandle() {
140 AssertOwnsLock();
141 MOZ_RELEASE_ASSERT(mReadHandlesCount == 0);
142 MOZ_RELEASE_ASSERT(mWriteHandleExists);
143 mWriteHandleExists = false;
146 size_t CacheFileChunkBuffer::SizeOfIncludingThis(
147 mozilla::MallocSizeOf mallocSizeOf) const {
148 size_t n = mallocSizeOf(this);
150 if (mBuf) {
151 n += mallocSizeOf(mBuf);
154 return n;
157 uint32_t CacheFileChunkHandle::DataSize() {
158 MOZ_ASSERT(mBuf, "Unexpected call on dummy handle");
159 mBuf->AssertOwnsLock();
160 return mBuf->mDataSize;
163 uint32_t CacheFileChunkHandle::Offset() {
164 MOZ_ASSERT(mBuf, "Unexpected call on dummy handle");
165 mBuf->AssertOwnsLock();
166 return mBuf->mChunk->Index() * kChunkSize;
169 CacheFileChunkReadHandle::CacheFileChunkReadHandle(CacheFileChunkBuffer* aBuf) {
170 mBuf = aBuf;
171 mBuf->mReadHandlesCount++;
174 CacheFileChunkReadHandle::~CacheFileChunkReadHandle() {
175 mBuf->RemoveReadHandle();
178 const char* CacheFileChunkReadHandle::Buf() { return mBuf->mBuf; }
180 CacheFileChunkWriteHandle::CacheFileChunkWriteHandle(
181 CacheFileChunkBuffer* aBuf) {
182 mBuf = aBuf;
183 if (mBuf) {
184 MOZ_ASSERT(!mBuf->mWriteHandleExists);
185 mBuf->mWriteHandleExists = true;
189 CacheFileChunkWriteHandle::~CacheFileChunkWriteHandle() {
190 if (mBuf) {
191 mBuf->RemoveWriteHandle();
195 char* CacheFileChunkWriteHandle::Buf() { return mBuf ? mBuf->mBuf : nullptr; }
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);
203 if (aOffset + aLen > mBuf->mDataSize) {
204 mBuf->mDataSize = aOffset + aLen;
207 mBuf->mChunk->UpdateDataSize(aOffset, aLen);
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));
221 protected:
222 ~NotifyUpdateListenerEvent() {
223 LOG(("NotifyUpdateListenerEvent::~NotifyUpdateListenerEvent() [this=%p]",
224 this));
227 public:
228 NS_IMETHOD Run() override {
229 LOG(("NotifyUpdateListenerEvent::Run() [this=%p]", this));
231 mCallback->OnChunkUpdated(mChunk);
232 return NS_OK;
235 protected:
236 nsCOMPtr<CacheFileChunkListener> mCallback;
237 RefPtr<CacheFileChunk> mChunk;
240 bool CacheFileChunk::DispatchRelease() {
241 if (NS_IsMainThread()) {
242 return false;
245 NS_DispatchToMainThread(NewNonOwningRunnableMethod(
246 "net::CacheFileChunk::Release", this, &CacheFileChunk::Release));
248 return true;
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;
260 MOZ_ASSERT(0 != mRefCnt, "dup release");
261 count = --mRefCnt;
262 NS_LOG_RELEASE(this, count, "CacheFileChunk");
264 if (0 == count) {
265 mRefCnt = 1;
266 delete (this);
267 return 0;
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().
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);
284 return count;
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
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);
311 CacheFileChunk::~CacheFileChunk() {
312 LOG(("CacheFileChunk::~CacheFileChunk() [this=%p]", this));
315 void CacheFileChunk::AssertOwnsLock() const { mFile->AssertOwnsLock(); }
317 void CacheFileChunk::InitNew() {
318 AssertOwnsLock();
320 LOG(("CacheFileChunk::InitNew() [this=%p]", this));
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);
329 mBuf = new CacheFileChunkBuffer(this);
330 mState = READY;
333 nsresult CacheFileChunk::Read(CacheFileHandle* aHandle, uint32_t aLen,
334 CacheHash::Hash16_t aHash,
335 CacheFileChunkListener* aCallback) {
336 AssertOwnsLock();
338 LOG(("CacheFileChunk::Read() [this=%p, handle=%p, len=%d, listener=%p]", this,
339 aHandle, aLen, aCallback));
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);
348 nsresult rv;
350 mState = READING;
352 RefPtr<CacheFileChunkBuffer> tmpBuf = new CacheFileChunkBuffer(this);
353 rv = tmpBuf->EnsureBufSize(aLen);
354 if (NS_FAILED(rv)) {
355 SetError(rv);
356 return mStatus;
358 tmpBuf->SetDataSize(aLen);
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;
377 return rv;
380 nsresult CacheFileChunk::Write(CacheFileHandle* aHandle,
381 CacheFileChunkListener* aCallback) {
382 AssertOwnsLock();
384 LOG(("CacheFileChunk::Write() [this=%p, handle=%p, listener=%p]", this,
385 aHandle, aCallback));
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());
394 nsresult rv;
396 mState = WRITING;
397 mWritingStateHandle = MakeUnique<CacheFileChunkReadHandle>(mBuf);
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;
410 return rv;
413 void CacheFileChunk::WaitForUpdate(CacheFileChunkListener* aCallback) {
414 AssertOwnsLock();
415 mFile->AssertOwnsLock(); // For thread-safety analysis
417 LOG(("CacheFileChunk::WaitForUpdate() [this=%p, listener=%p]", this,
418 aCallback));
420 MOZ_ASSERT(mFile->mOutput);
421 MOZ_ASSERT(IsReady());
423 #ifdef DEBUG
424 for (uint32_t i = 0; i < mUpdateListeners.Length(); i++) {
425 MOZ_ASSERT(mUpdateListeners[i]->mCallback != aCallback);
427 #endif
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();
437 item->mCallback = aCallback;
438 MOZ_ASSERT(item->mTarget);
439 item->mCallback = aCallback;
441 mUpdateListeners.AppendElement(item);
444 void CacheFileChunk::CancelWait(CacheFileChunkListener* aCallback) {
445 AssertOwnsLock();
447 LOG(("CacheFileChunk::CancelWait() [this=%p, listener=%p]", this, aCallback));
449 MOZ_ASSERT(IsReady());
451 uint32_t i;
452 for (i = 0; i < mUpdateListeners.Length(); i++) {
453 ChunkListenerItem* item = mUpdateListeners[i];
455 if (item->mCallback == aCallback) {
456 mUpdateListeners.RemoveElementAt(i);
457 delete item;
458 break;
462 #ifdef DEBUG
463 for (; i < mUpdateListeners.Length(); i++) {
464 MOZ_ASSERT(mUpdateListeners[i]->mCallback != aCallback);
466 #endif
469 nsresult CacheFileChunk::NotifyUpdateListeners() {
470 AssertOwnsLock();
472 LOG(("CacheFileChunk::NotifyUpdateListeners() [this=%p]", this));
474 MOZ_ASSERT(IsReady());
476 nsresult rv, rv2;
478 rv = NS_OK;
479 for (uint32_t i = 0; i < mUpdateListeners.Length(); i++) {
480 ChunkListenerItem* item = mUpdateListeners[i];
482 LOG(
483 ("CacheFileChunk::NotifyUpdateListeners() - Notifying listener %p "
484 "[this=%p]",
485 item->mCallback.get(), this));
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;
494 mUpdateListeners.Clear();
496 return rv;
499 uint32_t CacheFileChunk::Index() const { return mIndex; }
501 CacheHash::Hash16_t CacheFileChunk::Hash() const {
502 MOZ_ASSERT(IsReady());
504 return CacheHash::Hash16(mBuf->Buf(), mBuf->DataSize());
507 uint32_t CacheFileChunk::DataSize() const { return mBuf->DataSize(); }
509 void CacheFileChunk::UpdateDataSize(uint32_t aOffset, uint32_t aLen) {
510 AssertOwnsLock();
511 mFile->AssertOwnsLock(); // For thread-safety analysis
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));
517 LOG(("CacheFileChunk::UpdateDataSize() [this=%p, offset=%d, len=%d]", this,
518 aOffset, aLen));
520 mIsDirty = true;
522 int64_t fileSize = static_cast<int64_t>(kChunkSize) * mIndex + aOffset + aLen;
523 bool notify = false;
525 if (fileSize > mFile->mDataSize) {
526 mFile->mDataSize = fileSize;
527 notify = true;
530 if (mState == READY || mState == WRITING) {
531 MOZ_ASSERT(mValidityMap.Length() == 0);
533 if (notify) {
534 NotifyUpdateListeners();
537 return;
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.
545 MOZ_ASSERT(mUpdateListeners.Length() == 0);
546 MOZ_ASSERT(mState == READING);
548 mValidityMap.AddPair(aOffset, aLen);
549 mValidityMap.Log();
552 void CacheFileChunk::Truncate(uint32_t aOffset) {
553 MOZ_RELEASE_ASSERT(mState == READY || mState == WRITING || mState == READING);
555 if (mState == READING) {
556 mIsDirty = true;
559 mBuf->SetDataSize(aOffset);
562 nsresult CacheFileChunk::OnFileOpened(CacheFileHandle* aHandle,
563 nsresult aResult) {
564 MOZ_CRASH("CacheFileChunk::OnFileOpened should not be called!");
565 return NS_ERROR_UNEXPECTED;
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)));
575 nsCOMPtr<CacheFileChunkListener> listener;
578 CacheFileAutoLock lock(mFile);
580 MOZ_ASSERT(mState == WRITING);
581 MOZ_ASSERT(mListener);
583 mWritingStateHandle = nullptr;
585 if (NS_WARN_IF(NS_FAILED(aResult))) {
586 SetError(aResult);
589 mState = READY;
590 mListener.swap(listener);
593 listener->OnChunkWritten(aResult, this);
595 return NS_OK;
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)));
604 nsCOMPtr<CacheFileChunkListener> listener;
607 CacheFileAutoLock lock(mFile);
609 MOZ_ASSERT(mState == READING);
610 MOZ_ASSERT(mListener);
611 MOZ_ASSERT(mReadingStateBuf);
612 MOZ_RELEASE_ASSERT(mBuf->ReadHandlesCount() == 0);
613 MOZ_RELEASE_ASSERT(!mBuf->WriteHandleExists());
615 RefPtr<CacheFileChunkBuffer> tmpBuf;
616 tmpBuf.swap(mReadingStateBuf);
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());
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));
640 mValidityMap.Log();
641 aResult = mBuf->FillInvalidRanges(tmpBuf, &mValidityMap);
642 mValidityMap.Clear();
647 if (NS_FAILED(aResult)) {
648 aResult = mIndex ? NS_ERROR_FILE_CORRUPTED : NS_ERROR_FILE_NOT_FOUND;
649 SetError(aResult);
650 mBuf->SetDataSize(0);
653 mState = READY;
654 mListener.swap(listener);
657 listener->OnChunkRead(aResult, this);
659 return NS_OK;
662 nsresult CacheFileChunk::OnFileDoomed(CacheFileHandle* aHandle,
663 nsresult aResult) {
664 MOZ_CRASH("CacheFileChunk::OnFileDoomed should not be called!");
665 return NS_ERROR_UNEXPECTED;
668 nsresult CacheFileChunk::OnEOFSet(CacheFileHandle* aHandle, nsresult aResult) {
669 MOZ_CRASH("CacheFileChunk::OnEOFSet should not be called!");
670 return NS_ERROR_UNEXPECTED;
673 nsresult CacheFileChunk::OnFileRenamed(CacheFileHandle* aHandle,
674 nsresult aResult) {
675 MOZ_CRASH("CacheFileChunk::OnFileRenamed should not be called!");
676 return NS_ERROR_UNEXPECTED;
679 bool CacheFileChunk::IsKilled() { return mFile->IsKilled(); }
681 bool CacheFileChunk::IsReady() const {
682 return (NS_SUCCEEDED(mStatus) && (mState == READY || mState == WRITING));
685 bool CacheFileChunk::IsDirty() const {
686 AssertOwnsLock();
688 return mIsDirty;
691 nsresult CacheFileChunk::GetStatus() { return mStatus; }
693 void CacheFileChunk::SetError(nsresult aStatus) {
694 LOG(("CacheFileChunk::SetError() [this=%p, status=0x%08" PRIx32 "]", this,
695 static_cast<uint32_t>(aStatus)));
697 MOZ_ASSERT(NS_FAILED(aStatus));
699 if (NS_FAILED(mStatus)) {
700 // Remember only the first error code.
701 return;
704 mStatus = aStatus;
707 CacheFileChunkReadHandle CacheFileChunk::GetReadHandle() {
708 LOG(("CacheFileChunk::GetReadHandle() [this=%p]", this));
710 AssertOwnsLock();
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());
718 return CacheFileChunkReadHandle(mBuf);
721 CacheFileChunkWriteHandle CacheFileChunk::GetWriteHandle(
722 uint32_t aEnsuredBufSize) {
723 LOG(("CacheFileChunk::GetWriteHandle() [this=%p, ensuredBufSize=%u]", this,
724 aEnsuredBufSize));
726 AssertOwnsLock();
728 if (NS_FAILED(mStatus)) {
729 return CacheFileChunkWriteHandle(nullptr); // dummy handle
732 nsresult rv;
734 // We don't support multiple write handles
735 MOZ_RELEASE_ASSERT(!mBuf->WriteHandleExists());
737 if (mBuf->ReadHandlesCount()) {
738 LOG(
739 ("CacheFileChunk::GetWriteHandle() - cloning buffer because of existing"
740 " read handle"));
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;
750 } else {
751 rv = mBuf->EnsureBufSize(aEnsuredBufSize);
754 if (NS_FAILED(rv)) {
755 SetError(NS_ERROR_OUT_OF_MEMORY);
756 return CacheFileChunkWriteHandle(nullptr); // dummy handle
759 return CacheFileChunkWriteHandle(mBuf);
762 // Memory reporting
764 size_t CacheFileChunk::SizeOfExcludingThis(
765 mozilla::MallocSizeOf mallocSizeOf) const {
766 size_t n = mBuf->SizeOfIncludingThis(mallocSizeOf);
768 if (mReadingStateBuf) {
769 n += mReadingStateBuf->SizeOfIncludingThis(mallocSizeOf);
772 for (uint32_t i = 0; i < mOldBufs.Length(); ++i) {
773 n += mOldBufs[i]->SizeOfIncludingThis(mallocSizeOf);
776 n += mValidityMap.SizeOfExcludingThis(mallocSizeOf);
778 return n;
781 size_t CacheFileChunk::SizeOfIncludingThis(
782 mozilla::MallocSizeOf mallocSizeOf) const {
783 return mallocSizeOf(this) + SizeOfExcludingThis(mallocSizeOf);
786 bool CacheFileChunk::CanAllocate(uint32_t aSize) const {
787 if (!mLimitAllocation) {
788 return true;
791 LOG(("CacheFileChunk::CanAllocate() [this=%p, size=%u]", this, aSize));
793 int64_t limit = CacheObserver::MaxDiskChunksMemoryUsage(mIsPriority);
794 if (limit == 0) {
795 return true;
798 limit <<= 10;
799 if (limit > UINT32_MAX) {
800 limit = UINT32_MAX;
803 int64_t usage = ChunksMemoryUsage();
804 if (usage + aSize > limit) {
805 LOG(("CacheFileChunk::CanAllocate() - Returning false. [this=%p]", this));
806 return false;
809 return true;
812 void CacheFileChunk::BuffersAllocationChanged(uint32_t aFreed,
813 uint32_t aAllocated) {
814 uint32_t oldBuffersSize = mBuffersSize;
815 mBuffersSize += aAllocated;
816 mBuffersSize -= aFreed;
818 DoMemoryReport(sizeof(CacheFileChunk) + mBuffersSize);
820 if (!mLimitAllocation) {
821 return;
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));
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;
840 } // namespace mozilla::net