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/. */
6 #include "CacheFileChunk.h"
9 #include "nsThreadUtils.h"
11 #include "mozilla/IntegerPrintfMacros.h"
13 namespace mozilla::net
{
15 #define kMinBufSize 512
17 CacheFileChunkBuffer::CacheFileChunkBuffer(CacheFileChunk
* aChunk
)
23 mWriteHandleExists(false) {}
25 CacheFileChunkBuffer::~CacheFileChunkBuffer() {
27 CacheFileUtils::FreeBuffer(mBuf
);
29 mChunk
->BuffersAllocationChanged(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
) {
44 rv
= EnsureBufSize(aOther
->mDataSize
);
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
);
73 [[nodiscard
]] nsresult
CacheFileChunkBuffer::EnsureBufSize(uint32_t aBufSize
) {
76 if (mBufSize
>= aBufSize
) {
80 // find smallest power of 2 greater than or equal to aBufSize
82 aBufSize
|= aBufSize
>> 1;
83 aBufSize
|= aBufSize
>> 2;
84 aBufSize
|= aBufSize
>> 4;
85 aBufSize
|= aBufSize
>> 8;
86 aBufSize
|= aBufSize
>> 16;
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
));
99 return NS_ERROR_OUT_OF_MEMORY
;
102 mChunk
->BuffersAllocationChanged(mBufSize
, aBufSize
);
109 void CacheFileChunkBuffer::SetDataSize(uint32_t aDataSize
) {
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
120 (mBufSize
== 0 && mChunk
->mState
== CacheFileChunk::READING
));
122 mDataSize
= aDataSize
;
125 void CacheFileChunkBuffer::AssertOwnsLock() const { mChunk
->AssertOwnsLock(); }
127 void CacheFileChunkBuffer::RemoveReadHandle() {
129 MOZ_RELEASE_ASSERT(mReadHandlesCount
);
130 MOZ_RELEASE_ASSERT(!mWriteHandleExists
);
133 if (mReadHandlesCount
== 0 && mChunk
->mBuf
!= this) {
134 DebugOnly
<bool> removed
= mChunk
->mOldBufs
.RemoveElement(this);
139 void CacheFileChunkBuffer::RemoveWriteHandle() {
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);
151 n
+= mallocSizeOf(mBuf
);
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
) {
171 mBuf
->mReadHandlesCount
++;
174 CacheFileChunkReadHandle::~CacheFileChunkReadHandle() {
175 mBuf
->RemoveReadHandle();
178 const char* CacheFileChunkReadHandle::Buf() { return mBuf
->mBuf
; }
180 CacheFileChunkWriteHandle::CacheFileChunkWriteHandle(
181 CacheFileChunkBuffer
* aBuf
) {
184 MOZ_ASSERT(!mBuf
->mWriteHandleExists
);
185 mBuf
->mWriteHandleExists
= true;
189 CacheFileChunkWriteHandle::~CacheFileChunkWriteHandle() {
191 mBuf
->RemoveWriteHandle();
195 char* CacheFileChunkWriteHandle::Buf() { return mBuf
? mBuf
->mBuf
: nullptr; }
197 void CacheFileChunkWriteHandle::UpdateDataSize(uint32_t aOffset
,
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
{
212 NotifyUpdateListenerEvent(CacheFileChunkListener
* aCallback
,
213 CacheFileChunk
* aChunk
)
214 : Runnable("net::NotifyUpdateListenerEvent"),
215 mCallback(aCallback
),
217 LOG(("NotifyUpdateListenerEvent::NotifyUpdateListenerEvent() [this=%p]",
222 ~NotifyUpdateListenerEvent() {
223 LOG(("NotifyUpdateListenerEvent::~NotifyUpdateListenerEvent() [this=%p]",
228 NS_IMETHOD
Run() override
{
229 LOG(("NotifyUpdateListenerEvent::Run() [this=%p]", this));
231 mCallback
->OnChunkUpdated(mChunk
);
236 nsCOMPtr
<CacheFileChunkListener
> mCallback
;
237 RefPtr
<CacheFileChunk
> mChunk
;
240 bool CacheFileChunk::DispatchRelease() {
241 if (NS_IsMainThread()) {
245 NS_DispatchToMainThread(NewNonOwningRunnableMethod(
246 "net::CacheFileChunk::Release", this, &CacheFileChunk::Release
));
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.
260 MOZ_ASSERT(0 != mRefCnt
, "dup release");
262 NS_LOG_RELEASE(this, count
, "CacheFileChunk");
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);
287 NS_INTERFACE_MAP_BEGIN(CacheFileChunk
)
288 NS_INTERFACE_MAP_ENTRY(mozilla::net::CacheFileIOListener
)
289 NS_INTERFACE_MAP_ENTRY(nsISupports
)
292 CacheFileChunk::CacheFileChunk(CacheFile
* aFile
, uint32_t aIndex
,
294 : CacheMemoryConsumer(aFile
->mOpenAsMemoryOnly
? MEMORY_ONLY
: DONT_REPORT
),
300 mDiscardedChunk(false),
302 mLimitAllocation(!aFile
->mOpenAsMemoryOnly
&& aInitByWriter
),
303 mIsPriority(aFile
->mPriority
),
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() {
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);
333 nsresult
CacheFileChunk::Read(CacheFileHandle
* aHandle
, uint32_t aLen
,
334 CacheHash::Hash16_t aHash
,
335 CacheFileChunkListener
* aCallback
) {
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
);
352 RefPtr
<CacheFileChunkBuffer
> tmpBuf
= new CacheFileChunkBuffer(this);
353 rv
= tmpBuf
->EnsureBufSize(aLen
);
358 tmpBuf
->SetDataSize(aLen
);
360 rv
= CacheFileIOManager::Read(aHandle
, mIndex
* kChunkSize
, tmpBuf
->Buf(),
362 if (NS_WARN_IF(NS_FAILED(rv
))) {
363 rv
= mIndex
? NS_ERROR_FILE_CORRUPTED
: NS_ERROR_FILE_NOT_FOUND
;
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
;
380 nsresult
CacheFileChunk::Write(CacheFileHandle
* aHandle
,
381 CacheFileChunkListener
* aCallback
) {
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());
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;
406 mListener
= aCallback
;
413 void CacheFileChunk::WaitForUpdate(CacheFileChunkListener
* aCallback
) {
415 mFile
->AssertOwnsLock(); // For thread-safety analysis
417 LOG(("CacheFileChunk::WaitForUpdate() [this=%p, listener=%p]", this,
420 MOZ_ASSERT(mFile
->mOutput
);
421 MOZ_ASSERT(IsReady());
424 for (uint32_t i
= 0; i
< mUpdateListeners
.Length(); i
++) {
425 MOZ_ASSERT(mUpdateListeners
[i
]->mCallback
!= aCallback
);
429 ChunkListenerItem
* item
= new ChunkListenerItem();
430 item
->mTarget
= CacheFileIOManager::IOTarget();
431 if (!item
->mTarget
) {
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
) {
447 LOG(("CacheFileChunk::CancelWait() [this=%p, listener=%p]", this, aCallback
));
449 MOZ_ASSERT(IsReady());
452 for (i
= 0; i
< mUpdateListeners
.Length(); i
++) {
453 ChunkListenerItem
* item
= mUpdateListeners
[i
];
455 if (item
->mCallback
== aCallback
) {
456 mUpdateListeners
.RemoveElementAt(i
);
463 for (; i
< mUpdateListeners
.Length(); i
++) {
464 MOZ_ASSERT(mUpdateListeners
[i
]->mCallback
!= aCallback
);
469 nsresult
CacheFileChunk::NotifyUpdateListeners() {
472 LOG(("CacheFileChunk::NotifyUpdateListeners() [this=%p]", this));
474 MOZ_ASSERT(IsReady());
479 for (uint32_t i
= 0; i
< mUpdateListeners
.Length(); i
++) {
480 ChunkListenerItem
* item
= mUpdateListeners
[i
];
483 ("CacheFileChunk::NotifyUpdateListeners() - Notifying listener %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
;
494 mUpdateListeners
.Clear();
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
) {
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,
522 int64_t fileSize
= static_cast<int64_t>(kChunkSize
) * mIndex
+ aOffset
+ aLen
;
525 if (fileSize
> mFile
->mDataSize
) {
526 mFile
->mDataSize
= fileSize
;
530 if (mState
== READY
|| mState
== WRITING
) {
531 MOZ_ASSERT(mValidityMap
.Length() == 0);
534 NotifyUpdateListeners();
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
);
552 void CacheFileChunk::Truncate(uint32_t aOffset
) {
553 MOZ_RELEASE_ASSERT(mState
== READY
|| mState
== WRITING
|| mState
== READING
);
555 if (mState
== READING
) {
559 mBuf
->SetDataSize(aOffset
);
562 nsresult
CacheFileChunk::OnFileOpened(CacheFileHandle
* aHandle
,
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
) {
571 "CacheFileChunk::OnDataWritten() [this=%p, handle=%p, result=0x%08" PRIx32
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
))) {
590 mListener
.swap(listener
);
593 listener
->OnChunkWritten(aResult
, this);
598 nsresult
CacheFileChunk::OnDataRead(CacheFileHandle
* aHandle
, char* aBuf
,
600 LOG(("CacheFileChunk::OnDataRead() [this=%p, handle=%p, result=0x%08" PRIx32
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
) {
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
;
628 if (mBuf
->DataSize() < tmpBuf
->DataSize()) {
629 // Truncate() was called while the data was being read.
630 tmpBuf
->SetDataSize(mBuf
->DataSize());
634 // Just swap the buffers if mBuf is still empty
637 LOG(("CacheFileChunk::OnDataRead() - Merging buffers. [this=%p]",
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
;
650 mBuf
->SetDataSize(0);
654 mListener
.swap(listener
);
657 listener
->OnChunkRead(aResult
, this);
662 nsresult
CacheFileChunk::OnFileDoomed(CacheFileHandle
* aHandle
,
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
,
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 {
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.
707 CacheFileChunkReadHandle
CacheFileChunk::GetReadHandle() {
708 LOG(("CacheFileChunk::GetReadHandle() [this=%p]", this));
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,
728 if (NS_FAILED(mStatus
)) {
729 return CacheFileChunkWriteHandle(nullptr); // dummy handle
734 // We don't support multiple write handles
735 MOZ_RELEASE_ASSERT(!mBuf
->WriteHandleExists());
737 if (mBuf
->ReadHandlesCount()) {
739 ("CacheFileChunk::GetWriteHandle() - cloning buffer because of existing"
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
);
751 rv
= mBuf
->EnsureBufSize(aEnsuredBufSize
);
755 SetError(NS_ERROR_OUT_OF_MEMORY
);
756 return CacheFileChunkWriteHandle(nullptr); // dummy handle
759 return CacheFileChunkWriteHandle(mBuf
);
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
);
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
) {
791 LOG(("CacheFileChunk::CanAllocate() [this=%p, size=%u]", this, aSize
));
793 int64_t limit
= CacheObserver::MaxDiskChunksMemoryUsage(mIsPriority
);
799 if (limit
> UINT32_MAX
) {
803 int64_t usage
= ChunksMemoryUsage();
804 if (usage
+ aSize
> limit
) {
805 LOG(("CacheFileChunk::CanAllocate() - Returning false. [this=%p]", this));
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
) {
824 ChunksMemoryUsage() -= oldBuffersSize
;
825 ChunksMemoryUsage() += mBuffersSize
;
827 ("CacheFileChunk::BuffersAllocationChanged() - %s chunks usage %u "
829 mIsPriority
? "Priority" : "Normal",
830 static_cast<uint32_t>(ChunksMemoryUsage()), this));
833 mozilla::Atomic
<uint32_t, ReleaseAcquire
>& CacheFileChunk::ChunksMemoryUsage()
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