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/. */
6 #include "CacheFileInputStream.h"
9 #include "nsStreamUtils.h"
10 #include "nsThreadUtils.h"
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");
31 mFile
->RemoveInput(this, mStatus
);
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
)
45 CacheFileInputStream::CacheFileInputStream(CacheFile
*aFile
,
47 bool aAlternativeData
)
52 , mInReadSegments(false)
53 , mWaitingForUpdate(false)
54 , mAlternativeData(aAlternativeData
)
55 , mListeningForChunk(-1)
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
);
74 CacheFileInputStream::Close()
76 LOG(("CacheFileInputStream::Close() [this=%p]", this));
77 return CloseWithStatus(NS_OK
);
81 CacheFileInputStream::Available(uint64_t *_retval
)
83 CacheFileAutoLock
lock(mFile
);
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
)));
102 int64_t canRead
= mFile
->BytesFromChunk(mChunk
->Index(), mAlternativeData
);
103 canRead
-= (mPos
% kChunkSize
);
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
)));
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
);
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]",
138 if (mInReadSegments
) {
139 LOG(("CacheFileInputStream::ReadSegments() - Cannot be called while the "
140 "stream is in ReadSegments!"));
141 return NS_ERROR_UNEXPECTED
;
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
)) {
159 EnsureCorrectChunk(false);
162 if (NS_FAILED(mStatus
))
166 if (mListeningForChunk
== -1) {
169 return NS_BASE_STREAM_WOULD_BLOCK
;
172 CacheFileChunkReadHandle hnd
= mChunk
->GetReadHandle();
173 int64_t canRead
= CanRead(&hnd
);
174 if (NS_FAILED(mStatus
)) {
179 // file was truncated ???
180 MOZ_ASSERT(false, "SetEOF is currenty not implemented?!");
182 } else if (canRead
> 0) {
183 uint32_t toRead
= std::min(static_cast<uint32_t>(canRead
), aCount
);
185 const char *buf
= hnd
.Buf() + (mPos
- hnd
.Offset());
187 mInReadSegments
= true;
190 rv
= aWriter(this, aClosure
, buf
, *_retval
, toRead
, &read
);
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");
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.
215 // The stream was closed from aWriter, do the cleanup.
221 if (*_retval
== 0 && mFile
->OutputStreamExists(mAlternativeData
)) {
222 rv
= NS_BASE_STREAM_WOULD_BLOCK
;
231 LOG(("CacheFileInputStream::ReadSegments() [this=%p, rv=0x%08" PRIx32
", retval=%d]",
232 this, static_cast<uint32_t>(rv
), *_retval
));
238 CacheFileInputStream::IsNonBlocking(bool *_retval
)
244 // nsIAsyncInputStream
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
);
257 CacheFileInputStream::CloseWithStatusLocked(nsresult aStatus
)
259 LOG(("CacheFileInputStream::CloseWithStatusLocked() [this=%p, "
260 "aStatus=0x%08" PRIx32
"]", this, static_cast<uint32_t>(aStatus
)));
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
);
273 mStatus
= NS_FAILED(aStatus
) ? aStatus
: NS_BASE_STREAM_CLOSED
;
275 if (!mInReadSegments
) {
283 CacheFileInputStream::CleanUp()
285 MOZ_ASSERT(!mInReadSegments
);
292 // TODO propagate error from input stream to other streams ???
294 MaybeNotifyListener();
296 mFile
->ReleaseOutsideLock(mCacheEntryHandle
.forget());
300 CacheFileInputStream::AsyncWait(nsIInputStreamCallback
*aCallback
,
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
;
324 if (mWaitingForUpdate
) {
325 mChunk
->CancelWait(this);
326 mWaitingForUpdate
= false;
336 EnsureCorrectChunk(false);
338 MaybeNotifyListener();
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
;
359 LOG(("CacheFileInputStream::Seek() - Stream is closed. [this=%p]", this));
360 return NS_BASE_STREAM_CLOSED
;
363 int64_t newPos
= offset
;
366 if (mAlternativeData
) {
367 newPos
+= mFile
->mAltDataOffset
;
374 if (mAlternativeData
) {
375 newPos
+= mFile
->mDataSize
;
377 newPos
+= mFile
->mAltDataOffset
;
381 NS_ERROR("invalid whence");
382 return NS_ERROR_INVALID_ARG
;
385 EnsureCorrectChunk(false);
387 LOG(("CacheFileInputStream::Seek() [this=%p, pos=%" PRId64
"]", this, mPos
));
392 CacheFileInputStream::Tell(int64_t *_retval
)
394 CacheFileAutoLock
lock(mFile
);
397 LOG(("CacheFileInputStream::Tell() - Stream is closed. [this=%p]", this));
398 return NS_BASE_STREAM_CLOSED
;
403 if (mAlternativeData
) {
404 *_retval
-= mFile
->mAltDataOffset
;
407 LOG(("CacheFileInputStream::Tell() [this=%p, retval=%" PRId64
"]", this, *_retval
));
412 CacheFileInputStream::SetEOF()
414 MOZ_ASSERT(false, "Don't call SetEOF on cache input stream");
415 return NS_ERROR_NOT_IMPLEMENTED
;
418 // CacheFileChunkListener
420 CacheFileInputStream::OnChunkRead(nsresult aResult
, CacheFileChunk
*aChunk
)
422 MOZ_CRASH("CacheFileInputStream::OnChunkRead should not be called!");
423 return NS_ERROR_UNEXPECTED
;
427 CacheFileInputStream::OnChunkWritten(nsresult aResult
, CacheFileChunk
*aChunk
)
429 MOZ_CRASH("CacheFileInputStream::OnChunkWritten should not be called!");
430 return NS_ERROR_UNEXPECTED
;
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
));
454 MOZ_ASSERT(!mWaitingForUpdate
);
455 MOZ_ASSERT(!mInReadSegments
);
456 mListeningForChunk
= -1;
459 MOZ_ASSERT(!mCallback
);
461 LOG(("CacheFileInputStream::OnChunkAvailable() - Stream is closed, "
462 "ignoring notification. [this=%p]", this));
467 if (NS_SUCCEEDED(aResult
)) {
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
);
480 MaybeNotifyListener();
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));
500 mWaitingForUpdate
= false;
502 MOZ_ASSERT(mChunk
== aChunk
);
504 MaybeNotifyListener();
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. "
523 mChunk
->CancelWait(this);
524 mWaitingForUpdate
= false;
527 mFile
->ReleaseOutsideLock(mChunk
.forget());
531 CacheFileInputStream::EnsureCorrectChunk(bool aReleaseOnly
)
533 mFile
->AssertOwnsLock();
535 LOG(("CacheFileInputStream::EnsureCorrectChunk() [this=%p, releaseOnly=%d]",
536 this, aReleaseOnly
));
540 uint32_t chunkIdx
= mPos
/ kChunkSize
;
542 if (mInReadSegments
) {
543 // We must have correct chunk
545 MOZ_ASSERT(mChunk
->Index() == chunkIdx
);
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
));
560 MOZ_ASSERT(!mWaitingForUpdate
);
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));
573 rv
= mFile
->GetChunkLocked(chunkIdx
, CacheFile::READER
, this,
574 getter_AddRefs(mChunk
));
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
);
589 } else if (!mChunk
) {
590 mListeningForChunk
= static_cast<int64_t>(chunkIdx
);
593 MaybeNotifyListener();
597 CacheFileInputStream::CanRead(CacheFileChunkReadHandle
*aHandle
)
599 mFile
->AssertOwnsLock();
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
;
612 if (retval
<= 0 && NS_FAILED(mChunk
->GetStatus())) {
613 CloseWithStatusLocked(mChunk
->GetStatus());
616 LOG(("CacheFileInputStream::CanRead() [this=%p, canRead=%" PRId64
"]",
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
);
646 mCallbackTarget
= nullptr;
648 asyncCallback
->OnInputStreamReady(this);
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
,
662 MOZ_ASSERT(!mInReadSegments
);
667 if (mClosed
|| NS_FAILED(mStatus
)) {
673 if (mListeningForChunk
== -1) {
674 // EOF, should we notify even if mCallbackFlags == WAIT_CLOSURE_ONLY ??
680 MOZ_ASSERT(mPos
/ kChunkSize
== mChunk
->Index());
682 if (mWaitingForUpdate
)
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
);
695 if (!(mCallbackFlags
& WAIT_CLOSURE_ONLY
))
698 else if (canRead
== 0) {
699 if (!mFile
->OutputStreamExists(mAlternativeData
)) {
704 mChunk
->WaitForUpdate(this);
705 mWaitingForUpdate
= true;
709 // Output have set EOF before mPos?
710 MOZ_ASSERT(false, "SetEOF is currenty not implemented?!");
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);
728 } // namespace mozilla