Backed out changeset b71c8c052463 (bug 1943846) for causing mass failures. CLOSED...
[gecko.git] / netwerk / cache2 / CacheFileInputStream.cpp
blob564a08fd2ba638edd320d18aad18ebc0a8f80fe2
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 "mozilla/IntegerPrintfMacros.h"
12 #include <algorithm>
14 namespace mozilla::net {
16 NS_IMPL_ADDREF(CacheFileInputStream)
17 NS_IMETHODIMP_(MozExternalRefCountType)
18 CacheFileInputStream::Release() {
19 MOZ_ASSERT(0 != mRefCnt, "dup release");
20 nsrefcnt count = --mRefCnt;
21 NS_LOG_RELEASE(this, count, "CacheFileInputStream");
23 if (0 == count) {
24 mRefCnt = 1;
25 delete (this);
26 return 0;
29 if (count == 1) {
30 CacheFileAutoLock lock(mFile);
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(nsITellableStream)
42 NS_INTERFACE_MAP_ENTRY(mozilla::net::CacheFileChunkListener)
43 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInputStream)
44 NS_INTERFACE_MAP_END
46 CacheFileInputStream::CacheFileInputStream(CacheFile* aFile,
47 nsISupports* aEntry,
48 bool aAlternativeData)
49 : mFile(aFile),
50 mPos(0),
51 mStatus(NS_OK),
52 mClosed(false),
53 mInReadSegments(false),
54 mWaitingForUpdate(false),
55 mAlternativeData(aAlternativeData),
56 mListeningForChunk(-1),
57 mCallbackFlags(0),
58 mCacheEntryHandle(aEntry) {
59 LOG(("CacheFileInputStream::CacheFileInputStream() [this=%p]", this));
61 if (mAlternativeData) {
62 mPos = mFile->mAltDataOffset;
66 CacheFileInputStream::~CacheFileInputStream() {
67 LOG(("CacheFileInputStream::~CacheFileInputStream() [this=%p]", this));
68 MOZ_ASSERT(!mInReadSegments);
71 // nsIInputStream
72 NS_IMETHODIMP
73 CacheFileInputStream::Close() {
74 LOG(("CacheFileInputStream::Close() [this=%p]", this));
75 return CloseWithStatus(NS_OK);
78 NS_IMETHODIMP
79 CacheFileInputStream::Available(uint64_t* _retval) {
80 CacheFileAutoLock lock(mFile);
82 if (mClosed) {
83 LOG(
84 ("CacheFileInputStream::Available() - Stream is closed. [this=%p, "
85 "status=0x%08" PRIx32 "]",
86 this, static_cast<uint32_t>(mStatus)));
87 return NS_FAILED(mStatus) ? mStatus : NS_BASE_STREAM_CLOSED;
90 EnsureCorrectChunk(false);
91 if (NS_FAILED(mStatus)) {
92 LOG(
93 ("CacheFileInputStream::Available() - EnsureCorrectChunk failed. "
94 "[this=%p, status=0x%08" PRIx32 "]",
95 this, static_cast<uint32_t>(mStatus)));
96 return mStatus;
99 nsresult rv = NS_OK;
100 *_retval = 0;
102 if (mChunk) {
103 int64_t canRead = mFile->BytesFromChunk(mChunk->Index(), mAlternativeData);
104 canRead -= (mPos % kChunkSize);
106 if (canRead > 0) {
107 *_retval = canRead;
108 } else if (canRead == 0 && !mFile->OutputStreamExists(mAlternativeData)) {
109 rv = NS_BASE_STREAM_CLOSED;
113 LOG(("CacheFileInputStream::Available() [this=%p, retval=%" PRIu64
114 ", rv=0x%08" PRIx32 "]",
115 this, *_retval, static_cast<uint32_t>(rv)));
117 return rv;
120 NS_IMETHODIMP
121 CacheFileInputStream::StreamStatus() {
122 CacheFileAutoLock lock(mFile);
124 if (mClosed) {
125 LOG(
126 ("CacheFileInputStream::StreamStatus() - Stream is closed. [this=%p, "
127 "status=0x%08" PRIx32 "]",
128 this, static_cast<uint32_t>(mStatus)));
129 return NS_FAILED(mStatus) ? mStatus : NS_BASE_STREAM_CLOSED;
132 return NS_OK;
135 NS_IMETHODIMP
136 CacheFileInputStream::Read(char* aBuf, uint32_t aCount, uint32_t* _retval) {
137 LOG(("CacheFileInputStream::Read() [this=%p, count=%d]", this, aCount));
138 return ReadSegments(NS_CopySegmentToBuffer, aBuf, aCount, _retval);
141 NS_IMETHODIMP
142 CacheFileInputStream::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
143 uint32_t aCount, uint32_t* _retval) {
144 CacheFileAutoLock lock(mFile);
146 LOG(("CacheFileInputStream::ReadSegments() [this=%p, count=%d]", this,
147 aCount));
149 nsresult rv = NS_OK;
151 *_retval = 0;
153 if (mInReadSegments) {
154 LOG(
155 ("CacheFileInputStream::ReadSegments() - Cannot be called while the "
156 "stream is in ReadSegments!"));
157 return NS_ERROR_UNEXPECTED;
160 if (mClosed) {
161 LOG(
162 ("CacheFileInputStream::ReadSegments() - Stream is closed. [this=%p, "
163 "status=0x%08" PRIx32 "]",
164 this, static_cast<uint32_t>(mStatus)));
166 if (NS_FAILED(mStatus)) {
167 return mStatus;
170 return NS_OK;
173 if (aCount == 0) {
174 return NS_OK;
177 EnsureCorrectChunk(false);
179 while (true) {
180 if (NS_FAILED(mStatus)) return mStatus;
182 if (!mChunk) {
183 if (mListeningForChunk == -1) {
184 return NS_OK;
186 return NS_BASE_STREAM_WOULD_BLOCK;
189 CacheFileChunkReadHandle hnd = mChunk->GetReadHandle();
190 int64_t canRead = CanRead(&hnd);
191 if (NS_FAILED(mStatus)) {
192 return mStatus;
195 if (canRead < 0) {
196 // file was truncated ???
197 MOZ_ASSERT(false, "SetEOF is currenty not implemented?!");
198 rv = NS_OK;
199 } else if (canRead > 0) {
200 uint32_t toRead = std::min(static_cast<uint32_t>(canRead), aCount);
201 uint32_t read;
202 const char* buf = hnd.Buf() + (mPos - hnd.Offset());
204 mInReadSegments = true;
205 lock.Unlock();
207 rv = aWriter(this, aClosure, buf, *_retval, toRead, &read);
209 lock.Lock();
210 mInReadSegments = false;
212 if (NS_SUCCEEDED(rv)) {
213 MOZ_ASSERT(read <= toRead,
214 "writer should not write more than we asked it to write");
216 *_retval += read;
217 mPos += read;
218 aCount -= read;
220 if (!mClosed) {
221 // The last chunk is released after the caller closes this stream.
222 EnsureCorrectChunk(false);
224 if (mChunk && aCount) {
225 // Check whether there is more data available to read.
226 continue;
231 if (mClosed) {
232 // The stream was closed from aWriter, do the cleanup.
233 CleanUp();
236 rv = NS_OK;
237 } else {
238 if (*_retval == 0 && mFile->OutputStreamExists(mAlternativeData)) {
239 rv = NS_BASE_STREAM_WOULD_BLOCK;
240 } else {
241 rv = NS_OK;
245 break;
248 LOG(("CacheFileInputStream::ReadSegments() [this=%p, rv=0x%08" PRIx32
249 ", retval=%d]",
250 this, static_cast<uint32_t>(rv), *_retval));
252 return rv;
255 NS_IMETHODIMP
256 CacheFileInputStream::IsNonBlocking(bool* _retval) {
257 *_retval = true;
258 return NS_OK;
261 // nsIAsyncInputStream
262 NS_IMETHODIMP
263 CacheFileInputStream::CloseWithStatus(nsresult aStatus) {
264 CacheFileAutoLock lock(mFile);
266 LOG(("CacheFileInputStream::CloseWithStatus() [this=%p, aStatus=0x%08" PRIx32
267 "]",
268 this, static_cast<uint32_t>(aStatus)));
270 CloseWithStatusLocked(aStatus);
271 return NS_OK;
274 void CacheFileInputStream::CloseWithStatusLocked(nsresult aStatus) {
275 LOG(
276 ("CacheFileInputStream::CloseWithStatusLocked() [this=%p, "
277 "aStatus=0x%08" PRIx32 "]",
278 this, static_cast<uint32_t>(aStatus)));
280 if (mClosed) {
281 // We notify listener and null out mCallback immediately after closing
282 // the stream. If we're in ReadSegments we postpone notification until we
283 // step out from ReadSegments. So if the stream is already closed the
284 // following assertion must be true.
285 MOZ_ASSERT(!mCallback || mInReadSegments);
286 return;
289 mClosed = true;
290 mStatus = NS_FAILED(aStatus) ? aStatus : NS_BASE_STREAM_CLOSED;
292 if (!mInReadSegments) {
293 CleanUp();
297 void CacheFileInputStream::CleanUp() {
298 MOZ_ASSERT(!mInReadSegments);
299 MOZ_ASSERT(mClosed);
301 if (mChunk) {
302 ReleaseChunk();
305 // TODO propagate error from input stream to other streams ???
307 MaybeNotifyListener();
309 mFile->ReleaseOutsideLock(std::move(mCacheEntryHandle));
312 NS_IMETHODIMP
313 CacheFileInputStream::AsyncWait(nsIInputStreamCallback* aCallback,
314 uint32_t aFlags, uint32_t aRequestedCount,
315 nsIEventTarget* aEventTarget) {
316 CacheFileAutoLock lock(mFile);
318 LOG(
319 ("CacheFileInputStream::AsyncWait() [this=%p, callback=%p, flags=%d, "
320 "requestedCount=%d, eventTarget=%p]",
321 this, aCallback, aFlags, aRequestedCount, aEventTarget));
323 if (mInReadSegments) {
324 LOG(
325 ("CacheFileInputStream::AsyncWait() - Cannot be called while the stream"
326 " is in ReadSegments!"));
327 MOZ_ASSERT(false,
328 "Unexpected call. If it's a valid usage implement it. "
329 "Otherwise fix the caller.");
330 return NS_ERROR_UNEXPECTED;
333 mCallback = aCallback;
334 mCallbackFlags = aFlags;
335 mCallbackTarget = aEventTarget;
337 if (!mCallback) {
338 if (mWaitingForUpdate) {
339 mChunk->CancelWait(this);
340 mWaitingForUpdate = false;
342 return NS_OK;
345 if (mClosed) {
346 NotifyListener();
347 return NS_OK;
350 EnsureCorrectChunk(false);
352 MaybeNotifyListener();
354 return NS_OK;
357 // nsISeekableStream
358 NS_IMETHODIMP
359 CacheFileInputStream::Seek(int32_t whence, int64_t offset) {
360 CacheFileAutoLock lock(mFile);
361 mFile->AssertOwnsLock(); // For thread-safety analysis
363 LOG(("CacheFileInputStream::Seek() [this=%p, whence=%d, offset=%" PRId64 "]",
364 this, whence, offset));
366 if (mInReadSegments) {
367 LOG(
368 ("CacheFileInputStream::Seek() - Cannot be called while the stream is "
369 "in ReadSegments!"));
370 return NS_ERROR_UNEXPECTED;
373 if (mClosed) {
374 LOG(("CacheFileInputStream::Seek() - Stream is closed. [this=%p]", this));
375 return NS_BASE_STREAM_CLOSED;
378 int64_t newPos = offset;
379 switch (whence) {
380 case NS_SEEK_SET:
381 if (mAlternativeData) {
382 newPos += mFile->mAltDataOffset;
384 break;
385 case NS_SEEK_CUR:
386 newPos += mPos;
387 break;
388 case NS_SEEK_END:
389 if (mAlternativeData) {
390 newPos += mFile->mDataSize;
391 } else {
392 newPos += mFile->mAltDataOffset;
394 break;
395 default:
396 NS_ERROR("invalid whence");
397 return NS_ERROR_INVALID_ARG;
399 mPos = newPos;
400 EnsureCorrectChunk(false);
402 LOG(("CacheFileInputStream::Seek() [this=%p, pos=%" PRId64 "]", this, mPos));
403 return NS_OK;
406 NS_IMETHODIMP
407 CacheFileInputStream::SetEOF() {
408 MOZ_ASSERT(false, "Don't call SetEOF on cache input stream");
409 return NS_ERROR_NOT_IMPLEMENTED;
412 // nsITellableStream
413 NS_IMETHODIMP
414 CacheFileInputStream::Tell(int64_t* _retval) {
415 CacheFileAutoLock lock(mFile);
416 mFile->AssertOwnsLock(); // For thread-safety analysis
418 if (mClosed) {
419 LOG(("CacheFileInputStream::Tell() - Stream is closed. [this=%p]", this));
420 return NS_BASE_STREAM_CLOSED;
423 *_retval = mPos;
425 if (mAlternativeData) {
426 *_retval -= mFile->mAltDataOffset;
429 LOG(("CacheFileInputStream::Tell() [this=%p, retval=%" PRId64 "]", this,
430 *_retval));
431 return NS_OK;
434 // CacheFileChunkListener
435 nsresult CacheFileInputStream::OnChunkRead(nsresult aResult,
436 CacheFileChunk* aChunk) {
437 MOZ_CRASH("CacheFileInputStream::OnChunkRead should not be called!");
438 return NS_ERROR_UNEXPECTED;
441 nsresult CacheFileInputStream::OnChunkWritten(nsresult aResult,
442 CacheFileChunk* aChunk) {
443 MOZ_CRASH("CacheFileInputStream::OnChunkWritten should not be called!");
444 return NS_ERROR_UNEXPECTED;
447 nsresult CacheFileInputStream::OnChunkAvailable(nsresult aResult,
448 uint32_t aChunkIdx,
449 CacheFileChunk* aChunk) {
450 CacheFileAutoLock lock(mFile);
452 LOG(("CacheFileInputStream::OnChunkAvailable() [this=%p, result=0x%08" PRIx32
453 ", "
454 "idx=%d, chunk=%p]",
455 this, static_cast<uint32_t>(aResult), aChunkIdx, aChunk));
457 MOZ_ASSERT(mListeningForChunk != -1);
459 if (mListeningForChunk != static_cast<int64_t>(aChunkIdx)) {
460 // This is not a chunk that we're waiting for
461 LOG(
462 ("CacheFileInputStream::OnChunkAvailable() - Notification is for a "
463 "different chunk. [this=%p, listeningForChunk=%" PRId64 "]",
464 this, mListeningForChunk));
466 return NS_OK;
469 MOZ_ASSERT(!mChunk);
470 MOZ_ASSERT(!mWaitingForUpdate);
471 MOZ_ASSERT(!mInReadSegments);
472 mListeningForChunk = -1;
474 if (mClosed) {
475 MOZ_ASSERT(!mCallback);
477 LOG(
478 ("CacheFileInputStream::OnChunkAvailable() - Stream is closed, "
479 "ignoring notification. [this=%p]",
480 this));
482 return NS_OK;
485 if (NS_SUCCEEDED(aResult)) {
486 mChunk = aChunk;
487 } else if (aResult != NS_ERROR_NOT_AVAILABLE) {
488 // Close the stream with error. The consumer will receive this error later
489 // in Read(), Available() etc. We need to handle NS_ERROR_NOT_AVAILABLE
490 // differently since it is returned when the requested chunk is not
491 // available and there is no writer that could create it, i.e. it means that
492 // we've reached the end of the file.
493 CloseWithStatusLocked(aResult);
495 return NS_OK;
498 MaybeNotifyListener();
500 return NS_OK;
503 nsresult CacheFileInputStream::OnChunkUpdated(CacheFileChunk* aChunk) {
504 CacheFileAutoLock lock(mFile);
506 LOG(("CacheFileInputStream::OnChunkUpdated() [this=%p, idx=%d]", this,
507 aChunk->Index()));
509 if (!mWaitingForUpdate) {
510 LOG(
511 ("CacheFileInputStream::OnChunkUpdated() - Ignoring notification since "
512 "mWaitingforUpdate == false. [this=%p]",
513 this));
515 return NS_OK;
518 mWaitingForUpdate = false;
520 MOZ_ASSERT(mChunk == aChunk);
522 MaybeNotifyListener();
524 return NS_OK;
527 void CacheFileInputStream::ReleaseChunk() {
528 mFile->AssertOwnsLock();
530 LOG(("CacheFileInputStream::ReleaseChunk() [this=%p, idx=%d]", this,
531 mChunk->Index()));
533 MOZ_ASSERT(!mInReadSegments);
535 if (mWaitingForUpdate) {
536 LOG(
537 ("CacheFileInputStream::ReleaseChunk() - Canceling waiting for update. "
538 "[this=%p]",
539 this));
541 mChunk->CancelWait(this);
542 mWaitingForUpdate = false;
545 mFile->ReleaseOutsideLock(std::move(mChunk));
548 void CacheFileInputStream::EnsureCorrectChunk(bool aReleaseOnly) {
549 mFile->AssertOwnsLock();
551 LOG(("CacheFileInputStream::EnsureCorrectChunk() [this=%p, releaseOnly=%d]",
552 this, aReleaseOnly));
554 nsresult rv;
556 uint32_t chunkIdx = mPos / kChunkSize;
558 if (mInReadSegments) {
559 // We must have correct chunk
560 MOZ_ASSERT(mChunk);
561 MOZ_ASSERT(mChunk->Index() == chunkIdx);
562 return;
565 if (mChunk) {
566 if (mChunk->Index() == chunkIdx) {
567 // we have a correct chunk
568 LOG(
569 ("CacheFileInputStream::EnsureCorrectChunk() - Have correct chunk "
570 "[this=%p, idx=%d]",
571 this, chunkIdx));
573 return;
575 ReleaseChunk();
578 MOZ_ASSERT(!mWaitingForUpdate);
580 if (aReleaseOnly) return;
582 if (mListeningForChunk == static_cast<int64_t>(chunkIdx)) {
583 // We're already waiting for this chunk
584 LOG(
585 ("CacheFileInputStream::EnsureCorrectChunk() - Already listening for "
586 "chunk %" PRId64 " [this=%p]",
587 mListeningForChunk, this));
589 return;
592 rv = mFile->GetChunkLocked(chunkIdx, CacheFile::READER, this,
593 getter_AddRefs(mChunk));
594 if (NS_FAILED(rv)) {
595 LOG(
596 ("CacheFileInputStream::EnsureCorrectChunk() - GetChunkLocked failed. "
597 "[this=%p, idx=%d, rv=0x%08" PRIx32 "]",
598 this, chunkIdx, static_cast<uint32_t>(rv)));
599 if (rv != NS_ERROR_NOT_AVAILABLE) {
600 // Close the stream with error. The consumer will receive this error later
601 // in Read(), Available() etc. We need to handle NS_ERROR_NOT_AVAILABLE
602 // differently since it is returned when the requested chunk is not
603 // available and there is no writer that could create it, i.e. it means
604 // that we've reached the end of the file.
605 CloseWithStatusLocked(rv);
607 return;
609 } else if (!mChunk) {
610 mListeningForChunk = static_cast<int64_t>(chunkIdx);
613 MaybeNotifyListener();
616 int64_t CacheFileInputStream::CanRead(CacheFileChunkReadHandle* aHandle) {
617 mFile->AssertOwnsLock();
619 MOZ_ASSERT(mChunk);
620 MOZ_ASSERT(mPos / kChunkSize == mChunk->Index());
622 int64_t retval = aHandle->Offset() + aHandle->DataSize();
624 if (!mAlternativeData && mFile->mAltDataOffset != -1 &&
625 mFile->mAltDataOffset < retval) {
626 retval = mFile->mAltDataOffset;
629 retval -= mPos;
630 if (retval <= 0 && NS_FAILED(mChunk->GetStatus())) {
631 CloseWithStatusLocked(mChunk->GetStatus());
634 LOG(("CacheFileInputStream::CanRead() [this=%p, canRead=%" PRId64 "]", this,
635 retval));
637 return retval;
640 void CacheFileInputStream::NotifyListener() {
641 mFile->AssertOwnsLock();
643 LOG(("CacheFileInputStream::NotifyListener() [this=%p]", this));
645 MOZ_ASSERT(mCallback);
646 MOZ_ASSERT(!mInReadSegments);
648 if (!mCallbackTarget) {
649 mCallbackTarget = CacheFileIOManager::IOTarget();
650 if (!mCallbackTarget) {
651 LOG(
652 ("CacheFileInputStream::NotifyListener() - Cannot get Cache I/O "
653 "thread! Using main thread for callback."));
654 mCallbackTarget = GetMainThreadSerialEventTarget();
658 nsCOMPtr<nsIInputStreamCallback> asyncCallback = NS_NewInputStreamReadyEvent(
659 "CacheFileInputStream::NotifyListener", mCallback, mCallbackTarget);
661 mCallback = nullptr;
662 mCallbackTarget = nullptr;
664 asyncCallback->OnInputStreamReady(this);
667 void CacheFileInputStream::MaybeNotifyListener() {
668 mFile->AssertOwnsLock();
670 LOG(
671 ("CacheFileInputStream::MaybeNotifyListener() [this=%p, mCallback=%p, "
672 "mClosed=%d, mStatus=0x%08" PRIx32
673 ", mChunk=%p, mListeningForChunk=%" PRId64 ", "
674 "mWaitingForUpdate=%d]",
675 this, mCallback.get(), mClosed, static_cast<uint32_t>(mStatus),
676 mChunk.get(), mListeningForChunk, mWaitingForUpdate));
678 MOZ_ASSERT(!mInReadSegments);
680 if (!mCallback) return;
682 if (mClosed || NS_FAILED(mStatus)) {
683 NotifyListener();
684 return;
687 if (!mChunk) {
688 if (mListeningForChunk == -1) {
689 // EOF, should we notify even if mCallbackFlags == WAIT_CLOSURE_ONLY ??
690 NotifyListener();
692 return;
695 MOZ_ASSERT(mPos / kChunkSize == mChunk->Index());
697 if (mWaitingForUpdate) return;
699 CacheFileChunkReadHandle hnd = mChunk->GetReadHandle();
700 int64_t canRead = CanRead(&hnd);
701 if (NS_FAILED(mStatus)) {
702 // CanRead() called CloseWithStatusLocked() which called
703 // MaybeNotifyListener() so the listener was already notified. Stop here.
704 MOZ_ASSERT(!mCallback);
705 return;
708 if (canRead > 0) {
709 if (!(mCallbackFlags & WAIT_CLOSURE_ONLY)) NotifyListener();
710 } else if (canRead == 0) {
711 if (!mFile->OutputStreamExists(mAlternativeData)) {
712 // EOF
713 NotifyListener();
714 } else {
715 mChunk->WaitForUpdate(this);
716 mWaitingForUpdate = true;
718 } else {
719 // Output have set EOF before mPos?
720 MOZ_ASSERT(false, "SetEOF is currenty not implemented?!");
721 NotifyListener();
725 // Memory reporting
727 size_t CacheFileInputStream::SizeOfIncludingThis(
728 mozilla::MallocSizeOf mallocSizeOf) const {
729 // Everything the stream keeps a reference to is already reported somewhere
730 // else. mFile reports itself. mChunk reported as part of CacheFile. mCallback
731 // is usually CacheFile or a class that is reported elsewhere.
732 return mallocSizeOf(this);
735 } // namespace mozilla::net