Bug 1861709 replace AudioCallbackDriver::ThreadRunning() assertions that mean to...
[gecko.git] / netwerk / cache2 / CacheFileInputStream.cpp
blob99302baf546fbc4ae4e300d4d5948ddb651f5691
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::net {
15 NS_IMPL_ADDREF(CacheFileInputStream)
16 NS_IMETHODIMP_(MozExternalRefCountType)
17 CacheFileInputStream::Release() {
18 MOZ_ASSERT(0 != mRefCnt, "dup release");
19 nsrefcnt count = --mRefCnt;
20 NS_LOG_RELEASE(this, count, "CacheFileInputStream");
22 if (0 == count) {
23 mRefCnt = 1;
24 delete (this);
25 return 0;
28 if (count == 1) {
29 CacheFileAutoLock lock(mFile);
30 mFile->RemoveInput(this, mStatus);
33 return count;
36 NS_INTERFACE_MAP_BEGIN(CacheFileInputStream)
37 NS_INTERFACE_MAP_ENTRY(nsIInputStream)
38 NS_INTERFACE_MAP_ENTRY(nsIAsyncInputStream)
39 NS_INTERFACE_MAP_ENTRY(nsISeekableStream)
40 NS_INTERFACE_MAP_ENTRY(nsITellableStream)
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) {
58 LOG(("CacheFileInputStream::CacheFileInputStream() [this=%p]", this));
60 if (mAlternativeData) {
61 mPos = mFile->mAltDataOffset;
65 CacheFileInputStream::~CacheFileInputStream() {
66 LOG(("CacheFileInputStream::~CacheFileInputStream() [this=%p]", this));
67 MOZ_ASSERT(!mInReadSegments);
70 // nsIInputStream
71 NS_IMETHODIMP
72 CacheFileInputStream::Close() {
73 LOG(("CacheFileInputStream::Close() [this=%p]", this));
74 return CloseWithStatus(NS_OK);
77 NS_IMETHODIMP
78 CacheFileInputStream::Available(uint64_t* _retval) {
79 CacheFileAutoLock lock(mFile);
81 if (mClosed) {
82 LOG(
83 ("CacheFileInputStream::Available() - Stream is closed. [this=%p, "
84 "status=0x%08" PRIx32 "]",
85 this, static_cast<uint32_t>(mStatus)));
86 return NS_FAILED(mStatus) ? mStatus : NS_BASE_STREAM_CLOSED;
89 EnsureCorrectChunk(false);
90 if (NS_FAILED(mStatus)) {
91 LOG(
92 ("CacheFileInputStream::Available() - EnsureCorrectChunk failed. "
93 "[this=%p, status=0x%08" PRIx32 "]",
94 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
113 ", rv=0x%08" PRIx32 "]",
114 this, *_retval, static_cast<uint32_t>(rv)));
116 return rv;
119 NS_IMETHODIMP
120 CacheFileInputStream::StreamStatus() {
121 CacheFileAutoLock lock(mFile);
123 if (mClosed) {
124 LOG(
125 ("CacheFileInputStream::StreamStatus() - Stream is closed. [this=%p, "
126 "status=0x%08" PRIx32 "]",
127 this, static_cast<uint32_t>(mStatus)));
128 return NS_FAILED(mStatus) ? mStatus : NS_BASE_STREAM_CLOSED;
131 return NS_OK;
134 NS_IMETHODIMP
135 CacheFileInputStream::Read(char* aBuf, uint32_t aCount, uint32_t* _retval) {
136 LOG(("CacheFileInputStream::Read() [this=%p, count=%d]", this, aCount));
137 return ReadSegments(NS_CopySegmentToBuffer, aBuf, aCount, _retval);
140 NS_IMETHODIMP
141 CacheFileInputStream::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
142 uint32_t aCount, uint32_t* _retval) {
143 CacheFileAutoLock lock(mFile);
145 LOG(("CacheFileInputStream::ReadSegments() [this=%p, count=%d]", this,
146 aCount));
148 nsresult rv = NS_OK;
150 *_retval = 0;
152 if (mInReadSegments) {
153 LOG(
154 ("CacheFileInputStream::ReadSegments() - Cannot be called while the "
155 "stream is in ReadSegments!"));
156 return NS_ERROR_UNEXPECTED;
159 if (mClosed) {
160 LOG(
161 ("CacheFileInputStream::ReadSegments() - Stream is closed. [this=%p, "
162 "status=0x%08" PRIx32 "]",
163 this, static_cast<uint32_t>(mStatus)));
165 if (NS_FAILED(mStatus)) {
166 return mStatus;
169 return NS_OK;
172 if (aCount == 0) {
173 return NS_OK;
176 EnsureCorrectChunk(false);
178 while (true) {
179 if (NS_FAILED(mStatus)) return mStatus;
181 if (!mChunk) {
182 if (mListeningForChunk == -1) {
183 return NS_OK;
185 return NS_BASE_STREAM_WOULD_BLOCK;
188 CacheFileChunkReadHandle hnd = mChunk->GetReadHandle();
189 int64_t canRead = CanRead(&hnd);
190 if (NS_FAILED(mStatus)) {
191 return mStatus;
194 if (canRead < 0) {
195 // file was truncated ???
196 MOZ_ASSERT(false, "SetEOF is currenty not implemented?!");
197 rv = NS_OK;
198 } else if (canRead > 0) {
199 uint32_t toRead = std::min(static_cast<uint32_t>(canRead), aCount);
200 uint32_t read;
201 const char* buf = hnd.Buf() + (mPos - hnd.Offset());
203 mInReadSegments = true;
204 lock.Unlock();
206 rv = aWriter(this, aClosure, buf, *_retval, toRead, &read);
208 lock.Lock();
209 mInReadSegments = false;
211 if (NS_SUCCEEDED(rv)) {
212 MOZ_ASSERT(read <= toRead,
213 "writer should not write more than we asked it to write");
215 *_retval += read;
216 mPos += read;
217 aCount -= read;
219 if (!mClosed) {
220 // The last chunk is released after the caller closes this stream.
221 EnsureCorrectChunk(false);
223 if (mChunk && aCount) {
224 // Check whether there is more data available to read.
225 continue;
230 if (mClosed) {
231 // The stream was closed from aWriter, do the cleanup.
232 CleanUp();
235 rv = NS_OK;
236 } else {
237 if (*_retval == 0 && mFile->OutputStreamExists(mAlternativeData)) {
238 rv = NS_BASE_STREAM_WOULD_BLOCK;
239 } else {
240 rv = NS_OK;
244 break;
247 LOG(("CacheFileInputStream::ReadSegments() [this=%p, rv=0x%08" PRIx32
248 ", retval=%d]",
249 this, static_cast<uint32_t>(rv), *_retval));
251 return rv;
254 NS_IMETHODIMP
255 CacheFileInputStream::IsNonBlocking(bool* _retval) {
256 *_retval = true;
257 return NS_OK;
260 // nsIAsyncInputStream
261 NS_IMETHODIMP
262 CacheFileInputStream::CloseWithStatus(nsresult aStatus) {
263 CacheFileAutoLock lock(mFile);
265 LOG(("CacheFileInputStream::CloseWithStatus() [this=%p, aStatus=0x%08" PRIx32
266 "]",
267 this, static_cast<uint32_t>(aStatus)));
269 CloseWithStatusLocked(aStatus);
270 return NS_OK;
273 void CacheFileInputStream::CloseWithStatusLocked(nsresult aStatus) {
274 LOG(
275 ("CacheFileInputStream::CloseWithStatusLocked() [this=%p, "
276 "aStatus=0x%08" PRIx32 "]",
277 this, static_cast<uint32_t>(aStatus)));
279 if (mClosed) {
280 // We notify listener and null out mCallback immediately after closing
281 // the stream. If we're in ReadSegments we postpone notification until we
282 // step out from ReadSegments. So if the stream is already closed the
283 // following assertion must be true.
284 MOZ_ASSERT(!mCallback || mInReadSegments);
285 return;
288 mClosed = true;
289 mStatus = NS_FAILED(aStatus) ? aStatus : NS_BASE_STREAM_CLOSED;
291 if (!mInReadSegments) {
292 CleanUp();
296 void CacheFileInputStream::CleanUp() {
297 MOZ_ASSERT(!mInReadSegments);
298 MOZ_ASSERT(mClosed);
300 if (mChunk) {
301 ReleaseChunk();
304 // TODO propagate error from input stream to other streams ???
306 MaybeNotifyListener();
308 mFile->ReleaseOutsideLock(std::move(mCacheEntryHandle));
311 NS_IMETHODIMP
312 CacheFileInputStream::AsyncWait(nsIInputStreamCallback* aCallback,
313 uint32_t aFlags, uint32_t aRequestedCount,
314 nsIEventTarget* aEventTarget) {
315 CacheFileAutoLock lock(mFile);
317 LOG(
318 ("CacheFileInputStream::AsyncWait() [this=%p, callback=%p, flags=%d, "
319 "requestedCount=%d, eventTarget=%p]",
320 this, aCallback, aFlags, aRequestedCount, aEventTarget));
322 if (mInReadSegments) {
323 LOG(
324 ("CacheFileInputStream::AsyncWait() - Cannot be called while the stream"
325 " is in ReadSegments!"));
326 MOZ_ASSERT(false,
327 "Unexpected call. If it's a valid usage implement it. "
328 "Otherwise fix the caller.");
329 return NS_ERROR_UNEXPECTED;
332 mCallback = aCallback;
333 mCallbackFlags = aFlags;
334 mCallbackTarget = aEventTarget;
336 if (!mCallback) {
337 if (mWaitingForUpdate) {
338 mChunk->CancelWait(this);
339 mWaitingForUpdate = false;
341 return NS_OK;
344 if (mClosed) {
345 NotifyListener();
346 return NS_OK;
349 EnsureCorrectChunk(false);
351 MaybeNotifyListener();
353 return NS_OK;
356 // nsISeekableStream
357 NS_IMETHODIMP
358 CacheFileInputStream::Seek(int32_t whence, int64_t offset) {
359 CacheFileAutoLock lock(mFile);
360 mFile->AssertOwnsLock(); // For thread-safety analysis
362 LOG(("CacheFileInputStream::Seek() [this=%p, whence=%d, offset=%" PRId64 "]",
363 this, whence, offset));
365 if (mInReadSegments) {
366 LOG(
367 ("CacheFileInputStream::Seek() - Cannot be called while the stream is "
368 "in ReadSegments!"));
369 return NS_ERROR_UNEXPECTED;
372 if (mClosed) {
373 LOG(("CacheFileInputStream::Seek() - Stream is closed. [this=%p]", this));
374 return NS_BASE_STREAM_CLOSED;
377 int64_t newPos = offset;
378 switch (whence) {
379 case NS_SEEK_SET:
380 if (mAlternativeData) {
381 newPos += mFile->mAltDataOffset;
383 break;
384 case NS_SEEK_CUR:
385 newPos += mPos;
386 break;
387 case NS_SEEK_END:
388 if (mAlternativeData) {
389 newPos += mFile->mDataSize;
390 } else {
391 newPos += mFile->mAltDataOffset;
393 break;
394 default:
395 NS_ERROR("invalid whence");
396 return NS_ERROR_INVALID_ARG;
398 mPos = newPos;
399 EnsureCorrectChunk(false);
401 LOG(("CacheFileInputStream::Seek() [this=%p, pos=%" PRId64 "]", this, mPos));
402 return NS_OK;
405 NS_IMETHODIMP
406 CacheFileInputStream::SetEOF() {
407 MOZ_ASSERT(false, "Don't call SetEOF on cache input stream");
408 return NS_ERROR_NOT_IMPLEMENTED;
411 // nsITellableStream
412 NS_IMETHODIMP
413 CacheFileInputStream::Tell(int64_t* _retval) {
414 CacheFileAutoLock lock(mFile);
415 mFile->AssertOwnsLock(); // For thread-safety analysis
417 if (mClosed) {
418 LOG(("CacheFileInputStream::Tell() - Stream is closed. [this=%p]", this));
419 return NS_BASE_STREAM_CLOSED;
422 *_retval = mPos;
424 if (mAlternativeData) {
425 *_retval -= mFile->mAltDataOffset;
428 LOG(("CacheFileInputStream::Tell() [this=%p, retval=%" PRId64 "]", this,
429 *_retval));
430 return NS_OK;
433 // CacheFileChunkListener
434 nsresult CacheFileInputStream::OnChunkRead(nsresult aResult,
435 CacheFileChunk* aChunk) {
436 MOZ_CRASH("CacheFileInputStream::OnChunkRead should not be called!");
437 return NS_ERROR_UNEXPECTED;
440 nsresult CacheFileInputStream::OnChunkWritten(nsresult aResult,
441 CacheFileChunk* aChunk) {
442 MOZ_CRASH("CacheFileInputStream::OnChunkWritten should not be called!");
443 return NS_ERROR_UNEXPECTED;
446 nsresult CacheFileInputStream::OnChunkAvailable(nsresult aResult,
447 uint32_t aChunkIdx,
448 CacheFileChunk* aChunk) {
449 CacheFileAutoLock lock(mFile);
451 LOG(("CacheFileInputStream::OnChunkAvailable() [this=%p, result=0x%08" PRIx32
452 ", "
453 "idx=%d, chunk=%p]",
454 this, static_cast<uint32_t>(aResult), aChunkIdx, aChunk));
456 MOZ_ASSERT(mListeningForChunk != -1);
458 if (mListeningForChunk != static_cast<int64_t>(aChunkIdx)) {
459 // This is not a chunk that we're waiting for
460 LOG(
461 ("CacheFileInputStream::OnChunkAvailable() - Notification is for a "
462 "different chunk. [this=%p, listeningForChunk=%" PRId64 "]",
463 this, mListeningForChunk));
465 return NS_OK;
468 MOZ_ASSERT(!mChunk);
469 MOZ_ASSERT(!mWaitingForUpdate);
470 MOZ_ASSERT(!mInReadSegments);
471 mListeningForChunk = -1;
473 if (mClosed) {
474 MOZ_ASSERT(!mCallback);
476 LOG(
477 ("CacheFileInputStream::OnChunkAvailable() - Stream is closed, "
478 "ignoring notification. [this=%p]",
479 this));
481 return NS_OK;
484 if (NS_SUCCEEDED(aResult)) {
485 mChunk = aChunk;
486 } else if (aResult != NS_ERROR_NOT_AVAILABLE) {
487 // Close the stream with error. The consumer will receive this error later
488 // in Read(), Available() etc. We need to handle NS_ERROR_NOT_AVAILABLE
489 // differently since it is returned when the requested chunk is not
490 // available and there is no writer that could create it, i.e. it means that
491 // we've reached the end of the file.
492 CloseWithStatusLocked(aResult);
494 return NS_OK;
497 MaybeNotifyListener();
499 return NS_OK;
502 nsresult CacheFileInputStream::OnChunkUpdated(CacheFileChunk* aChunk) {
503 CacheFileAutoLock lock(mFile);
505 LOG(("CacheFileInputStream::OnChunkUpdated() [this=%p, idx=%d]", this,
506 aChunk->Index()));
508 if (!mWaitingForUpdate) {
509 LOG(
510 ("CacheFileInputStream::OnChunkUpdated() - Ignoring notification since "
511 "mWaitingforUpdate == false. [this=%p]",
512 this));
514 return NS_OK;
517 mWaitingForUpdate = false;
519 MOZ_ASSERT(mChunk == aChunk);
521 MaybeNotifyListener();
523 return NS_OK;
526 void CacheFileInputStream::ReleaseChunk() {
527 mFile->AssertOwnsLock();
529 LOG(("CacheFileInputStream::ReleaseChunk() [this=%p, idx=%d]", this,
530 mChunk->Index()));
532 MOZ_ASSERT(!mInReadSegments);
534 if (mWaitingForUpdate) {
535 LOG(
536 ("CacheFileInputStream::ReleaseChunk() - Canceling waiting for update. "
537 "[this=%p]",
538 this));
540 mChunk->CancelWait(this);
541 mWaitingForUpdate = false;
544 mFile->ReleaseOutsideLock(std::move(mChunk));
547 void CacheFileInputStream::EnsureCorrectChunk(bool aReleaseOnly) {
548 mFile->AssertOwnsLock();
550 LOG(("CacheFileInputStream::EnsureCorrectChunk() [this=%p, releaseOnly=%d]",
551 this, aReleaseOnly));
553 nsresult rv;
555 uint32_t chunkIdx = mPos / kChunkSize;
557 if (mInReadSegments) {
558 // We must have correct chunk
559 MOZ_ASSERT(mChunk);
560 MOZ_ASSERT(mChunk->Index() == chunkIdx);
561 return;
564 if (mChunk) {
565 if (mChunk->Index() == chunkIdx) {
566 // we have a correct chunk
567 LOG(
568 ("CacheFileInputStream::EnsureCorrectChunk() - Have correct chunk "
569 "[this=%p, idx=%d]",
570 this, chunkIdx));
572 return;
574 ReleaseChunk();
577 MOZ_ASSERT(!mWaitingForUpdate);
579 if (aReleaseOnly) return;
581 if (mListeningForChunk == static_cast<int64_t>(chunkIdx)) {
582 // We're already waiting for this chunk
583 LOG(
584 ("CacheFileInputStream::EnsureCorrectChunk() - Already listening for "
585 "chunk %" PRId64 " [this=%p]",
586 mListeningForChunk, this));
588 return;
591 rv = mFile->GetChunkLocked(chunkIdx, CacheFile::READER, this,
592 getter_AddRefs(mChunk));
593 if (NS_FAILED(rv)) {
594 LOG(
595 ("CacheFileInputStream::EnsureCorrectChunk() - GetChunkLocked failed. "
596 "[this=%p, idx=%d, rv=0x%08" PRIx32 "]",
597 this, chunkIdx, static_cast<uint32_t>(rv)));
598 if (rv != NS_ERROR_NOT_AVAILABLE) {
599 // Close the stream with error. The consumer will receive this error later
600 // in Read(), Available() etc. We need to handle NS_ERROR_NOT_AVAILABLE
601 // differently since it is returned when the requested chunk is not
602 // available and there is no writer that could create it, i.e. it means
603 // that we've reached the end of the file.
604 CloseWithStatusLocked(rv);
606 return;
608 } else if (!mChunk) {
609 mListeningForChunk = static_cast<int64_t>(chunkIdx);
612 MaybeNotifyListener();
615 int64_t CacheFileInputStream::CanRead(CacheFileChunkReadHandle* aHandle) {
616 mFile->AssertOwnsLock();
618 MOZ_ASSERT(mChunk);
619 MOZ_ASSERT(mPos / kChunkSize == mChunk->Index());
621 int64_t retval = aHandle->Offset() + aHandle->DataSize();
623 if (!mAlternativeData && mFile->mAltDataOffset != -1 &&
624 mFile->mAltDataOffset < retval) {
625 retval = mFile->mAltDataOffset;
628 retval -= mPos;
629 if (retval <= 0 && NS_FAILED(mChunk->GetStatus())) {
630 CloseWithStatusLocked(mChunk->GetStatus());
633 LOG(("CacheFileInputStream::CanRead() [this=%p, canRead=%" PRId64 "]", this,
634 retval));
636 return retval;
639 void CacheFileInputStream::NotifyListener() {
640 mFile->AssertOwnsLock();
642 LOG(("CacheFileInputStream::NotifyListener() [this=%p]", this));
644 MOZ_ASSERT(mCallback);
645 MOZ_ASSERT(!mInReadSegments);
647 if (!mCallbackTarget) {
648 mCallbackTarget = CacheFileIOManager::IOTarget();
649 if (!mCallbackTarget) {
650 LOG(
651 ("CacheFileInputStream::NotifyListener() - Cannot get Cache I/O "
652 "thread! Using main thread for callback."));
653 mCallbackTarget = GetMainThreadSerialEventTarget();
657 nsCOMPtr<nsIInputStreamCallback> asyncCallback = NS_NewInputStreamReadyEvent(
658 "CacheFileInputStream::NotifyListener", mCallback, mCallbackTarget);
660 mCallback = nullptr;
661 mCallbackTarget = nullptr;
663 asyncCallback->OnInputStreamReady(this);
666 void CacheFileInputStream::MaybeNotifyListener() {
667 mFile->AssertOwnsLock();
669 LOG(
670 ("CacheFileInputStream::MaybeNotifyListener() [this=%p, mCallback=%p, "
671 "mClosed=%d, mStatus=0x%08" PRIx32
672 ", mChunk=%p, mListeningForChunk=%" PRId64 ", "
673 "mWaitingForUpdate=%d]",
674 this, mCallback.get(), mClosed, static_cast<uint32_t>(mStatus),
675 mChunk.get(), mListeningForChunk, mWaitingForUpdate));
677 MOZ_ASSERT(!mInReadSegments);
679 if (!mCallback) return;
681 if (mClosed || NS_FAILED(mStatus)) {
682 NotifyListener();
683 return;
686 if (!mChunk) {
687 if (mListeningForChunk == -1) {
688 // EOF, should we notify even if mCallbackFlags == WAIT_CLOSURE_ONLY ??
689 NotifyListener();
691 return;
694 MOZ_ASSERT(mPos / kChunkSize == mChunk->Index());
696 if (mWaitingForUpdate) return;
698 CacheFileChunkReadHandle hnd = mChunk->GetReadHandle();
699 int64_t canRead = CanRead(&hnd);
700 if (NS_FAILED(mStatus)) {
701 // CanRead() called CloseWithStatusLocked() which called
702 // MaybeNotifyListener() so the listener was already notified. Stop here.
703 MOZ_ASSERT(!mCallback);
704 return;
707 if (canRead > 0) {
708 if (!(mCallbackFlags & WAIT_CLOSURE_ONLY)) NotifyListener();
709 } else if (canRead == 0) {
710 if (!mFile->OutputStreamExists(mAlternativeData)) {
711 // EOF
712 NotifyListener();
713 } else {
714 mChunk->WaitForUpdate(this);
715 mWaitingForUpdate = true;
717 } else {
718 // Output have set EOF before mPos?
719 MOZ_ASSERT(false, "SetEOF is currenty not implemented?!");
720 NotifyListener();
724 // Memory reporting
726 size_t CacheFileInputStream::SizeOfIncludingThis(
727 mozilla::MallocSizeOf mallocSizeOf) const {
728 // Everything the stream keeps a reference to is already reported somewhere
729 // else. mFile reports itself. mChunk reported as part of CacheFile. mCallback
730 // is usually CacheFile or a class that is reported elsewhere.
731 return mallocSizeOf(this);
734 } // namespace mozilla::net