netwerk/cache2/CacheFileInputStream.cpp

Wed, 31 Dec 2014 06:55:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:50 +0100
changeset 2
7e26c7da4463
permissions
-rw-r--r--

Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2

     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()
    19 {
    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;
    28   }
    30   if (count == 1) {
    31     mFile->RemoveInput(this);
    32   }
    34   return count;
    35 }
    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   , mInReadSegments(false)
    53   , mCallbackFlags(0)
    54 {
    55   LOG(("CacheFileInputStream::CacheFileInputStream() [this=%p]", this));
    56   MOZ_COUNT_CTOR(CacheFileInputStream);
    57 }
    59 CacheFileInputStream::~CacheFileInputStream()
    60 {
    61   LOG(("CacheFileInputStream::~CacheFileInputStream() [this=%p]", this));
    62   MOZ_COUNT_DTOR(CacheFileInputStream);
    63 }
    65 // nsIInputStream
    66 NS_IMETHODIMP
    67 CacheFileInputStream::Close()
    68 {
    69   LOG(("CacheFileInputStream::Close() [this=%p]", this));
    70   return CloseWithStatus(NS_OK);
    71 }
    73 NS_IMETHODIMP
    74 CacheFileInputStream::Available(uint64_t *_retval)
    75 {
    76   CacheFileAutoLock lock(mFile);
    77   MOZ_ASSERT(!mInReadSegments);
    79   if (mClosed) {
    80     LOG(("CacheFileInputStream::Available() - Stream is closed. [this=%p, "
    81          "status=0x%08x]", this, mStatus));
    82     return NS_FAILED(mStatus) ? mStatus : NS_BASE_STREAM_CLOSED;
    83   }
    85   EnsureCorrectChunk(false);
    86   if (NS_FAILED(mStatus))
    87     return mStatus;
    89   *_retval = 0;
    91   if (mChunk) {
    92     int64_t canRead;
    93     const char *buf;
    94     CanRead(&canRead, &buf);
    96     if (canRead > 0)
    97       *_retval = canRead;
    98     else if (canRead == 0 && !mFile->mOutput)
    99       return NS_BASE_STREAM_CLOSED;
   100   }
   102   LOG(("CacheFileInputStream::Available() [this=%p, retval=%lld]",
   103        this, *_retval));
   105   return NS_OK;
   106 }
   108 NS_IMETHODIMP
   109 CacheFileInputStream::Read(char *aBuf, uint32_t aCount, uint32_t *_retval)
   110 {
   111   CacheFileAutoLock lock(mFile);
   112   MOZ_ASSERT(!mInReadSegments);
   114   LOG(("CacheFileInputStream::Read() [this=%p, count=%d]", this, aCount));
   116   nsresult rv;
   118   if (mClosed) {
   119     LOG(("CacheFileInputStream::Read() - Stream is closed. [this=%p, "
   120          "status=0x%08x]", this, mStatus));
   122     if NS_FAILED(mStatus)
   123       return mStatus;
   125     *_retval = 0;
   126     return NS_OK;
   127   }
   129   EnsureCorrectChunk(false);
   130   if (NS_FAILED(mStatus))
   131     return mStatus;
   133   if (!mChunk) {
   134     if (mListeningForChunk == -1) {
   135       LOG(("  no chunk, returning 0 read and NS_OK"));
   136       *_retval = 0;
   137       return NS_OK;
   138     }
   139     else {
   140       LOG(("  waiting for chuck, returning WOULD_BLOCK"));
   141       return NS_BASE_STREAM_WOULD_BLOCK;
   142     }
   143   }
   145   int64_t canRead;
   146   const char *buf;
   147   CanRead(&canRead, &buf);
   149   if (canRead < 0) {
   150     // file was truncated ???
   151     MOZ_ASSERT(false, "SetEOF is currenty not implemented?!");
   152     *_retval = 0;
   153     rv = NS_OK;
   154   }
   155   else if (canRead > 0) {
   156     *_retval = std::min(static_cast<uint32_t>(canRead), aCount);
   157     memcpy(aBuf, buf, *_retval);
   158     mPos += *_retval;
   160     EnsureCorrectChunk(!(canRead < aCount && mPos % kChunkSize == 0));
   162     rv = NS_OK;
   163   }
   164   else {
   165     if (mFile->mOutput)
   166       rv = NS_BASE_STREAM_WOULD_BLOCK;
   167     else {
   168       *_retval = 0;
   169       rv = NS_OK;
   170     }
   171   }
   173   LOG(("CacheFileInputStream::Read() [this=%p, rv=0x%08x, retval=%d",
   174        this, rv, *_retval));
   176   return rv;
   177 }
   179 NS_IMETHODIMP
   180 CacheFileInputStream::ReadSegments(nsWriteSegmentFun aWriter, void *aClosure,
   181                                    uint32_t aCount, uint32_t *_retval)
   182 {
   183   CacheFileAutoLock lock(mFile);
   184   MOZ_ASSERT(!mInReadSegments);
   186   LOG(("CacheFileInputStream::ReadSegments() [this=%p, count=%d]",
   187        this, aCount));
   189   nsresult rv;
   191   if (mClosed) {
   192     LOG(("CacheFileInputStream::ReadSegments() - Stream is closed. [this=%p, "
   193          "status=0x%08x]", this, mStatus));
   195     if NS_FAILED(mStatus)
   196       return mStatus;
   198     *_retval = 0;
   199     return NS_OK;
   200   }
   202   EnsureCorrectChunk(false);
   203   if (NS_FAILED(mStatus))
   204     return mStatus;
   206   if (!mChunk) {
   207     if (mListeningForChunk == -1) {
   208       *_retval = 0;
   209       return NS_OK;
   210     }
   211     else {
   212       return NS_BASE_STREAM_WOULD_BLOCK;
   213     }
   214   }
   216   int64_t canRead;
   217   const char *buf;
   218   CanRead(&canRead, &buf);
   220   if (canRead < 0) {
   221     // file was truncated ???
   222     MOZ_ASSERT(false, "SetEOF is currenty not implemented?!");
   223     *_retval = 0;
   224     rv = NS_OK;
   225   }
   226   else if (canRead > 0) {
   227     uint32_t toRead = std::min(static_cast<uint32_t>(canRead), aCount);
   229     // We need to release the lock to avoid lock re-entering
   230 #ifdef DEBUG
   231     int64_t oldPos = mPos;
   232 #endif
   233     mInReadSegments = true;
   234     lock.Unlock();
   235     rv = aWriter(this, aClosure, buf, 0, toRead, _retval);
   236     lock.Lock();
   237     mInReadSegments = false;
   238 #ifdef DEBUG
   239     MOZ_ASSERT(oldPos == mPos);
   240 #endif
   242     if (NS_SUCCEEDED(rv)) {
   243       MOZ_ASSERT(*_retval <= toRead,
   244                  "writer should not write more than we asked it to write");
   245       mPos += *_retval;
   246     }
   248     EnsureCorrectChunk(!(canRead < aCount && mPos % kChunkSize == 0));
   250     rv = NS_OK;
   251   }
   252   else {
   253     if (mFile->mOutput)
   254       rv = NS_BASE_STREAM_WOULD_BLOCK;
   255     else {
   256       *_retval = 0;
   257       rv = NS_OK;
   258     }
   259   }
   261   LOG(("CacheFileInputStream::ReadSegments() [this=%p, rv=0x%08x, retval=%d",
   262        this, rv, *_retval));
   264   return rv;
   265 }
   267 NS_IMETHODIMP
   268 CacheFileInputStream::IsNonBlocking(bool *_retval)
   269 {
   270   *_retval = true;
   271   return NS_OK;
   272 }
   274 // nsIAsyncInputStream
   275 NS_IMETHODIMP
   276 CacheFileInputStream::CloseWithStatus(nsresult aStatus)
   277 {
   278   CacheFileAutoLock lock(mFile);
   279   MOZ_ASSERT(!mInReadSegments);
   281   LOG(("CacheFileInputStream::CloseWithStatus() [this=%p, aStatus=0x%08x]",
   282        this, aStatus));
   284   if (mClosed) {
   285     MOZ_ASSERT(!mCallback);
   286     return NS_OK;
   287   }
   289   mClosed = true;
   290   mStatus = NS_FAILED(aStatus) ? aStatus : NS_BASE_STREAM_CLOSED;
   292   if (mChunk)
   293     ReleaseChunk();
   295   // TODO propagate error from input stream to other streams ???
   297   MaybeNotifyListener();
   299   return NS_OK;
   300 }
   302 NS_IMETHODIMP
   303 CacheFileInputStream::AsyncWait(nsIInputStreamCallback *aCallback,
   304                                 uint32_t aFlags,
   305                                 uint32_t aRequestedCount,
   306                                 nsIEventTarget *aEventTarget)
   307 {
   308   CacheFileAutoLock lock(mFile);
   309   MOZ_ASSERT(!mInReadSegments);
   311   LOG(("CacheFileInputStream::AsyncWait() [this=%p, callback=%p, flags=%d, "
   312        "requestedCount=%d, eventTarget=%p]", this, aCallback, aFlags,
   313        aRequestedCount, aEventTarget));
   315   mCallback = aCallback;
   316   mCallbackFlags = aFlags;
   318   if (!mCallback) {
   319     if (mWaitingForUpdate) {
   320       mChunk->CancelWait(this);
   321       mWaitingForUpdate = false;
   322     }
   323     return NS_OK;
   324   }
   326   if (mClosed) {
   327     NotifyListener();
   328     return NS_OK;
   329   }
   331   EnsureCorrectChunk(false);
   333   MaybeNotifyListener();
   335   return NS_OK;
   336 }
   338 // nsISeekableStream
   339 NS_IMETHODIMP
   340 CacheFileInputStream::Seek(int32_t whence, int64_t offset)
   341 {
   342   CacheFileAutoLock lock(mFile);
   343   MOZ_ASSERT(!mInReadSegments);
   345   LOG(("CacheFileInputStream::Seek() [this=%p, whence=%d, offset=%lld]",
   346        this, whence, offset));
   348   if (mClosed) {
   349     LOG(("CacheFileInputStream::Seek() - Stream is closed. [this=%p]", this));
   350     return NS_BASE_STREAM_CLOSED;
   351   }
   353   int64_t newPos = offset;
   354   switch (whence) {
   355     case NS_SEEK_SET:
   356       break;
   357     case NS_SEEK_CUR:
   358       newPos += mPos;
   359       break;
   360     case NS_SEEK_END:
   361       newPos += mFile->mDataSize;
   362       break;
   363     default:
   364       NS_ERROR("invalid whence");
   365       return NS_ERROR_INVALID_ARG;
   366   }
   367   mPos = newPos;
   368   EnsureCorrectChunk(true);
   370   LOG(("CacheFileInputStream::Seek() [this=%p, pos=%lld]", this, mPos));
   371   return NS_OK;
   372 }
   374 NS_IMETHODIMP
   375 CacheFileInputStream::Tell(int64_t *_retval)
   376 {
   377   CacheFileAutoLock lock(mFile);
   378   MOZ_ASSERT(!mInReadSegments);
   380   if (mClosed) {
   381     LOG(("CacheFileInputStream::Tell() - Stream is closed. [this=%p]", this));
   382     return NS_BASE_STREAM_CLOSED;
   383   }
   385   *_retval = mPos;
   387   LOG(("CacheFileInputStream::Tell() [this=%p, retval=%lld]", this, *_retval));
   388   return NS_OK;
   389 }
   391 NS_IMETHODIMP
   392 CacheFileInputStream::SetEOF()
   393 {
   394   MOZ_ASSERT(false, "Don't call SetEOF on cache input stream");
   395   return NS_ERROR_NOT_IMPLEMENTED;
   396 }
   398 // CacheFileChunkListener
   399 nsresult
   400 CacheFileInputStream::OnChunkRead(nsresult aResult, CacheFileChunk *aChunk)
   401 {
   402   MOZ_CRASH("CacheFileInputStream::OnChunkRead should not be called!");
   403   return NS_ERROR_UNEXPECTED;
   404 }
   406 nsresult
   407 CacheFileInputStream::OnChunkWritten(nsresult aResult, CacheFileChunk *aChunk)
   408 {
   409   MOZ_CRASH("CacheFileInputStream::OnChunkWritten should not be called!");
   410   return NS_ERROR_UNEXPECTED;
   411 }
   413 nsresult
   414 CacheFileInputStream::OnChunkAvailable(nsresult aResult, uint32_t aChunkIdx,
   415                                        CacheFileChunk *aChunk)
   416 {
   417   CacheFileAutoLock lock(mFile);
   418   MOZ_ASSERT(!mInReadSegments);
   420   LOG(("CacheFileInputStream::OnChunkAvailable() [this=%p, result=0x%08x, "
   421        "idx=%d, chunk=%p]", this, aResult, aChunkIdx, aChunk));
   423   MOZ_ASSERT(mListeningForChunk != -1);
   425   if (mListeningForChunk != static_cast<int64_t>(aChunkIdx)) {
   426     // This is not a chunk that we're waiting for
   427     LOG(("CacheFileInputStream::OnChunkAvailable() - Notification is for a "
   428          "different chunk. [this=%p, listeningForChunk=%lld]",
   429          this, mListeningForChunk));
   431     return NS_OK;
   432   }
   434   MOZ_ASSERT(!mChunk);
   435   MOZ_ASSERT(!mWaitingForUpdate);
   436   mListeningForChunk = -1;
   438   if (mClosed) {
   439     MOZ_ASSERT(!mCallback);
   441     LOG(("CacheFileInputStream::OnChunkAvailable() - Stream is closed, "
   442          "ignoring notification. [this=%p]", this));
   444     return NS_OK;
   445   }
   447   if (NS_SUCCEEDED(aResult)) {
   448     mChunk = aChunk;
   449   } else if (aResult != NS_ERROR_NOT_AVAILABLE) {
   450     // We store the error in mStatus, so we can propagate it later to consumer
   451     // in Read(), Available() etc. We need to handle NS_ERROR_NOT_AVAILABLE
   452     // differently since it is returned when the requested chunk is not
   453     // available and there is no writer that could create it, i.e. it means that
   454     // we've reached the end of the file.
   455     mStatus = aResult;
   456   }
   458   MaybeNotifyListener();
   460   return NS_OK;
   461 }
   463 nsresult
   464 CacheFileInputStream::OnChunkUpdated(CacheFileChunk *aChunk)
   465 {
   466   CacheFileAutoLock lock(mFile);
   467   MOZ_ASSERT(!mInReadSegments);
   469   LOG(("CacheFileInputStream::OnChunkUpdated() [this=%p, idx=%d]",
   470        this, aChunk->Index()));
   472   if (!mWaitingForUpdate) {
   473     LOG(("CacheFileInputStream::OnChunkUpdated() - Ignoring notification since "
   474          "mWaitingforUpdate == false. [this=%p]", this));
   476     return NS_OK;
   477   }
   478   else {
   479     mWaitingForUpdate = false;
   480   }
   482   MOZ_ASSERT(mChunk == aChunk);
   484   MaybeNotifyListener();
   486   return NS_OK;
   487 }
   489 void
   490 CacheFileInputStream::ReleaseChunk()
   491 {
   492   mFile->AssertOwnsLock();
   494   LOG(("CacheFileInputStream::ReleaseChunk() [this=%p, idx=%d]",
   495        this, mChunk->Index()));
   497   if (mWaitingForUpdate) {
   498     LOG(("CacheFileInputStream::ReleaseChunk() - Canceling waiting for update. "
   499          "[this=%p]", this));
   501     mChunk->CancelWait(this);
   502     mWaitingForUpdate = false;
   503   }
   505   mFile->ReleaseOutsideLock(mChunk.forget().take());
   506 }
   508 void
   509 CacheFileInputStream::EnsureCorrectChunk(bool aReleaseOnly)
   510 {
   511   mFile->AssertOwnsLock();
   513   LOG(("CacheFileInputStream::EnsureCorrectChunk() [this=%p, releaseOnly=%d]",
   514        this, aReleaseOnly));
   516   nsresult rv;
   518   uint32_t chunkIdx = mPos / kChunkSize;
   520   if (mChunk) {
   521     if (mChunk->Index() == chunkIdx) {
   522       // we have a correct chunk
   523       LOG(("CacheFileInputStream::EnsureCorrectChunk() - Have correct chunk "
   524            "[this=%p, idx=%d]", this, chunkIdx));
   526       return;
   527     }
   528     else {
   529       ReleaseChunk();
   530     }
   531   }
   533   MOZ_ASSERT(!mWaitingForUpdate);
   535   if (aReleaseOnly)
   536     return;
   538   if (mListeningForChunk == static_cast<int64_t>(chunkIdx)) {
   539     // We're already waiting for this chunk
   540     LOG(("CacheFileInputStream::EnsureCorrectChunk() - Already listening for "
   541          "chunk %lld [this=%p]", mListeningForChunk, this));
   543     return;
   544   }
   546   rv = mFile->GetChunkLocked(chunkIdx, false, this, getter_AddRefs(mChunk));
   547   if (NS_FAILED(rv)) {
   548     LOG(("CacheFileInputStream::EnsureCorrectChunk() - GetChunkLocked failed. "
   549          "[this=%p, idx=%d, rv=0x%08x]", this, chunkIdx, rv));
   550     if (rv != NS_ERROR_NOT_AVAILABLE) {
   551       // We store the error in mStatus, so we can propagate it later to consumer
   552       // in Read(), Available() etc. We need to handle NS_ERROR_NOT_AVAILABLE
   553       // differently since it is returned when the requested chunk is not
   554       // available and there is no writer that could create it, i.e. it means
   555       // that we've reached the end of the file.
   556       mStatus = rv;
   557     }
   558   }
   559   else if (!mChunk) {
   560     mListeningForChunk = static_cast<int64_t>(chunkIdx);
   561   }
   563   MaybeNotifyListener();
   564 }
   566 void
   567 CacheFileInputStream::CanRead(int64_t *aCanRead, const char **aBuf)
   568 {
   569   mFile->AssertOwnsLock();
   571   MOZ_ASSERT(mChunk);
   572   MOZ_ASSERT(mPos / kChunkSize == mChunk->Index());
   574   uint32_t chunkOffset = mPos - (mPos / kChunkSize) * kChunkSize;
   575   *aCanRead = mChunk->DataSize() - chunkOffset;
   576   *aBuf = mChunk->BufForReading() + chunkOffset;
   578   LOG(("CacheFileInputStream::CanRead() [this=%p, canRead=%lld]",
   579        this, *aCanRead));
   580 }
   582 void
   583 CacheFileInputStream::NotifyListener()
   584 {
   585   mFile->AssertOwnsLock();
   587   LOG(("CacheFileInputStream::NotifyListener() [this=%p]", this));
   589   MOZ_ASSERT(mCallback);
   591   if (!mCallbackTarget)
   592     mCallbackTarget = NS_GetCurrentThread();
   594   nsCOMPtr<nsIInputStreamCallback> asyncCallback =
   595     NS_NewInputStreamReadyEvent(mCallback, mCallbackTarget);
   597   mCallback = nullptr;
   598   mCallbackTarget = nullptr;
   600   asyncCallback->OnInputStreamReady(this);
   601 }
   603 void
   604 CacheFileInputStream::MaybeNotifyListener()
   605 {
   606   mFile->AssertOwnsLock();
   608   LOG(("CacheFileInputStream::MaybeNotifyListener() [this=%p, mCallback=%p, "
   609        "mClosed=%d, mStatus=0x%08x, mChunk=%p, mListeningForChunk=%lld, "
   610        "mWaitingForUpdate=%d]", this, mCallback.get(), mClosed, mStatus,
   611        mChunk.get(), mListeningForChunk, mWaitingForUpdate));
   613   if (!mCallback)
   614     return;
   616   if (mClosed || NS_FAILED(mStatus)) {
   617     NotifyListener();
   618     return;
   619   }
   621   if (!mChunk) {
   622     if (mListeningForChunk == -1) {
   623       // EOF, should we notify even if mCallbackFlags == WAIT_CLOSURE_ONLY ??
   624       NotifyListener();
   625     }
   626     return;
   627   }
   629   MOZ_ASSERT(mPos / kChunkSize == mChunk->Index());
   631   if (mWaitingForUpdate)
   632     return;
   634   int64_t canRead;
   635   const char *buf;
   636   CanRead(&canRead, &buf);
   638   if (canRead > 0) {
   639     if (!(mCallbackFlags & WAIT_CLOSURE_ONLY))
   640       NotifyListener();
   641   }
   642   else if (canRead == 0) {
   643     if (!mFile->mOutput) {
   644       // EOF
   645       NotifyListener();
   646     }
   647     else {
   648       mChunk->WaitForUpdate(this);
   649       mWaitingForUpdate = true;
   650     }
   651   }
   652   else {
   653     // Output have set EOF before mPos?
   654     MOZ_ASSERT(false, "SetEOF is currenty not implemented?!");
   655     NotifyListener();
   656   }
   657 }
   659 // Memory reporting
   661 size_t
   662 CacheFileInputStream::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
   663 {
   664   // Everything the stream keeps a reference to is already reported somewhere else.
   665   // mFile reports itself.
   666   // mChunk reported as part of CacheFile.
   667   // mCallback is usually CacheFile or a class that is reported elsewhere.
   668   return mallocSizeOf(this);
   669 }
   671 } // net
   672 } // mozilla

mercurial