Bug 1494162 - Part 45: Lazy load Menu and MenuItem in TabBar. r=pbro
[gecko.git] / netwerk / cache2 / CacheFileChunk.cpp
blobd53be297e33dd2dff23a5a0310441f30ee0347e0
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 {
14 namespace net {
16 #define kMinBufSize 512
18 CacheFileChunkBuffer::CacheFileChunkBuffer(CacheFileChunk *aChunk)
19 : mChunk(aChunk)
20 , mBuf(nullptr)
21 , mBufSize(0)
22 , mDataSize(0)
23 , mReadHandlesCount(0)
24 , mWriteHandleExists(false)
28 CacheFileChunkBuffer::~CacheFileChunkBuffer()
30 if (mBuf) {
31 CacheFileUtils::FreeBuffer(mBuf);
32 mBuf = nullptr;
33 mChunk->BuffersAllocationChanged(mBufSize, 0);
34 mBufSize = 0;
38 void
39 CacheFileChunkBuffer::CopyFrom(CacheFileChunkBuffer *aOther)
41 MOZ_RELEASE_ASSERT(mBufSize >= aOther->mDataSize);
42 mDataSize = aOther->mDataSize;
43 memcpy(mBuf, aOther->mBuf, mDataSize);
46 nsresult
47 CacheFileChunkBuffer::FillInvalidRanges(CacheFileChunkBuffer *aOther,
48 CacheFileUtils::ValidityMap *aMap)
50 nsresult rv;
52 rv = EnsureBufSize(aOther->mDataSize);
53 if (NS_FAILED(rv)) {
54 return rv;
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);
78 return NS_OK;
81 MOZ_MUST_USE nsresult
82 CacheFileChunkBuffer::EnsureBufSize(uint32_t aBufSize)
84 AssertOwnsLock();
86 if (mBufSize >= aBufSize) {
87 return NS_OK;
90 // find smallest power of 2 greater than or equal to aBufSize
91 aBufSize--;
92 aBufSize |= aBufSize >> 1;
93 aBufSize |= aBufSize >> 2;
94 aBufSize |= aBufSize >> 4;
95 aBufSize |= aBufSize >> 8;
96 aBufSize |= aBufSize >> 16;
97 aBufSize++;
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));
108 if (!newBuf) {
109 return NS_ERROR_OUT_OF_MEMORY;
112 mChunk->BuffersAllocationChanged(mBufSize, aBufSize);
113 mBuf = newBuf;
114 mBufSize = aBufSize;
116 return NS_OK;
119 void
120 CacheFileChunkBuffer::SetDataSize(uint32_t aDataSize)
122 MOZ_RELEASE_ASSERT(
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;
136 void
137 CacheFileChunkBuffer::AssertOwnsLock() const
139 mChunk->AssertOwnsLock();
142 void
143 CacheFileChunkBuffer::RemoveReadHandle()
145 AssertOwnsLock();
146 MOZ_RELEASE_ASSERT(mReadHandlesCount);
147 MOZ_RELEASE_ASSERT(!mWriteHandleExists);
148 mReadHandlesCount--;
150 if (mReadHandlesCount == 0 && mChunk->mBuf != this) {
151 DebugOnly<bool> removed = mChunk->mOldBufs.RemoveElement(this);
152 MOZ_ASSERT(removed);
156 void
157 CacheFileChunkBuffer::RemoveWriteHandle()
159 AssertOwnsLock();
160 MOZ_RELEASE_ASSERT(mReadHandlesCount == 0);
161 MOZ_RELEASE_ASSERT(mWriteHandleExists);
162 mWriteHandleExists = false;
165 size_t
166 CacheFileChunkBuffer::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
168 size_t n = mallocSizeOf(this);
170 if (mBuf) {
171 n += mallocSizeOf(mBuf);
174 return n;
177 uint32_t
178 CacheFileChunkHandle::DataSize()
180 MOZ_ASSERT(mBuf, "Unexpected call on dummy handle");
181 mBuf->AssertOwnsLock();
182 return mBuf->mDataSize;
185 uint32_t
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)
195 mBuf = aBuf;
196 mBuf->mReadHandlesCount++;
199 CacheFileChunkReadHandle::~CacheFileChunkReadHandle()
201 mBuf->RemoveReadHandle();
204 const char *
205 CacheFileChunkReadHandle::Buf()
207 return mBuf->mBuf;
210 CacheFileChunkWriteHandle::CacheFileChunkWriteHandle(CacheFileChunkBuffer *aBuf)
212 mBuf = aBuf;
213 if (mBuf) {
214 MOZ_ASSERT(!mBuf->mWriteHandleExists);
215 mBuf->mWriteHandleExists = true;
219 CacheFileChunkWriteHandle::~CacheFileChunkWriteHandle()
221 if (mBuf) {
222 mBuf->RemoveWriteHandle();
226 char *
227 CacheFileChunkWriteHandle::Buf()
229 return mBuf ? mBuf->mBuf : nullptr;
232 void
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 {
248 public:
249 NotifyUpdateListenerEvent(CacheFileChunkListener* aCallback,
250 CacheFileChunk* aChunk)
251 : Runnable("net::NotifyUpdateListenerEvent")
252 , mCallback(aCallback)
253 , mChunk(aChunk)
255 LOG(("NotifyUpdateListenerEvent::NotifyUpdateListenerEvent() [this=%p]",
256 this));
259 protected:
260 ~NotifyUpdateListenerEvent()
262 LOG(("NotifyUpdateListenerEvent::~NotifyUpdateListenerEvent() [this=%p]",
263 this));
266 public:
267 NS_IMETHOD Run() override
269 LOG(("NotifyUpdateListenerEvent::Run() [this=%p]", this));
271 mCallback->OnChunkUpdated(mChunk);
272 return NS_OK;
275 protected:
276 nsCOMPtr<CacheFileChunkListener> mCallback;
277 RefPtr<CacheFileChunk> mChunk;
280 bool
281 CacheFileChunk::DispatchRelease()
283 if (NS_IsMainThread()) {
284 return false;
287 NS_DispatchToMainThread(NewNonOwningRunnableMethod(
288 "net::CacheFileChunk::Release", this, &CacheFileChunk::Release));
290 return true;
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.
300 return count;
303 MOZ_ASSERT(0 != mRefCnt, "dup release");
304 count = --mRefCnt;
305 NS_LOG_RELEASE(this, count, "CacheFileChunk");
307 if (0 == count) {
308 mRefCnt = 1;
309 delete (this);
310 return 0;
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);
327 return count;
330 NS_INTERFACE_MAP_BEGIN(CacheFileChunk)
331 NS_INTERFACE_MAP_ENTRY(mozilla::net::CacheFileIOListener)
332 NS_INTERFACE_MAP_ENTRY(nsISupports)
333 NS_INTERFACE_MAP_END
335 CacheFileChunk::CacheFileChunk(CacheFile *aFile, uint32_t aIndex,
336 bool aInitByWriter)
337 : CacheMemoryConsumer(aFile->mOpenAsMemoryOnly ? MEMORY_ONLY : DONT_REPORT)
338 , mIndex(aIndex)
339 , mState(INITIAL)
340 , mStatus(NS_OK)
341 , mActiveChunk(false)
342 , mIsDirty(false)
343 , mDiscardedChunk(false)
344 , mBuffersSize(0)
345 , mLimitAllocation(!aFile->mOpenAsMemoryOnly && aInitByWriter)
346 , mIsPriority(aFile->mPriority)
347 , mExpectedHash(0)
348 , mFile(aFile)
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));
360 void
361 CacheFileChunk::AssertOwnsLock() const
363 mFile->AssertOwnsLock();
366 void
367 CacheFileChunk::InitNew()
369 AssertOwnsLock();
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);
381 mState = READY;
384 nsresult
385 CacheFileChunk::Read(CacheFileHandle *aHandle, uint32_t aLen,
386 CacheHash::Hash16_t aHash,
387 CacheFileChunkListener *aCallback)
389 AssertOwnsLock();
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);
399 MOZ_ASSERT(aLen);
401 nsresult rv;
403 mState = READING;
405 RefPtr<CacheFileChunkBuffer> tmpBuf = new CacheFileChunkBuffer(this);
406 rv = tmpBuf->EnsureBufSize(aLen);
407 if (NS_FAILED(rv)) {
408 SetError(rv);
409 return mStatus;
411 tmpBuf->SetDataSize(aLen);
413 rv = CacheFileIOManager::Read(aHandle, mIndex * kChunkSize,
414 tmpBuf->Buf(), aLen,
415 this);
416 if (NS_WARN_IF(NS_FAILED(rv))) {
417 rv = mIndex ? NS_ERROR_FILE_CORRUPTED : NS_ERROR_FILE_NOT_FOUND;
418 SetError(rv);
419 } else {
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;
431 return rv;
434 nsresult
435 CacheFileChunk::Write(CacheFileHandle *aHandle,
436 CacheFileChunkListener *aCallback)
438 AssertOwnsLock();
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());
450 nsresult rv;
452 mState = WRITING;
453 mWritingStateHandle = new CacheFileChunkReadHandle(mBuf);
455 rv = CacheFileIOManager::Write(aHandle, mIndex * kChunkSize,
456 mWritingStateHandle->Buf(),
457 mWritingStateHandle->DataSize(),
458 false, false, this);
459 if (NS_WARN_IF(NS_FAILED(rv))) {
460 mWritingStateHandle = nullptr;
461 SetError(rv);
462 } else {
463 mListener = aCallback;
464 mIsDirty = false;
467 return rv;
470 void
471 CacheFileChunk::WaitForUpdate(CacheFileChunkListener *aCallback)
473 AssertOwnsLock();
475 LOG(("CacheFileChunk::WaitForUpdate() [this=%p, listener=%p]",
476 this, aCallback));
478 MOZ_ASSERT(mFile->mOutput);
479 MOZ_ASSERT(IsReady());
481 #ifdef DEBUG
482 for (uint32_t i = 0 ; i < mUpdateListeners.Length() ; i++) {
483 MOZ_ASSERT(mUpdateListeners[i]->mCallback != aCallback);
485 #endif
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);
501 nsresult
502 CacheFileChunk::CancelWait(CacheFileChunkListener *aCallback)
504 AssertOwnsLock();
506 LOG(("CacheFileChunk::CancelWait() [this=%p, listener=%p]", this, aCallback));
508 MOZ_ASSERT(IsReady());
510 uint32_t i;
511 for (i = 0 ; i < mUpdateListeners.Length() ; i++) {
512 ChunkListenerItem *item = mUpdateListeners[i];
514 if (item->mCallback == aCallback) {
515 mUpdateListeners.RemoveElementAt(i);
516 delete item;
517 break;
521 #ifdef DEBUG
522 for ( ; i < mUpdateListeners.Length() ; i++) {
523 MOZ_ASSERT(mUpdateListeners[i]->mCallback != aCallback);
525 #endif
527 return NS_OK;
530 nsresult
531 CacheFileChunk::NotifyUpdateListeners()
533 AssertOwnsLock();
535 LOG(("CacheFileChunk::NotifyUpdateListeners() [this=%p]", this));
537 MOZ_ASSERT(IsReady());
539 nsresult rv, rv2;
541 rv = NS_OK;
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))
552 rv = rv2;
553 delete item;
556 mUpdateListeners.Clear();
558 return rv;
561 uint32_t
562 CacheFileChunk::Index() const
564 return mIndex;
567 CacheHash::Hash16_t
568 CacheFileChunk::Hash() const
570 MOZ_ASSERT(IsReady());
572 return CacheHash::Hash16(mBuf->Buf(), mBuf->DataSize());
575 uint32_t
576 CacheFileChunk::DataSize() const
578 return mBuf->DataSize();
581 void
582 CacheFileChunk::UpdateDataSize(uint32_t aOffset, uint32_t aLen)
584 AssertOwnsLock();
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));
593 mIsDirty = true;
595 int64_t fileSize = static_cast<int64_t>(kChunkSize) * mIndex + aOffset + aLen;
596 bool notify = false;
598 if (fileSize > mFile->mDataSize) {
599 mFile->mDataSize = fileSize;
600 notify = true;
603 if (mState == READY || mState == WRITING) {
604 MOZ_ASSERT(mValidityMap.Length() == 0);
606 if (notify) {
607 NotifyUpdateListeners();
610 return;
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);
622 mValidityMap.Log();
625 nsresult
626 CacheFileChunk::Truncate(uint32_t aOffset)
628 MOZ_RELEASE_ASSERT(mState == READY || mState == WRITING || mState == READING);
630 if (mState == READING) {
631 mIsDirty = true;
634 mBuf->SetDataSize(aOffset);
635 return NS_OK;
638 nsresult
639 CacheFileChunk::OnFileOpened(CacheFileHandle *aHandle, nsresult aResult)
641 MOZ_CRASH("CacheFileChunk::OnFileOpened should not be called!");
642 return NS_ERROR_UNEXPECTED;
645 nsresult
646 CacheFileChunk::OnDataWritten(CacheFileHandle *aHandle, const char *aBuf,
647 nsresult aResult)
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))) {
663 SetError(aResult);
666 mState = READY;
667 mListener.swap(listener);
670 listener->OnChunkWritten(aResult, this);
672 return NS_OK;
675 nsresult
676 CacheFileChunk::OnDataRead(CacheFileHandle *aHandle, char *aBuf,
677 nsresult aResult)
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(),
698 tmpBuf->DataSize());
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;
704 } else {
705 if (mBuf->DataSize() < tmpBuf->DataSize()) {
706 // Truncate() was called while the data was being read.
707 tmpBuf->SetDataSize(mBuf->DataSize());
710 if (!mBuf->Buf()) {
711 // Just swap the buffers if mBuf is still empty
712 mBuf.swap(tmpBuf);
713 } else {
714 LOG(("CacheFileChunk::OnDataRead() - Merging buffers. [this=%p]",
715 this));
717 mValidityMap.Log();
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;
726 SetError(aResult);
727 mBuf->SetDataSize(0);
730 mState = READY;
731 mListener.swap(listener);
734 listener->OnChunkRead(aResult, this);
736 return NS_OK;
739 nsresult
740 CacheFileChunk::OnFileDoomed(CacheFileHandle *aHandle, nsresult aResult)
742 MOZ_CRASH("CacheFileChunk::OnFileDoomed should not be called!");
743 return NS_ERROR_UNEXPECTED;
746 nsresult
747 CacheFileChunk::OnEOFSet(CacheFileHandle *aHandle, nsresult aResult)
749 MOZ_CRASH("CacheFileChunk::OnEOFSet should not be called!");
750 return NS_ERROR_UNEXPECTED;
753 nsresult
754 CacheFileChunk::OnFileRenamed(CacheFileHandle *aHandle, nsresult aResult)
756 MOZ_CRASH("CacheFileChunk::OnFileRenamed should not be called!");
757 return NS_ERROR_UNEXPECTED;
760 bool
761 CacheFileChunk::IsKilled()
763 return mFile->IsKilled();
766 bool
767 CacheFileChunk::IsReady() const
769 return (NS_SUCCEEDED(mStatus) && (mState == READY || mState == WRITING));
772 bool
773 CacheFileChunk::IsDirty() const
775 AssertOwnsLock();
777 return mIsDirty;
780 nsresult
781 CacheFileChunk::GetStatus()
783 return mStatus;
786 void
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.
796 return;
799 mStatus = aStatus;
802 CacheFileChunkReadHandle
803 CacheFileChunk::GetReadHandle()
805 LOG(("CacheFileChunk::GetReadHandle() [this=%p]", this));
807 AssertOwnsLock();
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));
824 AssertOwnsLock();
826 if (NS_FAILED(mStatus)) {
827 return CacheFileChunkWriteHandle(nullptr); // dummy handle
830 nsresult rv;
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"
837 " read handle"));
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);
845 mBuf = newBuf;
847 } else {
848 rv = mBuf->EnsureBufSize(aEnsuredBufSize);
851 if (NS_FAILED(rv)) {
852 SetError(NS_ERROR_OUT_OF_MEMORY);
853 return CacheFileChunkWriteHandle(nullptr); // dummy handle
856 return CacheFileChunkWriteHandle(mBuf);
859 // Memory reporting
861 size_t
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);
876 return n;
879 size_t
880 CacheFileChunk::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
882 return mallocSizeOf(this) + SizeOfExcludingThis(mallocSizeOf);
885 bool
886 CacheFileChunk::CanAllocate(uint32_t aSize) const
888 if (!mLimitAllocation) {
889 return true;
892 LOG(("CacheFileChunk::CanAllocate() [this=%p, size=%u]", this, aSize));
894 uint32_t limit = CacheObserver::MaxDiskChunksMemoryUsage(mIsPriority);
895 if (limit == 0) {
896 return true;
899 uint32_t usage = ChunksMemoryUsage();
900 if (usage + aSize > limit) {
901 LOG(("CacheFileChunk::CanAllocate() - Returning false. [this=%p]", this));
902 return false;
905 return true;
908 void
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) {
918 return;
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;
935 } // namespace net
936 } // namespace mozilla