Bug 1494162 - Part 45: Lazy load Menu and MenuItem in TabBar. r=pbro
[gecko.git] / netwerk / cache2 / CacheFileInputStream.cpp
bloba81f20023d7d8b929de6ef5f199b1a5b8f95ce37
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 "CacheFileInputStream.h"
8 #include "CacheFile.h"
9 #include "nsStreamUtils.h"
10 #include "nsThreadUtils.h"
11 #include <algorithm>
13 namespace mozilla {
14 namespace net {
16 NS_IMPL_ADDREF(CacheFileInputStream)
17 NS_IMETHODIMP_(MozExternalRefCountType)
18 CacheFileInputStream::Release()
20 MOZ_ASSERT(0 != mRefCnt, "dup release");
21 nsrefcnt count = --mRefCnt;
22 NS_LOG_RELEASE(this, count, "CacheFileInputStream");
24 if (0 == count) {
25 mRefCnt = 1;
26 delete (this);
27 return 0;
30 if (count == 1) {
31 mFile->RemoveInput(this, mStatus);
34 return count;
37 NS_INTERFACE_MAP_BEGIN(CacheFileInputStream)
38 NS_INTERFACE_MAP_ENTRY(nsIInputStream)
39 NS_INTERFACE_MAP_ENTRY(nsIAsyncInputStream)
40 NS_INTERFACE_MAP_ENTRY(nsISeekableStream)
41 NS_INTERFACE_MAP_ENTRY(mozilla::net::CacheFileChunkListener)
42 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInputStream)
43 NS_INTERFACE_MAP_END
45 CacheFileInputStream::CacheFileInputStream(CacheFile *aFile,
46 nsISupports *aEntry,
47 bool aAlternativeData)
48 : mFile(aFile)
49 , mPos(0)
50 , mStatus(NS_OK)
51 , mClosed(false)
52 , mInReadSegments(false)
53 , mWaitingForUpdate(false)
54 , mAlternativeData(aAlternativeData)
55 , mListeningForChunk(-1)
56 , mCallbackFlags(0)
57 , mCacheEntryHandle(aEntry)
59 LOG(("CacheFileInputStream::CacheFileInputStream() [this=%p]", this));
61 if (mAlternativeData) {
62 mPos = mFile->mAltDataOffset;
66 CacheFileInputStream::~CacheFileInputStream()
68 LOG(("CacheFileInputStream::~CacheFileInputStream() [this=%p]", this));
69 MOZ_ASSERT(!mInReadSegments);
72 // nsIInputStream
73 NS_IMETHODIMP
74 CacheFileInputStream::Close()
76 LOG(("CacheFileInputStream::Close() [this=%p]", this));
77 return CloseWithStatus(NS_OK);
80 NS_IMETHODIMP
81 CacheFileInputStream::Available(uint64_t *_retval)
83 CacheFileAutoLock lock(mFile);
85 if (mClosed) {
86 LOG(("CacheFileInputStream::Available() - Stream is closed. [this=%p, "
87 "status=0x%08" PRIx32 "]", this, static_cast<uint32_t>(mStatus)));
88 return NS_FAILED(mStatus) ? mStatus : NS_BASE_STREAM_CLOSED;
91 EnsureCorrectChunk(false);
92 if (NS_FAILED(mStatus)) {
93 LOG(("CacheFileInputStream::Available() - EnsureCorrectChunk failed. "
94 "[this=%p, status=0x%08" PRIx32 "]", this, static_cast<uint32_t>(mStatus)));
95 return mStatus;
98 nsresult rv = NS_OK;
99 *_retval = 0;
101 if (mChunk) {
102 int64_t canRead = mFile->BytesFromChunk(mChunk->Index(), mAlternativeData);
103 canRead -= (mPos % kChunkSize);
105 if (canRead > 0) {
106 *_retval = canRead;
107 } else if (canRead == 0 && !mFile->OutputStreamExists(mAlternativeData)) {
108 rv = NS_BASE_STREAM_CLOSED;
112 LOG(("CacheFileInputStream::Available() [this=%p, retval=%" PRIu64 ", rv=0x%08" PRIx32 "]",
113 this, *_retval, static_cast<uint32_t>(rv)));
115 return rv;
118 NS_IMETHODIMP
119 CacheFileInputStream::Read(char *aBuf, uint32_t aCount, uint32_t *_retval)
121 LOG(("CacheFileInputStream::Read() [this=%p, count=%d]", this, aCount));
122 return ReadSegments(NS_CopySegmentToBuffer, aBuf, aCount, _retval);
125 NS_IMETHODIMP
126 CacheFileInputStream::ReadSegments(nsWriteSegmentFun aWriter, void *aClosure,
127 uint32_t aCount, uint32_t *_retval)
129 CacheFileAutoLock lock(mFile);
131 LOG(("CacheFileInputStream::ReadSegments() [this=%p, count=%d]",
132 this, aCount));
134 nsresult rv = NS_OK;
136 *_retval = 0;
138 if (mInReadSegments) {
139 LOG(("CacheFileInputStream::ReadSegments() - Cannot be called while the "
140 "stream is in ReadSegments!"));
141 return NS_ERROR_UNEXPECTED;
144 if (mClosed) {
145 LOG(("CacheFileInputStream::ReadSegments() - Stream is closed. [this=%p, "
146 "status=0x%08" PRIx32 "]", this, static_cast<uint32_t>(mStatus)));
148 if (NS_FAILED(mStatus)) {
149 return mStatus;
152 return NS_OK;
155 if (aCount == 0) {
156 return NS_OK;
159 EnsureCorrectChunk(false);
161 while (true) {
162 if (NS_FAILED(mStatus))
163 return mStatus;
165 if (!mChunk) {
166 if (mListeningForChunk == -1) {
167 return NS_OK;
169 return NS_BASE_STREAM_WOULD_BLOCK;
172 CacheFileChunkReadHandle hnd = mChunk->GetReadHandle();
173 int64_t canRead = CanRead(&hnd);
174 if (NS_FAILED(mStatus)) {
175 return mStatus;
178 if (canRead < 0) {
179 // file was truncated ???
180 MOZ_ASSERT(false, "SetEOF is currenty not implemented?!");
181 rv = NS_OK;
182 } else if (canRead > 0) {
183 uint32_t toRead = std::min(static_cast<uint32_t>(canRead), aCount);
184 uint32_t read;
185 const char *buf = hnd.Buf() + (mPos - hnd.Offset());
187 mInReadSegments = true;
188 lock.Unlock();
190 rv = aWriter(this, aClosure, buf, *_retval, toRead, &read);
192 lock.Lock();
193 mInReadSegments = false;
195 if (NS_SUCCEEDED(rv)) {
196 MOZ_ASSERT(read <= toRead,
197 "writer should not write more than we asked it to write");
199 *_retval += read;
200 mPos += read;
201 aCount -= read;
203 if (!mClosed) {
204 // The last chunk is released after the caller closes this stream.
205 EnsureCorrectChunk(false);
207 if (mChunk && aCount) {
208 // Check whether there is more data available to read.
209 continue;
214 if (mClosed) {
215 // The stream was closed from aWriter, do the cleanup.
216 CleanUp();
219 rv = NS_OK;
220 } else {
221 if (*_retval == 0 && mFile->OutputStreamExists(mAlternativeData)) {
222 rv = NS_BASE_STREAM_WOULD_BLOCK;
223 } else {
224 rv = NS_OK;
228 break;
231 LOG(("CacheFileInputStream::ReadSegments() [this=%p, rv=0x%08" PRIx32 ", retval=%d]",
232 this, static_cast<uint32_t>(rv), *_retval));
234 return rv;
237 NS_IMETHODIMP
238 CacheFileInputStream::IsNonBlocking(bool *_retval)
240 *_retval = true;
241 return NS_OK;
244 // nsIAsyncInputStream
245 NS_IMETHODIMP
246 CacheFileInputStream::CloseWithStatus(nsresult aStatus)
248 CacheFileAutoLock lock(mFile);
250 LOG(("CacheFileInputStream::CloseWithStatus() [this=%p, aStatus=0x%08" PRIx32 "]",
251 this, static_cast<uint32_t>(aStatus)));
253 return CloseWithStatusLocked(aStatus);
256 nsresult
257 CacheFileInputStream::CloseWithStatusLocked(nsresult aStatus)
259 LOG(("CacheFileInputStream::CloseWithStatusLocked() [this=%p, "
260 "aStatus=0x%08" PRIx32 "]", this, static_cast<uint32_t>(aStatus)));
262 if (mClosed) {
263 // We notify listener and null out mCallback immediately after closing
264 // the stream. If we're in ReadSegments we postpone notification until we
265 // step out from ReadSegments. So if the stream is already closed the
266 // following assertion must be true.
267 MOZ_ASSERT(!mCallback || mInReadSegments);
269 return NS_OK;
272 mClosed = true;
273 mStatus = NS_FAILED(aStatus) ? aStatus : NS_BASE_STREAM_CLOSED;
275 if (!mInReadSegments) {
276 CleanUp();
279 return NS_OK;
282 void
283 CacheFileInputStream::CleanUp()
285 MOZ_ASSERT(!mInReadSegments);
286 MOZ_ASSERT(mClosed);
288 if (mChunk) {
289 ReleaseChunk();
292 // TODO propagate error from input stream to other streams ???
294 MaybeNotifyListener();
296 mFile->ReleaseOutsideLock(mCacheEntryHandle.forget());
299 NS_IMETHODIMP
300 CacheFileInputStream::AsyncWait(nsIInputStreamCallback *aCallback,
301 uint32_t aFlags,
302 uint32_t aRequestedCount,
303 nsIEventTarget *aEventTarget)
305 CacheFileAutoLock lock(mFile);
307 LOG(("CacheFileInputStream::AsyncWait() [this=%p, callback=%p, flags=%d, "
308 "requestedCount=%d, eventTarget=%p]", this, aCallback, aFlags,
309 aRequestedCount, aEventTarget));
311 if (mInReadSegments) {
312 LOG(("CacheFileInputStream::AsyncWait() - Cannot be called while the stream"
313 " is in ReadSegments!"));
314 MOZ_ASSERT(false, "Unexpected call. If it's a valid usage implement it. "
315 "Otherwise fix the caller.");
316 return NS_ERROR_UNEXPECTED;
319 mCallback = aCallback;
320 mCallbackFlags = aFlags;
321 mCallbackTarget = aEventTarget;
323 if (!mCallback) {
324 if (mWaitingForUpdate) {
325 mChunk->CancelWait(this);
326 mWaitingForUpdate = false;
328 return NS_OK;
331 if (mClosed) {
332 NotifyListener();
333 return NS_OK;
336 EnsureCorrectChunk(false);
338 MaybeNotifyListener();
340 return NS_OK;
343 // nsISeekableStream
344 NS_IMETHODIMP
345 CacheFileInputStream::Seek(int32_t whence, int64_t offset)
347 CacheFileAutoLock lock(mFile);
349 LOG(("CacheFileInputStream::Seek() [this=%p, whence=%d, offset=%" PRId64 "]",
350 this, whence, offset));
352 if (mInReadSegments) {
353 LOG(("CacheFileInputStream::Seek() - Cannot be called while the stream is "
354 "in ReadSegments!"));
355 return NS_ERROR_UNEXPECTED;
358 if (mClosed) {
359 LOG(("CacheFileInputStream::Seek() - Stream is closed. [this=%p]", this));
360 return NS_BASE_STREAM_CLOSED;
363 int64_t newPos = offset;
364 switch (whence) {
365 case NS_SEEK_SET:
366 if (mAlternativeData) {
367 newPos += mFile->mAltDataOffset;
369 break;
370 case NS_SEEK_CUR:
371 newPos += mPos;
372 break;
373 case NS_SEEK_END:
374 if (mAlternativeData) {
375 newPos += mFile->mDataSize;
376 } else {
377 newPos += mFile->mAltDataOffset;
379 break;
380 default:
381 NS_ERROR("invalid whence");
382 return NS_ERROR_INVALID_ARG;
384 mPos = newPos;
385 EnsureCorrectChunk(false);
387 LOG(("CacheFileInputStream::Seek() [this=%p, pos=%" PRId64 "]", this, mPos));
388 return NS_OK;
391 NS_IMETHODIMP
392 CacheFileInputStream::Tell(int64_t *_retval)
394 CacheFileAutoLock lock(mFile);
396 if (mClosed) {
397 LOG(("CacheFileInputStream::Tell() - Stream is closed. [this=%p]", this));
398 return NS_BASE_STREAM_CLOSED;
401 *_retval = mPos;
403 if (mAlternativeData) {
404 *_retval -= mFile->mAltDataOffset;
407 LOG(("CacheFileInputStream::Tell() [this=%p, retval=%" PRId64 "]", this, *_retval));
408 return NS_OK;
411 NS_IMETHODIMP
412 CacheFileInputStream::SetEOF()
414 MOZ_ASSERT(false, "Don't call SetEOF on cache input stream");
415 return NS_ERROR_NOT_IMPLEMENTED;
418 // CacheFileChunkListener
419 nsresult
420 CacheFileInputStream::OnChunkRead(nsresult aResult, CacheFileChunk *aChunk)
422 MOZ_CRASH("CacheFileInputStream::OnChunkRead should not be called!");
423 return NS_ERROR_UNEXPECTED;
426 nsresult
427 CacheFileInputStream::OnChunkWritten(nsresult aResult, CacheFileChunk *aChunk)
429 MOZ_CRASH("CacheFileInputStream::OnChunkWritten should not be called!");
430 return NS_ERROR_UNEXPECTED;
433 nsresult
434 CacheFileInputStream::OnChunkAvailable(nsresult aResult, uint32_t aChunkIdx,
435 CacheFileChunk *aChunk)
437 CacheFileAutoLock lock(mFile);
439 LOG(("CacheFileInputStream::OnChunkAvailable() [this=%p, result=0x%08" PRIx32 ", "
440 "idx=%d, chunk=%p]", this, static_cast<uint32_t>(aResult), aChunkIdx, aChunk));
442 MOZ_ASSERT(mListeningForChunk != -1);
444 if (mListeningForChunk != static_cast<int64_t>(aChunkIdx)) {
445 // This is not a chunk that we're waiting for
446 LOG(("CacheFileInputStream::OnChunkAvailable() - Notification is for a "
447 "different chunk. [this=%p, listeningForChunk=%" PRId64 "]",
448 this, mListeningForChunk));
450 return NS_OK;
453 MOZ_ASSERT(!mChunk);
454 MOZ_ASSERT(!mWaitingForUpdate);
455 MOZ_ASSERT(!mInReadSegments);
456 mListeningForChunk = -1;
458 if (mClosed) {
459 MOZ_ASSERT(!mCallback);
461 LOG(("CacheFileInputStream::OnChunkAvailable() - Stream is closed, "
462 "ignoring notification. [this=%p]", this));
464 return NS_OK;
467 if (NS_SUCCEEDED(aResult)) {
468 mChunk = aChunk;
469 } else if (aResult != NS_ERROR_NOT_AVAILABLE) {
470 // Close the stream with error. The consumer will receive this error later
471 // in Read(), Available() etc. We need to handle NS_ERROR_NOT_AVAILABLE
472 // differently since it is returned when the requested chunk is not
473 // available and there is no writer that could create it, i.e. it means that
474 // we've reached the end of the file.
475 CloseWithStatusLocked(aResult);
477 return NS_OK;
480 MaybeNotifyListener();
482 return NS_OK;
485 nsresult
486 CacheFileInputStream::OnChunkUpdated(CacheFileChunk *aChunk)
488 CacheFileAutoLock lock(mFile);
490 LOG(("CacheFileInputStream::OnChunkUpdated() [this=%p, idx=%d]",
491 this, aChunk->Index()));
493 if (!mWaitingForUpdate) {
494 LOG(("CacheFileInputStream::OnChunkUpdated() - Ignoring notification since "
495 "mWaitingforUpdate == false. [this=%p]", this));
497 return NS_OK;
500 mWaitingForUpdate = false;
502 MOZ_ASSERT(mChunk == aChunk);
504 MaybeNotifyListener();
506 return NS_OK;
509 void
510 CacheFileInputStream::ReleaseChunk()
512 mFile->AssertOwnsLock();
514 LOG(("CacheFileInputStream::ReleaseChunk() [this=%p, idx=%d]",
515 this, mChunk->Index()));
517 MOZ_ASSERT(!mInReadSegments);
519 if (mWaitingForUpdate) {
520 LOG(("CacheFileInputStream::ReleaseChunk() - Canceling waiting for update. "
521 "[this=%p]", this));
523 mChunk->CancelWait(this);
524 mWaitingForUpdate = false;
527 mFile->ReleaseOutsideLock(mChunk.forget());
530 void
531 CacheFileInputStream::EnsureCorrectChunk(bool aReleaseOnly)
533 mFile->AssertOwnsLock();
535 LOG(("CacheFileInputStream::EnsureCorrectChunk() [this=%p, releaseOnly=%d]",
536 this, aReleaseOnly));
538 nsresult rv;
540 uint32_t chunkIdx = mPos / kChunkSize;
542 if (mInReadSegments) {
543 // We must have correct chunk
544 MOZ_ASSERT(mChunk);
545 MOZ_ASSERT(mChunk->Index() == chunkIdx);
546 return;
549 if (mChunk) {
550 if (mChunk->Index() == chunkIdx) {
551 // we have a correct chunk
552 LOG(("CacheFileInputStream::EnsureCorrectChunk() - Have correct chunk "
553 "[this=%p, idx=%d]", this, chunkIdx));
555 return;
557 ReleaseChunk();
560 MOZ_ASSERT(!mWaitingForUpdate);
562 if (aReleaseOnly)
563 return;
565 if (mListeningForChunk == static_cast<int64_t>(chunkIdx)) {
566 // We're already waiting for this chunk
567 LOG(("CacheFileInputStream::EnsureCorrectChunk() - Already listening for "
568 "chunk %" PRId64 " [this=%p]", mListeningForChunk, this));
570 return;
573 rv = mFile->GetChunkLocked(chunkIdx, CacheFile::READER, this,
574 getter_AddRefs(mChunk));
575 if (NS_FAILED(rv)) {
576 LOG(("CacheFileInputStream::EnsureCorrectChunk() - GetChunkLocked failed. "
577 "[this=%p, idx=%d, rv=0x%08" PRIx32 "]", this, chunkIdx,
578 static_cast<uint32_t>(rv)));
579 if (rv != NS_ERROR_NOT_AVAILABLE) {
580 // Close the stream with error. The consumer will receive this error later
581 // in Read(), Available() etc. We need to handle NS_ERROR_NOT_AVAILABLE
582 // differently since it is returned when the requested chunk is not
583 // available and there is no writer that could create it, i.e. it means
584 // that we've reached the end of the file.
585 CloseWithStatusLocked(rv);
587 return;
589 } else if (!mChunk) {
590 mListeningForChunk = static_cast<int64_t>(chunkIdx);
593 MaybeNotifyListener();
596 int64_t
597 CacheFileInputStream::CanRead(CacheFileChunkReadHandle *aHandle)
599 mFile->AssertOwnsLock();
601 MOZ_ASSERT(mChunk);
602 MOZ_ASSERT(mPos / kChunkSize == mChunk->Index());
604 int64_t retval = aHandle->Offset() + aHandle->DataSize();
606 if (!mAlternativeData && mFile->mAltDataOffset != -1 &&
607 mFile->mAltDataOffset < retval) {
608 retval = mFile->mAltDataOffset;
611 retval -= mPos;
612 if (retval <= 0 && NS_FAILED(mChunk->GetStatus())) {
613 CloseWithStatusLocked(mChunk->GetStatus());
616 LOG(("CacheFileInputStream::CanRead() [this=%p, canRead=%" PRId64 "]",
617 this, retval));
619 return retval;
622 void
623 CacheFileInputStream::NotifyListener()
625 mFile->AssertOwnsLock();
627 LOG(("CacheFileInputStream::NotifyListener() [this=%p]", this));
629 MOZ_ASSERT(mCallback);
630 MOZ_ASSERT(!mInReadSegments);
632 if (!mCallbackTarget) {
633 mCallbackTarget = CacheFileIOManager::IOTarget();
634 if (!mCallbackTarget) {
635 LOG(("CacheFileInputStream::NotifyListener() - Cannot get Cache I/O "
636 "thread! Using main thread for callback."));
637 mCallbackTarget = GetMainThreadEventTarget();
641 nsCOMPtr<nsIInputStreamCallback> asyncCallback =
642 NS_NewInputStreamReadyEvent("CacheFileInputStream::NotifyListener",
643 mCallback, mCallbackTarget);
645 mCallback = nullptr;
646 mCallbackTarget = nullptr;
648 asyncCallback->OnInputStreamReady(this);
651 void
652 CacheFileInputStream::MaybeNotifyListener()
654 mFile->AssertOwnsLock();
656 LOG(("CacheFileInputStream::MaybeNotifyListener() [this=%p, mCallback=%p, "
657 "mClosed=%d, mStatus=0x%08" PRIx32 ", mChunk=%p, mListeningForChunk=%" PRId64 ", "
658 "mWaitingForUpdate=%d]", this, mCallback.get(), mClosed,
659 static_cast<uint32_t>(mStatus), mChunk.get(), mListeningForChunk,
660 mWaitingForUpdate));
662 MOZ_ASSERT(!mInReadSegments);
664 if (!mCallback)
665 return;
667 if (mClosed || NS_FAILED(mStatus)) {
668 NotifyListener();
669 return;
672 if (!mChunk) {
673 if (mListeningForChunk == -1) {
674 // EOF, should we notify even if mCallbackFlags == WAIT_CLOSURE_ONLY ??
675 NotifyListener();
677 return;
680 MOZ_ASSERT(mPos / kChunkSize == mChunk->Index());
682 if (mWaitingForUpdate)
683 return;
685 CacheFileChunkReadHandle hnd = mChunk->GetReadHandle();
686 int64_t canRead = CanRead(&hnd);
687 if (NS_FAILED(mStatus)) {
688 // CanRead() called CloseWithStatusLocked() which called
689 // MaybeNotifyListener() so the listener was already notified. Stop here.
690 MOZ_ASSERT(!mCallback);
691 return;
694 if (canRead > 0) {
695 if (!(mCallbackFlags & WAIT_CLOSURE_ONLY))
696 NotifyListener();
698 else if (canRead == 0) {
699 if (!mFile->OutputStreamExists(mAlternativeData)) {
700 // EOF
701 NotifyListener();
703 else {
704 mChunk->WaitForUpdate(this);
705 mWaitingForUpdate = true;
708 else {
709 // Output have set EOF before mPos?
710 MOZ_ASSERT(false, "SetEOF is currenty not implemented?!");
711 NotifyListener();
715 // Memory reporting
717 size_t
718 CacheFileInputStream::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
720 // Everything the stream keeps a reference to is already reported somewhere else.
721 // mFile reports itself.
722 // mChunk reported as part of CacheFile.
723 // mCallback is usually CacheFile or a class that is reported elsewhere.
724 return mallocSizeOf(this);
727 } // namespace net
728 } // namespace mozilla