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 NS_PRECONDITION(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
)
43 NS_INTERFACE_MAP_END_THREADSAFE
45 CacheFileInputStream::CacheFileInputStream(CacheFile
*aFile
)
50 , mWaitingForUpdate(false)
51 , mListeningForChunk(-1)
54 LOG(("CacheFileInputStream::CacheFileInputStream() [this=%p]", this));
55 MOZ_COUNT_CTOR(CacheFileInputStream
);
58 CacheFileInputStream::~CacheFileInputStream()
60 LOG(("CacheFileInputStream::~CacheFileInputStream() [this=%p]", this));
61 MOZ_COUNT_DTOR(CacheFileInputStream
);
66 CacheFileInputStream::Close()
68 LOG(("CacheFileInputStream::Close() [this=%p]", this));
69 return CloseWithStatus(NS_OK
);
73 CacheFileInputStream::Available(uint64_t *_retval
)
75 CacheFileAutoLock
lock(mFile
);
78 LOG(("CacheFileInputStream::Available() - Stream is closed. [this=%p, "
79 "status=0x%08x]", this, mStatus
));
80 return NS_FAILED(mStatus
) ? mStatus
: NS_BASE_STREAM_CLOSED
;
83 EnsureCorrectChunk(false);
84 if (NS_FAILED(mStatus
))
90 int64_t canRead
= mFile
->BytesFromChunk(mChunk
->Index());
91 canRead
-= (mPos
% kChunkSize
);
95 else if (canRead
== 0 && !mFile
->mOutput
)
96 return NS_BASE_STREAM_CLOSED
;
99 LOG(("CacheFileInputStream::Available() [this=%p, retval=%lld]",
106 CacheFileInputStream::Read(char *aBuf
, uint32_t aCount
, uint32_t *_retval
)
108 LOG(("CacheFileInputStream::Read() [this=%p, count=%d]", this, aCount
));
109 return ReadSegments(NS_CopySegmentToBuffer
, aBuf
, aCount
, _retval
);
113 CacheFileInputStream::ReadSegments(nsWriteSegmentFun aWriter
, void *aClosure
,
114 uint32_t aCount
, uint32_t *_retval
)
116 CacheFileAutoLock
lock(mFile
);
118 LOG(("CacheFileInputStream::ReadSegments() [this=%p, count=%d]",
126 LOG(("CacheFileInputStream::ReadSegments() - Stream is closed. [this=%p, "
127 "status=0x%08x]", this, mStatus
));
129 if NS_FAILED(mStatus
)
135 EnsureCorrectChunk(false);
138 if (NS_FAILED(mStatus
))
142 if (mListeningForChunk
== -1) {
146 return NS_BASE_STREAM_WOULD_BLOCK
;
152 CanRead(&canRead
, &buf
);
153 if (NS_FAILED(mStatus
)) {
158 // file was truncated ???
159 MOZ_ASSERT(false, "SetEOF is currenty not implemented?!");
161 } else if (canRead
> 0) {
162 uint32_t toRead
= std::min(static_cast<uint32_t>(canRead
), aCount
);
165 rv
= aWriter(this, aClosure
, buf
, *_retval
, toRead
, &read
);
167 if (NS_SUCCEEDED(rv
)) {
168 MOZ_ASSERT(read
<= toRead
,
169 "writer should not write more than we asked it to write");
175 // The last chunk is released after the caller closes this stream.
176 EnsureCorrectChunk(false);
178 if (mChunk
&& aCount
) {
179 // We have the next chunk! Go on.
187 rv
= NS_BASE_STREAM_WOULD_BLOCK
;
196 LOG(("CacheFileInputStream::ReadSegments() [this=%p, rv=0x%08x, retval=%d]",
197 this, rv
, *_retval
));
203 CacheFileInputStream::IsNonBlocking(bool *_retval
)
209 // nsIAsyncInputStream
211 CacheFileInputStream::CloseWithStatus(nsresult aStatus
)
213 CacheFileAutoLock
lock(mFile
);
215 LOG(("CacheFileInputStream::CloseWithStatus() [this=%p, aStatus=0x%08x]",
218 return CloseWithStatusLocked(aStatus
);
222 CacheFileInputStream::CloseWithStatusLocked(nsresult aStatus
)
224 LOG(("CacheFileInputStream::CloseWithStatusLocked() [this=%p, "
225 "aStatus=0x%08x]", this, aStatus
));
228 MOZ_ASSERT(!mCallback
);
233 mStatus
= NS_FAILED(aStatus
) ? aStatus
: NS_BASE_STREAM_CLOSED
;
239 // TODO propagate error from input stream to other streams ???
241 MaybeNotifyListener();
247 CacheFileInputStream::AsyncWait(nsIInputStreamCallback
*aCallback
,
249 uint32_t aRequestedCount
,
250 nsIEventTarget
*aEventTarget
)
252 CacheFileAutoLock
lock(mFile
);
254 LOG(("CacheFileInputStream::AsyncWait() [this=%p, callback=%p, flags=%d, "
255 "requestedCount=%d, eventTarget=%p]", this, aCallback
, aFlags
,
256 aRequestedCount
, aEventTarget
));
258 mCallback
= aCallback
;
259 mCallbackFlags
= aFlags
;
260 mCallbackTarget
= aEventTarget
;
263 if (mWaitingForUpdate
) {
264 mChunk
->CancelWait(this);
265 mWaitingForUpdate
= false;
275 EnsureCorrectChunk(false);
277 MaybeNotifyListener();
284 CacheFileInputStream::Seek(int32_t whence
, int64_t offset
)
286 CacheFileAutoLock
lock(mFile
);
288 LOG(("CacheFileInputStream::Seek() [this=%p, whence=%d, offset=%lld]",
289 this, whence
, offset
));
292 LOG(("CacheFileInputStream::Seek() - Stream is closed. [this=%p]", this));
293 return NS_BASE_STREAM_CLOSED
;
296 int64_t newPos
= offset
;
304 newPos
+= mFile
->mDataSize
;
307 NS_ERROR("invalid whence");
308 return NS_ERROR_INVALID_ARG
;
311 EnsureCorrectChunk(false);
313 LOG(("CacheFileInputStream::Seek() [this=%p, pos=%lld]", this, mPos
));
318 CacheFileInputStream::Tell(int64_t *_retval
)
320 CacheFileAutoLock
lock(mFile
);
323 LOG(("CacheFileInputStream::Tell() - Stream is closed. [this=%p]", this));
324 return NS_BASE_STREAM_CLOSED
;
329 LOG(("CacheFileInputStream::Tell() [this=%p, retval=%lld]", this, *_retval
));
334 CacheFileInputStream::SetEOF()
336 MOZ_ASSERT(false, "Don't call SetEOF on cache input stream");
337 return NS_ERROR_NOT_IMPLEMENTED
;
340 // CacheFileChunkListener
342 CacheFileInputStream::OnChunkRead(nsresult aResult
, CacheFileChunk
*aChunk
)
344 MOZ_CRASH("CacheFileInputStream::OnChunkRead should not be called!");
345 return NS_ERROR_UNEXPECTED
;
349 CacheFileInputStream::OnChunkWritten(nsresult aResult
, CacheFileChunk
*aChunk
)
351 MOZ_CRASH("CacheFileInputStream::OnChunkWritten should not be called!");
352 return NS_ERROR_UNEXPECTED
;
356 CacheFileInputStream::OnChunkAvailable(nsresult aResult
, uint32_t aChunkIdx
,
357 CacheFileChunk
*aChunk
)
359 CacheFileAutoLock
lock(mFile
);
361 LOG(("CacheFileInputStream::OnChunkAvailable() [this=%p, result=0x%08x, "
362 "idx=%d, chunk=%p]", this, aResult
, aChunkIdx
, aChunk
));
364 MOZ_ASSERT(mListeningForChunk
!= -1);
366 if (mListeningForChunk
!= static_cast<int64_t>(aChunkIdx
)) {
367 // This is not a chunk that we're waiting for
368 LOG(("CacheFileInputStream::OnChunkAvailable() - Notification is for a "
369 "different chunk. [this=%p, listeningForChunk=%lld]",
370 this, mListeningForChunk
));
376 MOZ_ASSERT(!mWaitingForUpdate
);
377 mListeningForChunk
= -1;
380 MOZ_ASSERT(!mCallback
);
382 LOG(("CacheFileInputStream::OnChunkAvailable() - Stream is closed, "
383 "ignoring notification. [this=%p]", this));
388 if (NS_SUCCEEDED(aResult
)) {
390 } else if (aResult
!= NS_ERROR_NOT_AVAILABLE
) {
391 // Close the stream with error. The consumer will receive this error later
392 // in Read(), Available() etc. We need to handle NS_ERROR_NOT_AVAILABLE
393 // differently since it is returned when the requested chunk is not
394 // available and there is no writer that could create it, i.e. it means that
395 // we've reached the end of the file.
396 CloseWithStatusLocked(aResult
);
401 MaybeNotifyListener();
407 CacheFileInputStream::OnChunkUpdated(CacheFileChunk
*aChunk
)
409 CacheFileAutoLock
lock(mFile
);
411 LOG(("CacheFileInputStream::OnChunkUpdated() [this=%p, idx=%d]",
412 this, aChunk
->Index()));
414 if (!mWaitingForUpdate
) {
415 LOG(("CacheFileInputStream::OnChunkUpdated() - Ignoring notification since "
416 "mWaitingforUpdate == false. [this=%p]", this));
421 mWaitingForUpdate
= false;
424 MOZ_ASSERT(mChunk
== aChunk
);
426 MaybeNotifyListener();
432 CacheFileInputStream::ReleaseChunk()
434 mFile
->AssertOwnsLock();
436 LOG(("CacheFileInputStream::ReleaseChunk() [this=%p, idx=%d]",
437 this, mChunk
->Index()));
439 if (mWaitingForUpdate
) {
440 LOG(("CacheFileInputStream::ReleaseChunk() - Canceling waiting for update. "
443 mChunk
->CancelWait(this);
444 mWaitingForUpdate
= false;
447 mFile
->ReleaseOutsideLock(mChunk
.forget().take());
451 CacheFileInputStream::EnsureCorrectChunk(bool aReleaseOnly
)
453 mFile
->AssertOwnsLock();
455 LOG(("CacheFileInputStream::EnsureCorrectChunk() [this=%p, releaseOnly=%d]",
456 this, aReleaseOnly
));
460 uint32_t chunkIdx
= mPos
/ kChunkSize
;
463 if (mChunk
->Index() == chunkIdx
) {
464 // we have a correct chunk
465 LOG(("CacheFileInputStream::EnsureCorrectChunk() - Have correct chunk "
466 "[this=%p, idx=%d]", this, chunkIdx
));
475 MOZ_ASSERT(!mWaitingForUpdate
);
480 if (mListeningForChunk
== static_cast<int64_t>(chunkIdx
)) {
481 // We're already waiting for this chunk
482 LOG(("CacheFileInputStream::EnsureCorrectChunk() - Already listening for "
483 "chunk %lld [this=%p]", mListeningForChunk
, this));
488 rv
= mFile
->GetChunkLocked(chunkIdx
, CacheFile::READER
, this,
489 getter_AddRefs(mChunk
));
491 LOG(("CacheFileInputStream::EnsureCorrectChunk() - GetChunkLocked failed. "
492 "[this=%p, idx=%d, rv=0x%08x]", this, chunkIdx
, rv
));
493 if (rv
!= NS_ERROR_NOT_AVAILABLE
) {
494 // Close the stream with error. The consumer will receive this error later
495 // in Read(), Available() etc. We need to handle NS_ERROR_NOT_AVAILABLE
496 // differently since it is returned when the requested chunk is not
497 // available and there is no writer that could create it, i.e. it means
498 // that we've reached the end of the file.
499 CloseWithStatusLocked(rv
);
503 } else if (!mChunk
) {
504 mListeningForChunk
= static_cast<int64_t>(chunkIdx
);
507 MaybeNotifyListener();
511 CacheFileInputStream::CanRead(int64_t *aCanRead
, const char **aBuf
)
513 mFile
->AssertOwnsLock();
516 MOZ_ASSERT(mPos
/ kChunkSize
== mChunk
->Index());
518 uint32_t chunkOffset
= mPos
- (mPos
/ kChunkSize
) * kChunkSize
;
519 *aCanRead
= mChunk
->DataSize() - chunkOffset
;
521 *aBuf
= mChunk
->BufForReading() + chunkOffset
;
524 if (NS_FAILED(mChunk
->GetStatus())) {
525 CloseWithStatusLocked(mChunk
->GetStatus());
529 LOG(("CacheFileInputStream::CanRead() [this=%p, canRead=%lld]",
534 CacheFileInputStream::NotifyListener()
536 mFile
->AssertOwnsLock();
538 LOG(("CacheFileInputStream::NotifyListener() [this=%p]", this));
540 MOZ_ASSERT(mCallback
);
542 if (!mCallbackTarget
) {
543 mCallbackTarget
= CacheFileIOManager::IOTarget();
544 if (!mCallbackTarget
) {
545 LOG(("CacheFileInputStream::NotifyListener() - Cannot get Cache I/O "
546 "thread! Using main thread for callback."));
547 mCallbackTarget
= do_GetMainThread();
551 nsCOMPtr
<nsIInputStreamCallback
> asyncCallback
=
552 NS_NewInputStreamReadyEvent(mCallback
, mCallbackTarget
);
555 mCallbackTarget
= nullptr;
557 asyncCallback
->OnInputStreamReady(this);
561 CacheFileInputStream::MaybeNotifyListener()
563 mFile
->AssertOwnsLock();
565 LOG(("CacheFileInputStream::MaybeNotifyListener() [this=%p, mCallback=%p, "
566 "mClosed=%d, mStatus=0x%08x, mChunk=%p, mListeningForChunk=%lld, "
567 "mWaitingForUpdate=%d]", this, mCallback
.get(), mClosed
, mStatus
,
568 mChunk
.get(), mListeningForChunk
, mWaitingForUpdate
));
573 if (mClosed
|| NS_FAILED(mStatus
)) {
579 if (mListeningForChunk
== -1) {
580 // EOF, should we notify even if mCallbackFlags == WAIT_CLOSURE_ONLY ??
586 MOZ_ASSERT(mPos
/ kChunkSize
== mChunk
->Index());
588 if (mWaitingForUpdate
)
593 CanRead(&canRead
, &buf
);
594 if (NS_FAILED(mStatus
)) {
595 // CanRead() called CloseWithStatusLocked() which called
596 // MaybeNotifyListener() so the listener was already notified. Stop here.
597 MOZ_ASSERT(!mCallback
);
602 if (!(mCallbackFlags
& WAIT_CLOSURE_ONLY
))
605 else if (canRead
== 0) {
606 if (!mFile
->mOutput
) {
611 mChunk
->WaitForUpdate(this);
612 mWaitingForUpdate
= true;
616 // Output have set EOF before mPos?
617 MOZ_ASSERT(false, "SetEOF is currenty not implemented?!");
625 CacheFileInputStream::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf
) const
627 // Everything the stream keeps a reference to is already reported somewhere else.
628 // mFile reports itself.
629 // mChunk reported as part of CacheFile.
630 // mCallback is usually CacheFile or a class that is reported elsewhere.
631 return mallocSizeOf(this);