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"
16 #define kMinBufSize 512
18 CacheFileChunkBuffer::CacheFileChunkBuffer(CacheFileChunk
*aChunk
)
23 , mReadHandlesCount(0)
24 , mWriteHandleExists(false)
28 CacheFileChunkBuffer::~CacheFileChunkBuffer()
31 CacheFileUtils::FreeBuffer(mBuf
);
33 mChunk
->BuffersAllocationChanged(mBufSize
, 0);
39 CacheFileChunkBuffer::CopyFrom(CacheFileChunkBuffer
*aOther
)
41 MOZ_RELEASE_ASSERT(mBufSize
>= aOther
->mDataSize
);
42 mDataSize
= aOther
->mDataSize
;
43 memcpy(mBuf
, aOther
->mBuf
, mDataSize
);
47 CacheFileChunkBuffer::FillInvalidRanges(CacheFileChunkBuffer
*aOther
,
48 CacheFileUtils::ValidityMap
*aMap
)
52 rv
= EnsureBufSize(aOther
->mDataSize
);
57 uint32_t invalidOffset
= 0;
58 uint32_t invalidLength
;
60 for (uint32_t i
= 0; i
< aMap
->Length(); ++i
) {
61 uint32_t validOffset
= (*aMap
)[i
].Offset();
62 uint32_t validLength
= (*aMap
)[i
].Len();
64 MOZ_RELEASE_ASSERT(invalidOffset
<= validOffset
);
65 invalidLength
= validOffset
- invalidOffset
;
66 if (invalidLength
> 0) {
67 MOZ_RELEASE_ASSERT(invalidOffset
+ invalidLength
<= aOther
->mDataSize
);
68 memcpy(mBuf
+ invalidOffset
, aOther
->mBuf
+ invalidOffset
, invalidLength
);
70 invalidOffset
= validOffset
+ validLength
;
73 if (invalidOffset
< aOther
->mDataSize
) {
74 invalidLength
= aOther
->mDataSize
- invalidOffset
;
75 memcpy(mBuf
+ invalidOffset
, aOther
->mBuf
+ invalidOffset
, invalidLength
);
82 CacheFileChunkBuffer::EnsureBufSize(uint32_t aBufSize
)
86 if (mBufSize
>= aBufSize
) {
90 // find smallest power of 2 greater than or equal to aBufSize
92 aBufSize
|= aBufSize
>> 1;
93 aBufSize
|= aBufSize
>> 2;
94 aBufSize
|= aBufSize
>> 4;
95 aBufSize
|= aBufSize
>> 8;
96 aBufSize
|= aBufSize
>> 16;
99 const uint32_t minBufSize
= kMinBufSize
;
100 const uint32_t maxBufSize
= kChunkSize
;
101 aBufSize
= clamped(aBufSize
, minBufSize
, maxBufSize
);
103 if (!mChunk
->CanAllocate(aBufSize
- mBufSize
)) {
104 return NS_ERROR_OUT_OF_MEMORY
;
107 char *newBuf
= static_cast<char *>(realloc(mBuf
, aBufSize
));
109 return NS_ERROR_OUT_OF_MEMORY
;
112 mChunk
->BuffersAllocationChanged(mBufSize
, aBufSize
);
120 CacheFileChunkBuffer::SetDataSize(uint32_t aDataSize
)
123 // EnsureBufSize must be called before SetDataSize, so the new data size
124 // is guaranteed to be smaller than or equal to mBufSize.
125 aDataSize
<= mBufSize
||
126 // The only exception is an optimization when we read the data from the
127 // disk. The data is read to a separate buffer and CacheFileChunk::mBuf is
128 // empty (see CacheFileChunk::Read). We need to set mBuf::mDataSize
129 // accordingly so that DataSize() methods return correct value, but we don't
130 // want to allocate the buffer since it wouldn't be used in most cases.
131 (mBufSize
== 0 && mChunk
->mState
== CacheFileChunk::READING
));
133 mDataSize
= aDataSize
;
137 CacheFileChunkBuffer::AssertOwnsLock() const
139 mChunk
->AssertOwnsLock();
143 CacheFileChunkBuffer::RemoveReadHandle()
146 MOZ_RELEASE_ASSERT(mReadHandlesCount
);
147 MOZ_RELEASE_ASSERT(!mWriteHandleExists
);
150 if (mReadHandlesCount
== 0 && mChunk
->mBuf
!= this) {
151 DebugOnly
<bool> removed
= mChunk
->mOldBufs
.RemoveElement(this);
157 CacheFileChunkBuffer::RemoveWriteHandle()
160 MOZ_RELEASE_ASSERT(mReadHandlesCount
== 0);
161 MOZ_RELEASE_ASSERT(mWriteHandleExists
);
162 mWriteHandleExists
= false;
166 CacheFileChunkBuffer::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf
) const
168 size_t n
= mallocSizeOf(this);
171 n
+= mallocSizeOf(mBuf
);
178 CacheFileChunkHandle::DataSize()
180 MOZ_ASSERT(mBuf
, "Unexpected call on dummy handle");
181 mBuf
->AssertOwnsLock();
182 return mBuf
->mDataSize
;
186 CacheFileChunkHandle::Offset()
188 MOZ_ASSERT(mBuf
, "Unexpected call on dummy handle");
189 mBuf
->AssertOwnsLock();
190 return mBuf
->mChunk
->Index() * kChunkSize
;
193 CacheFileChunkReadHandle::CacheFileChunkReadHandle(CacheFileChunkBuffer
*aBuf
)
196 mBuf
->mReadHandlesCount
++;
199 CacheFileChunkReadHandle::~CacheFileChunkReadHandle()
201 mBuf
->RemoveReadHandle();
205 CacheFileChunkReadHandle::Buf()
210 CacheFileChunkWriteHandle::CacheFileChunkWriteHandle(CacheFileChunkBuffer
*aBuf
)
214 MOZ_ASSERT(!mBuf
->mWriteHandleExists
);
215 mBuf
->mWriteHandleExists
= true;
219 CacheFileChunkWriteHandle::~CacheFileChunkWriteHandle()
222 mBuf
->RemoveWriteHandle();
227 CacheFileChunkWriteHandle::Buf()
229 return mBuf
? mBuf
->mBuf
: nullptr;
233 CacheFileChunkWriteHandle::UpdateDataSize(uint32_t aOffset
, uint32_t aLen
)
235 MOZ_ASSERT(mBuf
, "Write performed on dummy handle?");
236 MOZ_ASSERT(aOffset
<= mBuf
->mDataSize
);
237 MOZ_ASSERT(aOffset
+ aLen
<= mBuf
->mBufSize
);
239 if (aOffset
+ aLen
> mBuf
->mDataSize
) {
240 mBuf
->mDataSize
= aOffset
+ aLen
;
243 mBuf
->mChunk
->UpdateDataSize(aOffset
, aLen
);
247 class NotifyUpdateListenerEvent
: public Runnable
{
249 NotifyUpdateListenerEvent(CacheFileChunkListener
* aCallback
,
250 CacheFileChunk
* aChunk
)
251 : Runnable("net::NotifyUpdateListenerEvent")
252 , mCallback(aCallback
)
255 LOG(("NotifyUpdateListenerEvent::NotifyUpdateListenerEvent() [this=%p]",
260 ~NotifyUpdateListenerEvent()
262 LOG(("NotifyUpdateListenerEvent::~NotifyUpdateListenerEvent() [this=%p]",
267 NS_IMETHOD
Run() override
269 LOG(("NotifyUpdateListenerEvent::Run() [this=%p]", this));
271 mCallback
->OnChunkUpdated(mChunk
);
276 nsCOMPtr
<CacheFileChunkListener
> mCallback
;
277 RefPtr
<CacheFileChunk
> mChunk
;
281 CacheFileChunk::DispatchRelease()
283 if (NS_IsMainThread()) {
287 NS_DispatchToMainThread(NewNonOwningRunnableMethod(
288 "net::CacheFileChunk::Release", this, &CacheFileChunk::Release
));
293 NS_IMPL_ADDREF(CacheFileChunk
)
294 NS_IMETHODIMP_(MozExternalRefCountType
)
295 CacheFileChunk::Release()
297 nsrefcnt count
= mRefCnt
- 1;
298 if (DispatchRelease()) {
299 // Redispatched to the main thread.
303 MOZ_ASSERT(0 != mRefCnt
, "dup release");
305 NS_LOG_RELEASE(this, count
, "CacheFileChunk");
313 // We can safely access this chunk after decreasing mRefCnt since we re-post
314 // all calls to Release() happening off the main thread to the main thread.
315 // I.e. no other Release() that would delete the object could be run before
316 // we call CacheFile::DeactivateChunk().
318 // NOTE: we don't grab the CacheFile's lock, so the chunk might be addrefed
319 // on another thread before CacheFile::DeactivateChunk() grabs the lock on
320 // this thread. To make sure we won't deactivate chunk that was just returned
321 // to a new consumer we check mRefCnt once again in
322 // CacheFile::DeactivateChunk() after we grab the lock.
323 if (mActiveChunk
&& count
== 1) {
324 mFile
->DeactivateChunk(this);
330 NS_INTERFACE_MAP_BEGIN(CacheFileChunk
)
331 NS_INTERFACE_MAP_ENTRY(mozilla::net::CacheFileIOListener
)
332 NS_INTERFACE_MAP_ENTRY(nsISupports
)
335 CacheFileChunk::CacheFileChunk(CacheFile
*aFile
, uint32_t aIndex
,
337 : CacheMemoryConsumer(aFile
->mOpenAsMemoryOnly
? MEMORY_ONLY
: DONT_REPORT
)
341 , mActiveChunk(false)
343 , mDiscardedChunk(false)
345 , mLimitAllocation(!aFile
->mOpenAsMemoryOnly
&& aInitByWriter
)
346 , mIsPriority(aFile
->mPriority
)
350 LOG(("CacheFileChunk::CacheFileChunk() [this=%p, index=%u, initByWriter=%d]",
351 this, aIndex
, aInitByWriter
));
352 mBuf
= new CacheFileChunkBuffer(this);
355 CacheFileChunk::~CacheFileChunk()
357 LOG(("CacheFileChunk::~CacheFileChunk() [this=%p]", this));
361 CacheFileChunk::AssertOwnsLock() const
363 mFile
->AssertOwnsLock();
367 CacheFileChunk::InitNew()
371 LOG(("CacheFileChunk::InitNew() [this=%p]", this));
373 MOZ_ASSERT(mState
== INITIAL
);
374 MOZ_ASSERT(NS_SUCCEEDED(mStatus
));
375 MOZ_ASSERT(!mBuf
->Buf());
376 MOZ_ASSERT(!mWritingStateHandle
);
377 MOZ_ASSERT(!mReadingStateBuf
);
378 MOZ_ASSERT(!mIsDirty
);
380 mBuf
= new CacheFileChunkBuffer(this);
385 CacheFileChunk::Read(CacheFileHandle
*aHandle
, uint32_t aLen
,
386 CacheHash::Hash16_t aHash
,
387 CacheFileChunkListener
*aCallback
)
391 LOG(("CacheFileChunk::Read() [this=%p, handle=%p, len=%d, listener=%p]",
392 this, aHandle
, aLen
, aCallback
));
394 MOZ_ASSERT(mState
== INITIAL
);
395 MOZ_ASSERT(NS_SUCCEEDED(mStatus
));
396 MOZ_ASSERT(!mBuf
->Buf());
397 MOZ_ASSERT(!mWritingStateHandle
);
398 MOZ_ASSERT(!mReadingStateBuf
);
405 RefPtr
<CacheFileChunkBuffer
> tmpBuf
= new CacheFileChunkBuffer(this);
406 rv
= tmpBuf
->EnsureBufSize(aLen
);
411 tmpBuf
->SetDataSize(aLen
);
413 rv
= CacheFileIOManager::Read(aHandle
, mIndex
* kChunkSize
,
416 if (NS_WARN_IF(NS_FAILED(rv
))) {
417 rv
= mIndex
? NS_ERROR_FILE_CORRUPTED
: NS_ERROR_FILE_NOT_FOUND
;
420 mReadingStateBuf
.swap(tmpBuf
);
421 mListener
= aCallback
;
422 // mBuf contains no data but we set datasize to size of the data that will
423 // be read from the disk. No handle is allowed to access the non-existent
424 // data until reading finishes, but data can be appended or overwritten.
425 // These pieces are tracked in mValidityMap and will be merged with the data
426 // read from disk in OnDataRead().
427 mBuf
->SetDataSize(aLen
);
428 mExpectedHash
= aHash
;
435 CacheFileChunk::Write(CacheFileHandle
*aHandle
,
436 CacheFileChunkListener
*aCallback
)
440 LOG(("CacheFileChunk::Write() [this=%p, handle=%p, listener=%p]",
441 this, aHandle
, aCallback
));
443 MOZ_ASSERT(mState
== READY
);
444 MOZ_ASSERT(NS_SUCCEEDED(mStatus
));
445 MOZ_ASSERT(!mWritingStateHandle
);
446 MOZ_ASSERT(mBuf
->DataSize()); // Don't write chunk when it is empty
447 MOZ_ASSERT(mBuf
->ReadHandlesCount() == 0);
448 MOZ_ASSERT(!mBuf
->WriteHandleExists());
453 mWritingStateHandle
= new CacheFileChunkReadHandle(mBuf
);
455 rv
= CacheFileIOManager::Write(aHandle
, mIndex
* kChunkSize
,
456 mWritingStateHandle
->Buf(),
457 mWritingStateHandle
->DataSize(),
459 if (NS_WARN_IF(NS_FAILED(rv
))) {
460 mWritingStateHandle
= nullptr;
463 mListener
= aCallback
;
471 CacheFileChunk::WaitForUpdate(CacheFileChunkListener
*aCallback
)
475 LOG(("CacheFileChunk::WaitForUpdate() [this=%p, listener=%p]",
478 MOZ_ASSERT(mFile
->mOutput
);
479 MOZ_ASSERT(IsReady());
482 for (uint32_t i
= 0 ; i
< mUpdateListeners
.Length() ; i
++) {
483 MOZ_ASSERT(mUpdateListeners
[i
]->mCallback
!= aCallback
);
487 ChunkListenerItem
*item
= new ChunkListenerItem();
488 item
->mTarget
= CacheFileIOManager::IOTarget();
489 if (!item
->mTarget
) {
490 LOG(("CacheFileChunk::WaitForUpdate() - Cannot get Cache I/O thread! Using "
491 "main thread for callback."));
492 item
->mTarget
= GetMainThreadEventTarget();
494 item
->mCallback
= aCallback
;
495 MOZ_ASSERT(item
->mTarget
);
496 item
->mCallback
= aCallback
;
498 mUpdateListeners
.AppendElement(item
);
502 CacheFileChunk::CancelWait(CacheFileChunkListener
*aCallback
)
506 LOG(("CacheFileChunk::CancelWait() [this=%p, listener=%p]", this, aCallback
));
508 MOZ_ASSERT(IsReady());
511 for (i
= 0 ; i
< mUpdateListeners
.Length() ; i
++) {
512 ChunkListenerItem
*item
= mUpdateListeners
[i
];
514 if (item
->mCallback
== aCallback
) {
515 mUpdateListeners
.RemoveElementAt(i
);
522 for ( ; i
< mUpdateListeners
.Length() ; i
++) {
523 MOZ_ASSERT(mUpdateListeners
[i
]->mCallback
!= aCallback
);
531 CacheFileChunk::NotifyUpdateListeners()
535 LOG(("CacheFileChunk::NotifyUpdateListeners() [this=%p]", this));
537 MOZ_ASSERT(IsReady());
542 for (uint32_t i
= 0 ; i
< mUpdateListeners
.Length() ; i
++) {
543 ChunkListenerItem
*item
= mUpdateListeners
[i
];
545 LOG(("CacheFileChunk::NotifyUpdateListeners() - Notifying listener %p "
546 "[this=%p]", item
->mCallback
.get(), this));
548 RefPtr
<NotifyUpdateListenerEvent
> ev
;
549 ev
= new NotifyUpdateListenerEvent(item
->mCallback
, this);
550 rv2
= item
->mTarget
->Dispatch(ev
, NS_DISPATCH_NORMAL
);
551 if (NS_FAILED(rv2
) && NS_SUCCEEDED(rv
))
556 mUpdateListeners
.Clear();
562 CacheFileChunk::Index() const
568 CacheFileChunk::Hash() const
570 MOZ_ASSERT(IsReady());
572 return CacheHash::Hash16(mBuf
->Buf(), mBuf
->DataSize());
576 CacheFileChunk::DataSize() const
578 return mBuf
->DataSize();
582 CacheFileChunk::UpdateDataSize(uint32_t aOffset
, uint32_t aLen
)
586 // UpdateDataSize() is called only when we've written some data to the chunk
587 // and we never write data anymore once some error occurs.
588 MOZ_ASSERT(NS_SUCCEEDED(mStatus
));
590 LOG(("CacheFileChunk::UpdateDataSize() [this=%p, offset=%d, len=%d]",
591 this, aOffset
, aLen
));
595 int64_t fileSize
= static_cast<int64_t>(kChunkSize
) * mIndex
+ aOffset
+ aLen
;
598 if (fileSize
> mFile
->mDataSize
) {
599 mFile
->mDataSize
= fileSize
;
603 if (mState
== READY
|| mState
== WRITING
) {
604 MOZ_ASSERT(mValidityMap
.Length() == 0);
607 NotifyUpdateListeners();
613 // We're still waiting for data from the disk. This chunk cannot be used by
614 // input stream, so there must be no update listener. We also need to keep
615 // track of where the data is written so that we can correctly merge the new
616 // data with the old one.
618 MOZ_ASSERT(mUpdateListeners
.Length() == 0);
619 MOZ_ASSERT(mState
== READING
);
621 mValidityMap
.AddPair(aOffset
, aLen
);
626 CacheFileChunk::Truncate(uint32_t aOffset
)
628 MOZ_RELEASE_ASSERT(mState
== READY
|| mState
== WRITING
|| mState
== READING
);
630 if (mState
== READING
) {
634 mBuf
->SetDataSize(aOffset
);
639 CacheFileChunk::OnFileOpened(CacheFileHandle
*aHandle
, nsresult aResult
)
641 MOZ_CRASH("CacheFileChunk::OnFileOpened should not be called!");
642 return NS_ERROR_UNEXPECTED
;
646 CacheFileChunk::OnDataWritten(CacheFileHandle
*aHandle
, const char *aBuf
,
649 LOG(("CacheFileChunk::OnDataWritten() [this=%p, handle=%p, result=0x%08" PRIx32
"]",
650 this, aHandle
, static_cast<uint32_t>(aResult
)));
652 nsCOMPtr
<CacheFileChunkListener
> listener
;
655 CacheFileAutoLock
lock(mFile
);
657 MOZ_ASSERT(mState
== WRITING
);
658 MOZ_ASSERT(mListener
);
660 mWritingStateHandle
= nullptr;
662 if (NS_WARN_IF(NS_FAILED(aResult
))) {
667 mListener
.swap(listener
);
670 listener
->OnChunkWritten(aResult
, this);
676 CacheFileChunk::OnDataRead(CacheFileHandle
*aHandle
, char *aBuf
,
679 LOG(("CacheFileChunk::OnDataRead() [this=%p, handle=%p, result=0x%08" PRIx32
"]",
680 this, aHandle
, static_cast<uint32_t>(aResult
)));
682 nsCOMPtr
<CacheFileChunkListener
> listener
;
685 CacheFileAutoLock
lock(mFile
);
687 MOZ_ASSERT(mState
== READING
);
688 MOZ_ASSERT(mListener
);
689 MOZ_ASSERT(mReadingStateBuf
);
690 MOZ_RELEASE_ASSERT(mBuf
->ReadHandlesCount() == 0);
691 MOZ_RELEASE_ASSERT(!mBuf
->WriteHandleExists());
693 RefPtr
<CacheFileChunkBuffer
> tmpBuf
;
694 tmpBuf
.swap(mReadingStateBuf
);
696 if (NS_SUCCEEDED(aResult
)) {
697 CacheHash::Hash16_t hash
= CacheHash::Hash16(tmpBuf
->Buf(),
699 if (hash
!= mExpectedHash
) {
700 LOG(("CacheFileChunk::OnDataRead() - Hash mismatch! Hash of the data is"
701 " %hx, hash in metadata is %hx. [this=%p, idx=%d]",
702 hash
, mExpectedHash
, this, mIndex
));
703 aResult
= NS_ERROR_FILE_CORRUPTED
;
705 if (mBuf
->DataSize() < tmpBuf
->DataSize()) {
706 // Truncate() was called while the data was being read.
707 tmpBuf
->SetDataSize(mBuf
->DataSize());
711 // Just swap the buffers if mBuf is still empty
714 LOG(("CacheFileChunk::OnDataRead() - Merging buffers. [this=%p]",
718 aResult
= mBuf
->FillInvalidRanges(tmpBuf
, &mValidityMap
);
719 mValidityMap
.Clear();
724 if (NS_FAILED(aResult
)) {
725 aResult
= mIndex
? NS_ERROR_FILE_CORRUPTED
: NS_ERROR_FILE_NOT_FOUND
;
727 mBuf
->SetDataSize(0);
731 mListener
.swap(listener
);
734 listener
->OnChunkRead(aResult
, this);
740 CacheFileChunk::OnFileDoomed(CacheFileHandle
*aHandle
, nsresult aResult
)
742 MOZ_CRASH("CacheFileChunk::OnFileDoomed should not be called!");
743 return NS_ERROR_UNEXPECTED
;
747 CacheFileChunk::OnEOFSet(CacheFileHandle
*aHandle
, nsresult aResult
)
749 MOZ_CRASH("CacheFileChunk::OnEOFSet should not be called!");
750 return NS_ERROR_UNEXPECTED
;
754 CacheFileChunk::OnFileRenamed(CacheFileHandle
*aHandle
, nsresult aResult
)
756 MOZ_CRASH("CacheFileChunk::OnFileRenamed should not be called!");
757 return NS_ERROR_UNEXPECTED
;
761 CacheFileChunk::IsKilled()
763 return mFile
->IsKilled();
767 CacheFileChunk::IsReady() const
769 return (NS_SUCCEEDED(mStatus
) && (mState
== READY
|| mState
== WRITING
));
773 CacheFileChunk::IsDirty() const
781 CacheFileChunk::GetStatus()
787 CacheFileChunk::SetError(nsresult aStatus
)
789 LOG(("CacheFileChunk::SetError() [this=%p, status=0x%08" PRIx32
"]",
790 this, static_cast<uint32_t>(aStatus
)));
792 MOZ_ASSERT(NS_FAILED(aStatus
));
794 if (NS_FAILED(mStatus
)) {
795 // Remember only the first error code.
802 CacheFileChunkReadHandle
803 CacheFileChunk::GetReadHandle()
805 LOG(("CacheFileChunk::GetReadHandle() [this=%p]", this));
809 MOZ_RELEASE_ASSERT(mState
== READY
|| mState
== WRITING
);
810 // We don't release the lock when writing the data and CacheFileOutputStream
811 // doesn't get the read handle, so there cannot be a write handle when read
812 // handle is obtained.
813 MOZ_RELEASE_ASSERT(!mBuf
->WriteHandleExists());
815 return CacheFileChunkReadHandle(mBuf
);
818 CacheFileChunkWriteHandle
819 CacheFileChunk::GetWriteHandle(uint32_t aEnsuredBufSize
)
821 LOG(("CacheFileChunk::GetWriteHandle() [this=%p, ensuredBufSize=%u]",
822 this, aEnsuredBufSize
));
826 if (NS_FAILED(mStatus
)) {
827 return CacheFileChunkWriteHandle(nullptr); // dummy handle
832 // We don't support multiple write handles
833 MOZ_RELEASE_ASSERT(!mBuf
->WriteHandleExists());
835 if (mBuf
->ReadHandlesCount()) {
836 LOG(("CacheFileChunk::GetWriteHandle() - cloning buffer because of existing"
839 MOZ_RELEASE_ASSERT(mState
!= READING
);
840 RefPtr
<CacheFileChunkBuffer
> newBuf
= new CacheFileChunkBuffer(this);
841 rv
= newBuf
->EnsureBufSize(std::max(aEnsuredBufSize
, mBuf
->DataSize()));
842 if (NS_SUCCEEDED(rv
)) {
843 newBuf
->CopyFrom(mBuf
);
844 mOldBufs
.AppendElement(mBuf
);
848 rv
= mBuf
->EnsureBufSize(aEnsuredBufSize
);
852 SetError(NS_ERROR_OUT_OF_MEMORY
);
853 return CacheFileChunkWriteHandle(nullptr); // dummy handle
856 return CacheFileChunkWriteHandle(mBuf
);
862 CacheFileChunk::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf
) const
864 size_t n
= mBuf
->SizeOfIncludingThis(mallocSizeOf
);
866 if (mReadingStateBuf
) {
867 n
+= mReadingStateBuf
->SizeOfIncludingThis(mallocSizeOf
);
870 for (uint32_t i
= 0; i
< mOldBufs
.Length(); ++i
) {
871 n
+= mOldBufs
[i
]->SizeOfIncludingThis(mallocSizeOf
);
874 n
+= mValidityMap
.SizeOfExcludingThis(mallocSizeOf
);
880 CacheFileChunk::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf
) const
882 return mallocSizeOf(this) + SizeOfExcludingThis(mallocSizeOf
);
886 CacheFileChunk::CanAllocate(uint32_t aSize
) const
888 if (!mLimitAllocation
) {
892 LOG(("CacheFileChunk::CanAllocate() [this=%p, size=%u]", this, aSize
));
894 uint32_t limit
= CacheObserver::MaxDiskChunksMemoryUsage(mIsPriority
);
899 uint32_t usage
= ChunksMemoryUsage();
900 if (usage
+ aSize
> limit
) {
901 LOG(("CacheFileChunk::CanAllocate() - Returning false. [this=%p]", this));
909 CacheFileChunk::BuffersAllocationChanged(uint32_t aFreed
, uint32_t aAllocated
)
911 uint32_t oldBuffersSize
= mBuffersSize
;
912 mBuffersSize
+= aAllocated
;
913 mBuffersSize
-= aFreed
;
915 DoMemoryReport(sizeof(CacheFileChunk
) + mBuffersSize
);
917 if (!mLimitAllocation
) {
921 ChunksMemoryUsage() -= oldBuffersSize
;
922 ChunksMemoryUsage() += mBuffersSize
;
923 LOG(("CacheFileChunk::BuffersAllocationChanged() - %s chunks usage %u "
924 "[this=%p]", mIsPriority
? "Priority" : "Normal",
925 static_cast<uint32_t>(ChunksMemoryUsage()), this));
928 mozilla::Atomic
<uint32_t, ReleaseAcquire
>& CacheFileChunk::ChunksMemoryUsage() const
930 static mozilla::Atomic
<uint32_t, ReleaseAcquire
> chunksMemoryUsage(0);
931 static mozilla::Atomic
<uint32_t, ReleaseAcquire
> prioChunksMemoryUsage(0);
932 return mIsPriority
? prioChunksMemoryUsage
: chunksMemoryUsage
;
936 } // namespace mozilla