Bug 1719578 log cubeb speaker enumeration results in MediaEngineWebRTC r=padenot
[gecko.git] / netwerk / cache2 / CacheFileInputStream.cpp
blobc034cb27ed7577207328f2d0e1c7f2f21fbf57d6
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::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) {
128 CacheFileAutoLock lock(mFile);
130 LOG(("CacheFileInputStream::ReadSegments() [this=%p, count=%d]", this,
131 aCount));
133 nsresult rv = NS_OK;
135 *_retval = 0;
137 if (mInReadSegments) {
138 LOG(
139 ("CacheFileInputStream::ReadSegments() - Cannot be called while the "
140 "stream is in ReadSegments!"));
141 return NS_ERROR_UNEXPECTED;
144 if (mClosed) {
145 LOG(
146 ("CacheFileInputStream::ReadSegments() - Stream is closed. [this=%p, "
147 "status=0x%08" PRIx32 "]",
148 this, static_cast<uint32_t>(mStatus)));
150 if (NS_FAILED(mStatus)) {
151 return mStatus;
154 return NS_OK;
157 if (aCount == 0) {
158 return NS_OK;
161 EnsureCorrectChunk(false);
163 while (true) {
164 if (NS_FAILED(mStatus)) return mStatus;
166 if (!mChunk) {
167 if (mListeningForChunk == -1) {
168 return NS_OK;
170 return NS_BASE_STREAM_WOULD_BLOCK;
173 CacheFileChunkReadHandle hnd = mChunk->GetReadHandle();
174 int64_t canRead = CanRead(&hnd);
175 if (NS_FAILED(mStatus)) {
176 return mStatus;
179 if (canRead < 0) {
180 // file was truncated ???
181 MOZ_ASSERT(false, "SetEOF is currenty not implemented?!");
182 rv = NS_OK;
183 } else if (canRead > 0) {
184 uint32_t toRead = std::min(static_cast<uint32_t>(canRead), aCount);
185 uint32_t read;
186 const char* buf = hnd.Buf() + (mPos - hnd.Offset());
188 mInReadSegments = true;
189 lock.Unlock();
191 rv = aWriter(this, aClosure, buf, *_retval, toRead, &read);
193 lock.Lock();
194 mInReadSegments = false;
196 if (NS_SUCCEEDED(rv)) {
197 MOZ_ASSERT(read <= toRead,
198 "writer should not write more than we asked it to write");
200 *_retval += read;
201 mPos += read;
202 aCount -= read;
204 if (!mClosed) {
205 // The last chunk is released after the caller closes this stream.
206 EnsureCorrectChunk(false);
208 if (mChunk && aCount) {
209 // Check whether there is more data available to read.
210 continue;
215 if (mClosed) {
216 // The stream was closed from aWriter, do the cleanup.
217 CleanUp();
220 rv = NS_OK;
221 } else {
222 if (*_retval == 0 && mFile->OutputStreamExists(mAlternativeData)) {
223 rv = NS_BASE_STREAM_WOULD_BLOCK;
224 } else {
225 rv = NS_OK;
229 break;
232 LOG(("CacheFileInputStream::ReadSegments() [this=%p, rv=0x%08" PRIx32
233 ", retval=%d]",
234 this, static_cast<uint32_t>(rv), *_retval));
236 return rv;
239 NS_IMETHODIMP
240 CacheFileInputStream::IsNonBlocking(bool* _retval) {
241 *_retval = true;
242 return NS_OK;
245 // nsIAsyncInputStream
246 NS_IMETHODIMP
247 CacheFileInputStream::CloseWithStatus(nsresult aStatus) {
248 CacheFileAutoLock lock(mFile);
250 LOG(("CacheFileInputStream::CloseWithStatus() [this=%p, aStatus=0x%08" PRIx32
251 "]",
252 this, static_cast<uint32_t>(aStatus)));
254 CloseWithStatusLocked(aStatus);
255 return NS_OK;
258 void CacheFileInputStream::CloseWithStatusLocked(nsresult aStatus) {
259 LOG(
260 ("CacheFileInputStream::CloseWithStatusLocked() [this=%p, "
261 "aStatus=0x%08" PRIx32 "]",
262 this, static_cast<uint32_t>(aStatus)));
264 if (mClosed) {
265 // We notify listener and null out mCallback immediately after closing
266 // the stream. If we're in ReadSegments we postpone notification until we
267 // step out from ReadSegments. So if the stream is already closed the
268 // following assertion must be true.
269 MOZ_ASSERT(!mCallback || mInReadSegments);
270 return;
273 mClosed = true;
274 mStatus = NS_FAILED(aStatus) ? aStatus : NS_BASE_STREAM_CLOSED;
276 if (!mInReadSegments) {
277 CleanUp();
281 void CacheFileInputStream::CleanUp() {
282 MOZ_ASSERT(!mInReadSegments);
283 MOZ_ASSERT(mClosed);
285 if (mChunk) {
286 ReleaseChunk();
289 // TODO propagate error from input stream to other streams ???
291 MaybeNotifyListener();
293 mFile->ReleaseOutsideLock(std::move(mCacheEntryHandle));
296 NS_IMETHODIMP
297 CacheFileInputStream::AsyncWait(nsIInputStreamCallback* aCallback,
298 uint32_t aFlags, uint32_t aRequestedCount,
299 nsIEventTarget* aEventTarget) {
300 CacheFileAutoLock lock(mFile);
302 LOG(
303 ("CacheFileInputStream::AsyncWait() [this=%p, callback=%p, flags=%d, "
304 "requestedCount=%d, eventTarget=%p]",
305 this, aCallback, aFlags, aRequestedCount, aEventTarget));
307 if (mInReadSegments) {
308 LOG(
309 ("CacheFileInputStream::AsyncWait() - Cannot be called while the stream"
310 " is in ReadSegments!"));
311 MOZ_ASSERT(false,
312 "Unexpected call. If it's a valid usage implement it. "
313 "Otherwise fix the caller.");
314 return NS_ERROR_UNEXPECTED;
317 mCallback = aCallback;
318 mCallbackFlags = aFlags;
319 mCallbackTarget = aEventTarget;
321 if (!mCallback) {
322 if (mWaitingForUpdate) {
323 mChunk->CancelWait(this);
324 mWaitingForUpdate = false;
326 return NS_OK;
329 if (mClosed) {
330 NotifyListener();
331 return NS_OK;
334 EnsureCorrectChunk(false);
336 MaybeNotifyListener();
338 return NS_OK;
341 // nsISeekableStream
342 NS_IMETHODIMP
343 CacheFileInputStream::Seek(int32_t whence, int64_t offset) {
344 CacheFileAutoLock lock(mFile);
346 LOG(("CacheFileInputStream::Seek() [this=%p, whence=%d, offset=%" PRId64 "]",
347 this, whence, offset));
349 if (mInReadSegments) {
350 LOG(
351 ("CacheFileInputStream::Seek() - Cannot be called while the stream is "
352 "in ReadSegments!"));
353 return NS_ERROR_UNEXPECTED;
356 if (mClosed) {
357 LOG(("CacheFileInputStream::Seek() - Stream is closed. [this=%p]", this));
358 return NS_BASE_STREAM_CLOSED;
361 int64_t newPos = offset;
362 switch (whence) {
363 case NS_SEEK_SET:
364 if (mAlternativeData) {
365 newPos += mFile->mAltDataOffset;
367 break;
368 case NS_SEEK_CUR:
369 newPos += mPos;
370 break;
371 case NS_SEEK_END:
372 if (mAlternativeData) {
373 newPos += mFile->mDataSize;
374 } else {
375 newPos += mFile->mAltDataOffset;
377 break;
378 default:
379 NS_ERROR("invalid whence");
380 return NS_ERROR_INVALID_ARG;
382 mPos = newPos;
383 EnsureCorrectChunk(false);
385 LOG(("CacheFileInputStream::Seek() [this=%p, pos=%" PRId64 "]", this, mPos));
386 return NS_OK;
389 NS_IMETHODIMP
390 CacheFileInputStream::SetEOF() {
391 MOZ_ASSERT(false, "Don't call SetEOF on cache input stream");
392 return NS_ERROR_NOT_IMPLEMENTED;
395 // nsITellableStream
396 NS_IMETHODIMP
397 CacheFileInputStream::Tell(int64_t* _retval) {
398 CacheFileAutoLock lock(mFile);
400 if (mClosed) {
401 LOG(("CacheFileInputStream::Tell() - Stream is closed. [this=%p]", this));
402 return NS_BASE_STREAM_CLOSED;
405 *_retval = mPos;
407 if (mAlternativeData) {
408 *_retval -= mFile->mAltDataOffset;
411 LOG(("CacheFileInputStream::Tell() [this=%p, retval=%" PRId64 "]", this,
412 *_retval));
413 return NS_OK;
416 // CacheFileChunkListener
417 nsresult CacheFileInputStream::OnChunkRead(nsresult aResult,
418 CacheFileChunk* aChunk) {
419 MOZ_CRASH("CacheFileInputStream::OnChunkRead should not be called!");
420 return NS_ERROR_UNEXPECTED;
423 nsresult CacheFileInputStream::OnChunkWritten(nsresult aResult,
424 CacheFileChunk* aChunk) {
425 MOZ_CRASH("CacheFileInputStream::OnChunkWritten should not be called!");
426 return NS_ERROR_UNEXPECTED;
429 nsresult CacheFileInputStream::OnChunkAvailable(nsresult aResult,
430 uint32_t aChunkIdx,
431 CacheFileChunk* aChunk) {
432 CacheFileAutoLock lock(mFile);
434 LOG(("CacheFileInputStream::OnChunkAvailable() [this=%p, result=0x%08" PRIx32
435 ", "
436 "idx=%d, chunk=%p]",
437 this, static_cast<uint32_t>(aResult), aChunkIdx, aChunk));
439 MOZ_ASSERT(mListeningForChunk != -1);
441 if (mListeningForChunk != static_cast<int64_t>(aChunkIdx)) {
442 // This is not a chunk that we're waiting for
443 LOG(
444 ("CacheFileInputStream::OnChunkAvailable() - Notification is for a "
445 "different chunk. [this=%p, listeningForChunk=%" PRId64 "]",
446 this, mListeningForChunk));
448 return NS_OK;
451 MOZ_ASSERT(!mChunk);
452 MOZ_ASSERT(!mWaitingForUpdate);
453 MOZ_ASSERT(!mInReadSegments);
454 mListeningForChunk = -1;
456 if (mClosed) {
457 MOZ_ASSERT(!mCallback);
459 LOG(
460 ("CacheFileInputStream::OnChunkAvailable() - Stream is closed, "
461 "ignoring notification. [this=%p]",
462 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 CacheFileInputStream::OnChunkUpdated(CacheFileChunk* aChunk) {
486 CacheFileAutoLock lock(mFile);
488 LOG(("CacheFileInputStream::OnChunkUpdated() [this=%p, idx=%d]", this,
489 aChunk->Index()));
491 if (!mWaitingForUpdate) {
492 LOG(
493 ("CacheFileInputStream::OnChunkUpdated() - Ignoring notification since "
494 "mWaitingforUpdate == false. [this=%p]",
495 this));
497 return NS_OK;
500 mWaitingForUpdate = false;
502 MOZ_ASSERT(mChunk == aChunk);
504 MaybeNotifyListener();
506 return NS_OK;
509 void CacheFileInputStream::ReleaseChunk() {
510 mFile->AssertOwnsLock();
512 LOG(("CacheFileInputStream::ReleaseChunk() [this=%p, idx=%d]", this,
513 mChunk->Index()));
515 MOZ_ASSERT(!mInReadSegments);
517 if (mWaitingForUpdate) {
518 LOG(
519 ("CacheFileInputStream::ReleaseChunk() - Canceling waiting for update. "
520 "[this=%p]",
521 this));
523 mChunk->CancelWait(this);
524 mWaitingForUpdate = false;
527 mFile->ReleaseOutsideLock(std::move(mChunk));
530 void CacheFileInputStream::EnsureCorrectChunk(bool aReleaseOnly) {
531 mFile->AssertOwnsLock();
533 LOG(("CacheFileInputStream::EnsureCorrectChunk() [this=%p, releaseOnly=%d]",
534 this, aReleaseOnly));
536 nsresult rv;
538 uint32_t chunkIdx = mPos / kChunkSize;
540 if (mInReadSegments) {
541 // We must have correct chunk
542 MOZ_ASSERT(mChunk);
543 MOZ_ASSERT(mChunk->Index() == chunkIdx);
544 return;
547 if (mChunk) {
548 if (mChunk->Index() == chunkIdx) {
549 // we have a correct chunk
550 LOG(
551 ("CacheFileInputStream::EnsureCorrectChunk() - Have correct chunk "
552 "[this=%p, idx=%d]",
553 this, chunkIdx));
555 return;
557 ReleaseChunk();
560 MOZ_ASSERT(!mWaitingForUpdate);
562 if (aReleaseOnly) return;
564 if (mListeningForChunk == static_cast<int64_t>(chunkIdx)) {
565 // We're already waiting for this chunk
566 LOG(
567 ("CacheFileInputStream::EnsureCorrectChunk() - Already listening for "
568 "chunk %" PRId64 " [this=%p]",
569 mListeningForChunk, this));
571 return;
574 rv = mFile->GetChunkLocked(chunkIdx, CacheFile::READER, this,
575 getter_AddRefs(mChunk));
576 if (NS_FAILED(rv)) {
577 LOG(
578 ("CacheFileInputStream::EnsureCorrectChunk() - GetChunkLocked failed. "
579 "[this=%p, idx=%d, rv=0x%08" PRIx32 "]",
580 this, chunkIdx, static_cast<uint32_t>(rv)));
581 if (rv != NS_ERROR_NOT_AVAILABLE) {
582 // Close the stream with error. The consumer will receive this error later
583 // in Read(), Available() etc. We need to handle NS_ERROR_NOT_AVAILABLE
584 // differently since it is returned when the requested chunk is not
585 // available and there is no writer that could create it, i.e. it means
586 // that we've reached the end of the file.
587 CloseWithStatusLocked(rv);
589 return;
591 } else if (!mChunk) {
592 mListeningForChunk = static_cast<int64_t>(chunkIdx);
595 MaybeNotifyListener();
598 int64_t 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 "]", this,
617 retval));
619 return retval;
622 void CacheFileInputStream::NotifyListener() {
623 mFile->AssertOwnsLock();
625 LOG(("CacheFileInputStream::NotifyListener() [this=%p]", this));
627 MOZ_ASSERT(mCallback);
628 MOZ_ASSERT(!mInReadSegments);
630 if (!mCallbackTarget) {
631 mCallbackTarget = CacheFileIOManager::IOTarget();
632 if (!mCallbackTarget) {
633 LOG(
634 ("CacheFileInputStream::NotifyListener() - Cannot get Cache I/O "
635 "thread! Using main thread for callback."));
636 mCallbackTarget = GetMainThreadEventTarget();
640 nsCOMPtr<nsIInputStreamCallback> asyncCallback = NS_NewInputStreamReadyEvent(
641 "CacheFileInputStream::NotifyListener", mCallback, mCallbackTarget);
643 mCallback = nullptr;
644 mCallbackTarget = nullptr;
646 asyncCallback->OnInputStreamReady(this);
649 void CacheFileInputStream::MaybeNotifyListener() {
650 mFile->AssertOwnsLock();
652 LOG(
653 ("CacheFileInputStream::MaybeNotifyListener() [this=%p, mCallback=%p, "
654 "mClosed=%d, mStatus=0x%08" PRIx32
655 ", mChunk=%p, mListeningForChunk=%" PRId64 ", "
656 "mWaitingForUpdate=%d]",
657 this, mCallback.get(), mClosed, static_cast<uint32_t>(mStatus),
658 mChunk.get(), mListeningForChunk, mWaitingForUpdate));
660 MOZ_ASSERT(!mInReadSegments);
662 if (!mCallback) return;
664 if (mClosed || NS_FAILED(mStatus)) {
665 NotifyListener();
666 return;
669 if (!mChunk) {
670 if (mListeningForChunk == -1) {
671 // EOF, should we notify even if mCallbackFlags == WAIT_CLOSURE_ONLY ??
672 NotifyListener();
674 return;
677 MOZ_ASSERT(mPos / kChunkSize == mChunk->Index());
679 if (mWaitingForUpdate) return;
681 CacheFileChunkReadHandle hnd = mChunk->GetReadHandle();
682 int64_t canRead = CanRead(&hnd);
683 if (NS_FAILED(mStatus)) {
684 // CanRead() called CloseWithStatusLocked() which called
685 // MaybeNotifyListener() so the listener was already notified. Stop here.
686 MOZ_ASSERT(!mCallback);
687 return;
690 if (canRead > 0) {
691 if (!(mCallbackFlags & WAIT_CLOSURE_ONLY)) NotifyListener();
692 } else if (canRead == 0) {
693 if (!mFile->OutputStreamExists(mAlternativeData)) {
694 // EOF
695 NotifyListener();
696 } else {
697 mChunk->WaitForUpdate(this);
698 mWaitingForUpdate = true;
700 } else {
701 // Output have set EOF before mPos?
702 MOZ_ASSERT(false, "SetEOF is currenty not implemented?!");
703 NotifyListener();
707 // Memory reporting
709 size_t CacheFileInputStream::SizeOfIncludingThis(
710 mozilla::MallocSizeOf mallocSizeOf) const {
711 // Everything the stream keeps a reference to is already reported somewhere
712 // else. mFile reports itself. mChunk reported as part of CacheFile. mCallback
713 // is usually CacheFile or a class that is reported elsewhere.
714 return mallocSizeOf(this);
717 } // namespace mozilla::net