Bumping manifests a=b2g-bump
[gecko.git] / netwerk / cache2 / CacheFileInputStream.cpp
blob20e6e58b31d7b6bbb36b78a1ac4fe8a67a1cf5a9
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 {
14 namespace net {
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");
24 if (0 == count) {
25 mRefCnt = 1;
26 delete (this);
27 return 0;
30 if (count == 1) {
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(mozilla::net::CacheFileChunkListener)
42 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInputStream)
43 NS_INTERFACE_MAP_END_THREADSAFE
45 CacheFileInputStream::CacheFileInputStream(CacheFile *aFile)
46 : mFile(aFile)
47 , mPos(0)
48 , mClosed(false)
49 , mStatus(NS_OK)
50 , mWaitingForUpdate(false)
51 , mListeningForChunk(-1)
52 , mCallbackFlags(0)
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);
64 // nsIInputStream
65 NS_IMETHODIMP
66 CacheFileInputStream::Close()
68 LOG(("CacheFileInputStream::Close() [this=%p]", this));
69 return CloseWithStatus(NS_OK);
72 NS_IMETHODIMP
73 CacheFileInputStream::Available(uint64_t *_retval)
75 CacheFileAutoLock lock(mFile);
77 if (mClosed) {
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))
85 return mStatus;
87 *_retval = 0;
89 if (mChunk) {
90 int64_t canRead = mFile->BytesFromChunk(mChunk->Index());
91 canRead -= (mPos % kChunkSize);
93 if (canRead > 0)
94 *_retval = canRead;
95 else if (canRead == 0 && !mFile->mOutput)
96 return NS_BASE_STREAM_CLOSED;
99 LOG(("CacheFileInputStream::Available() [this=%p, retval=%lld]",
100 this, *_retval));
102 return NS_OK;
105 NS_IMETHODIMP
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);
112 NS_IMETHODIMP
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]",
119 this, aCount));
121 nsresult rv;
123 *_retval = 0;
125 if (mClosed) {
126 LOG(("CacheFileInputStream::ReadSegments() - Stream is closed. [this=%p, "
127 "status=0x%08x]", this, mStatus));
129 if NS_FAILED(mStatus)
130 return mStatus;
132 return NS_OK;
135 EnsureCorrectChunk(false);
137 while (true) {
138 if (NS_FAILED(mStatus))
139 return mStatus;
141 if (!mChunk) {
142 if (mListeningForChunk == -1) {
143 return NS_OK;
145 else {
146 return NS_BASE_STREAM_WOULD_BLOCK;
150 int64_t canRead;
151 const char *buf;
152 CanRead(&canRead, &buf);
153 if (NS_FAILED(mStatus)) {
154 return mStatus;
157 if (canRead < 0) {
158 // file was truncated ???
159 MOZ_ASSERT(false, "SetEOF is currenty not implemented?!");
160 rv = NS_OK;
161 } else if (canRead > 0) {
162 uint32_t toRead = std::min(static_cast<uint32_t>(canRead), aCount);
164 uint32_t read;
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");
171 *_retval += read;
172 mPos += read;
173 aCount -= read;
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.
180 continue;
184 rv = NS_OK;
185 } else {
186 if (mFile->mOutput)
187 rv = NS_BASE_STREAM_WOULD_BLOCK;
188 else {
189 rv = NS_OK;
193 break;
196 LOG(("CacheFileInputStream::ReadSegments() [this=%p, rv=0x%08x, retval=%d]",
197 this, rv, *_retval));
199 return rv;
202 NS_IMETHODIMP
203 CacheFileInputStream::IsNonBlocking(bool *_retval)
205 *_retval = true;
206 return NS_OK;
209 // nsIAsyncInputStream
210 NS_IMETHODIMP
211 CacheFileInputStream::CloseWithStatus(nsresult aStatus)
213 CacheFileAutoLock lock(mFile);
215 LOG(("CacheFileInputStream::CloseWithStatus() [this=%p, aStatus=0x%08x]",
216 this, aStatus));
218 return CloseWithStatusLocked(aStatus);
221 nsresult
222 CacheFileInputStream::CloseWithStatusLocked(nsresult aStatus)
224 LOG(("CacheFileInputStream::CloseWithStatusLocked() [this=%p, "
225 "aStatus=0x%08x]", this, aStatus));
227 if (mClosed) {
228 MOZ_ASSERT(!mCallback);
229 return NS_OK;
232 mClosed = true;
233 mStatus = NS_FAILED(aStatus) ? aStatus : NS_BASE_STREAM_CLOSED;
235 if (mChunk) {
236 ReleaseChunk();
239 // TODO propagate error from input stream to other streams ???
241 MaybeNotifyListener();
243 return NS_OK;
246 NS_IMETHODIMP
247 CacheFileInputStream::AsyncWait(nsIInputStreamCallback *aCallback,
248 uint32_t aFlags,
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;
262 if (!mCallback) {
263 if (mWaitingForUpdate) {
264 mChunk->CancelWait(this);
265 mWaitingForUpdate = false;
267 return NS_OK;
270 if (mClosed) {
271 NotifyListener();
272 return NS_OK;
275 EnsureCorrectChunk(false);
277 MaybeNotifyListener();
279 return NS_OK;
282 // nsISeekableStream
283 NS_IMETHODIMP
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));
291 if (mClosed) {
292 LOG(("CacheFileInputStream::Seek() - Stream is closed. [this=%p]", this));
293 return NS_BASE_STREAM_CLOSED;
296 int64_t newPos = offset;
297 switch (whence) {
298 case NS_SEEK_SET:
299 break;
300 case NS_SEEK_CUR:
301 newPos += mPos;
302 break;
303 case NS_SEEK_END:
304 newPos += mFile->mDataSize;
305 break;
306 default:
307 NS_ERROR("invalid whence");
308 return NS_ERROR_INVALID_ARG;
310 mPos = newPos;
311 EnsureCorrectChunk(false);
313 LOG(("CacheFileInputStream::Seek() [this=%p, pos=%lld]", this, mPos));
314 return NS_OK;
317 NS_IMETHODIMP
318 CacheFileInputStream::Tell(int64_t *_retval)
320 CacheFileAutoLock lock(mFile);
322 if (mClosed) {
323 LOG(("CacheFileInputStream::Tell() - Stream is closed. [this=%p]", this));
324 return NS_BASE_STREAM_CLOSED;
327 *_retval = mPos;
329 LOG(("CacheFileInputStream::Tell() [this=%p, retval=%lld]", this, *_retval));
330 return NS_OK;
333 NS_IMETHODIMP
334 CacheFileInputStream::SetEOF()
336 MOZ_ASSERT(false, "Don't call SetEOF on cache input stream");
337 return NS_ERROR_NOT_IMPLEMENTED;
340 // CacheFileChunkListener
341 nsresult
342 CacheFileInputStream::OnChunkRead(nsresult aResult, CacheFileChunk *aChunk)
344 MOZ_CRASH("CacheFileInputStream::OnChunkRead should not be called!");
345 return NS_ERROR_UNEXPECTED;
348 nsresult
349 CacheFileInputStream::OnChunkWritten(nsresult aResult, CacheFileChunk *aChunk)
351 MOZ_CRASH("CacheFileInputStream::OnChunkWritten should not be called!");
352 return NS_ERROR_UNEXPECTED;
355 nsresult
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));
372 return NS_OK;
375 MOZ_ASSERT(!mChunk);
376 MOZ_ASSERT(!mWaitingForUpdate);
377 mListeningForChunk = -1;
379 if (mClosed) {
380 MOZ_ASSERT(!mCallback);
382 LOG(("CacheFileInputStream::OnChunkAvailable() - Stream is closed, "
383 "ignoring notification. [this=%p]", this));
385 return NS_OK;
388 if (NS_SUCCEEDED(aResult)) {
389 mChunk = aChunk;
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);
398 return NS_OK;
401 MaybeNotifyListener();
403 return NS_OK;
406 nsresult
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));
418 return NS_OK;
420 else {
421 mWaitingForUpdate = false;
424 MOZ_ASSERT(mChunk == aChunk);
426 MaybeNotifyListener();
428 return NS_OK;
431 void
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. "
441 "[this=%p]", this));
443 mChunk->CancelWait(this);
444 mWaitingForUpdate = false;
447 mFile->ReleaseOutsideLock(mChunk.forget().take());
450 void
451 CacheFileInputStream::EnsureCorrectChunk(bool aReleaseOnly)
453 mFile->AssertOwnsLock();
455 LOG(("CacheFileInputStream::EnsureCorrectChunk() [this=%p, releaseOnly=%d]",
456 this, aReleaseOnly));
458 nsresult rv;
460 uint32_t chunkIdx = mPos / kChunkSize;
462 if (mChunk) {
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));
468 return;
470 else {
471 ReleaseChunk();
475 MOZ_ASSERT(!mWaitingForUpdate);
477 if (aReleaseOnly)
478 return;
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));
485 return;
488 rv = mFile->GetChunkLocked(chunkIdx, CacheFile::READER, this,
489 getter_AddRefs(mChunk));
490 if (NS_FAILED(rv)) {
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);
501 return;
503 } else if (!mChunk) {
504 mListeningForChunk = static_cast<int64_t>(chunkIdx);
507 MaybeNotifyListener();
510 void
511 CacheFileInputStream::CanRead(int64_t *aCanRead, const char **aBuf)
513 mFile->AssertOwnsLock();
515 MOZ_ASSERT(mChunk);
516 MOZ_ASSERT(mPos / kChunkSize == mChunk->Index());
518 uint32_t chunkOffset = mPos - (mPos / kChunkSize) * kChunkSize;
519 *aCanRead = mChunk->DataSize() - chunkOffset;
520 if (*aCanRead > 0) {
521 *aBuf = mChunk->BufForReading() + chunkOffset;
522 } else {
523 *aBuf = nullptr;
524 if (NS_FAILED(mChunk->GetStatus())) {
525 CloseWithStatusLocked(mChunk->GetStatus());
529 LOG(("CacheFileInputStream::CanRead() [this=%p, canRead=%lld]",
530 this, *aCanRead));
533 void
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);
554 mCallback = nullptr;
555 mCallbackTarget = nullptr;
557 asyncCallback->OnInputStreamReady(this);
560 void
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));
570 if (!mCallback)
571 return;
573 if (mClosed || NS_FAILED(mStatus)) {
574 NotifyListener();
575 return;
578 if (!mChunk) {
579 if (mListeningForChunk == -1) {
580 // EOF, should we notify even if mCallbackFlags == WAIT_CLOSURE_ONLY ??
581 NotifyListener();
583 return;
586 MOZ_ASSERT(mPos / kChunkSize == mChunk->Index());
588 if (mWaitingForUpdate)
589 return;
591 int64_t canRead;
592 const char *buf;
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);
598 return;
601 if (canRead > 0) {
602 if (!(mCallbackFlags & WAIT_CLOSURE_ONLY))
603 NotifyListener();
605 else if (canRead == 0) {
606 if (!mFile->mOutput) {
607 // EOF
608 NotifyListener();
610 else {
611 mChunk->WaitForUpdate(this);
612 mWaitingForUpdate = true;
615 else {
616 // Output have set EOF before mPos?
617 MOZ_ASSERT(false, "SetEOF is currenty not implemented?!");
618 NotifyListener();
622 // Memory reporting
624 size_t
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);
634 } // net
635 } // mozilla