netwerk/cache2/CacheFileOutputStream.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 "CacheFileOutputStream.h"
     8 #include "CacheFile.h"
     9 #include "CacheEntry.h"
    10 #include "nsStreamUtils.h"
    11 #include "nsThreadUtils.h"
    12 #include "mozilla/DebugOnly.h"
    13 #include <algorithm>
    15 namespace mozilla {
    16 namespace net {
    18 NS_IMPL_ADDREF(CacheFileOutputStream)
    19 NS_IMETHODIMP_(MozExternalRefCountType)
    20 CacheFileOutputStream::Release()
    21 {
    22   NS_PRECONDITION(0 != mRefCnt, "dup release");
    23   nsrefcnt count = --mRefCnt;
    24   NS_LOG_RELEASE(this, count, "CacheFileOutputStream");
    26   if (0 == count) {
    27     mRefCnt = 1;
    28     {
    29       CacheFileAutoLock lock(mFile);
    30       mFile->RemoveOutput(this);
    31     }
    32     delete (this);
    33     return 0;
    34   }
    36   return count;
    37 }
    39 NS_INTERFACE_MAP_BEGIN(CacheFileOutputStream)
    40   NS_INTERFACE_MAP_ENTRY(nsIOutputStream)
    41   NS_INTERFACE_MAP_ENTRY(nsIAsyncOutputStream)
    42   NS_INTERFACE_MAP_ENTRY(nsISeekableStream)
    43   NS_INTERFACE_MAP_ENTRY(mozilla::net::CacheFileChunkListener)
    44   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIOutputStream)
    45 NS_INTERFACE_MAP_END_THREADSAFE
    47 CacheFileOutputStream::CacheFileOutputStream(CacheFile *aFile,
    48                                              CacheOutputCloseListener *aCloseListener)
    49   : mFile(aFile)
    50   , mCloseListener(aCloseListener)
    51   , mPos(0)
    52   , mClosed(false)
    53   , mStatus(NS_OK)
    54   , mCallbackFlags(0)
    55 {
    56   LOG(("CacheFileOutputStream::CacheFileOutputStream() [this=%p]", this));
    57   MOZ_COUNT_CTOR(CacheFileOutputStream);
    58 }
    60 CacheFileOutputStream::~CacheFileOutputStream()
    61 {
    62   LOG(("CacheFileOutputStream::~CacheFileOutputStream() [this=%p]", this));
    63   MOZ_COUNT_DTOR(CacheFileOutputStream);
    64 }
    66 // nsIOutputStream
    67 NS_IMETHODIMP
    68 CacheFileOutputStream::Close()
    69 {
    70   LOG(("CacheFileOutputStream::Close() [this=%p]", this));
    71   return CloseWithStatus(NS_OK);
    72 }
    74 NS_IMETHODIMP
    75 CacheFileOutputStream::Flush()
    76 {
    77   // TODO do we need to implement flush ???
    78   LOG(("CacheFileOutputStream::Flush() [this=%p]", this));
    79   return NS_OK;
    80 }
    82 NS_IMETHODIMP
    83 CacheFileOutputStream::Write(const char * aBuf, uint32_t aCount,
    84                              uint32_t *_retval)
    85 {
    86   CacheFileAutoLock lock(mFile);
    88   LOG(("CacheFileOutputStream::Write() [this=%p, count=%d]", this, aCount));
    90   if (mClosed) {
    91     LOG(("CacheFileOutputStream::Write() - Stream is closed. [this=%p, "
    92          "status=0x%08x]", this, mStatus));
    94     return NS_FAILED(mStatus) ? mStatus : NS_BASE_STREAM_CLOSED;
    95   }
    97   *_retval = aCount;
    99   while (aCount) {
   100     EnsureCorrectChunk(false);
   101     if (NS_FAILED(mStatus))
   102       return mStatus;
   104     FillHole();
   106     uint32_t chunkOffset = mPos - (mPos / kChunkSize) * kChunkSize;
   107     uint32_t canWrite = kChunkSize - chunkOffset;
   108     uint32_t thisWrite = std::min(static_cast<uint32_t>(canWrite), aCount);
   109     mChunk->EnsureBufSize(chunkOffset + thisWrite);
   110     memcpy(mChunk->BufForWriting() + chunkOffset, aBuf, thisWrite);
   112     mPos += thisWrite;
   113     aBuf += thisWrite;
   114     aCount -= thisWrite;
   116     mChunk->UpdateDataSize(chunkOffset, thisWrite, false);
   117   }
   119   EnsureCorrectChunk(true);
   121   LOG(("CacheFileOutputStream::Write() - Wrote %d bytes [this=%p]",
   122        *_retval, this));
   124   return NS_OK;
   125 }
   127 NS_IMETHODIMP
   128 CacheFileOutputStream::WriteFrom(nsIInputStream *aFromStream, uint32_t aCount,
   129                                  uint32_t *_retval)
   130 {
   131   LOG(("CacheFileOutputStream::WriteFrom() - NOT_IMPLEMENTED [this=%p, from=%p"
   132        ", count=%d]", this, aFromStream, aCount));
   134   return NS_ERROR_NOT_IMPLEMENTED;
   135 }
   137 NS_IMETHODIMP
   138 CacheFileOutputStream::WriteSegments(nsReadSegmentFun aReader, void *aClosure,
   139                                      uint32_t aCount, uint32_t *_retval)
   140 {
   141   LOG(("CacheFileOutputStream::WriteSegments() - NOT_IMPLEMENTED [this=%p, "
   142        "count=%d]", this, aCount));
   144   return NS_ERROR_NOT_IMPLEMENTED;
   145 }
   147 NS_IMETHODIMP
   148 CacheFileOutputStream::IsNonBlocking(bool *_retval)
   149 {
   150   *_retval = false;
   151   return NS_OK;
   152 }
   154 // nsIAsyncOutputStream
   155 NS_IMETHODIMP
   156 CacheFileOutputStream::CloseWithStatus(nsresult aStatus)
   157 {
   158   CacheFileAutoLock lock(mFile);
   160   LOG(("CacheFileOutputStream::CloseWithStatus() [this=%p, aStatus=0x%08x]",
   161        this, aStatus));
   163   if (mClosed) {
   164     MOZ_ASSERT(!mCallback);
   165     return NS_OK;
   166   }
   168   mClosed = true;
   169   mStatus = NS_FAILED(aStatus) ? aStatus : NS_BASE_STREAM_CLOSED;
   171   if (mChunk)
   172     ReleaseChunk();
   174   if (mCallback)
   175     NotifyListener();
   177   mFile->RemoveOutput(this);
   179   return NS_OK;
   180 }
   182 NS_IMETHODIMP
   183 CacheFileOutputStream::AsyncWait(nsIOutputStreamCallback *aCallback,
   184                                  uint32_t aFlags,
   185                                  uint32_t aRequestedCount,
   186                                  nsIEventTarget *aEventTarget)
   187 {
   188   CacheFileAutoLock lock(mFile);
   190   LOG(("CacheFileOutputStream::AsyncWait() [this=%p, callback=%p, flags=%d, "
   191        "requestedCount=%d, eventTarget=%p]", this, aCallback, aFlags,
   192        aRequestedCount, aEventTarget));
   194   mCallback = aCallback;
   195   mCallbackFlags = aFlags;
   197   if (!mCallback)
   198     return NS_OK;
   200   // The stream is blocking so it is writable at any time
   201   if (mClosed || !(aFlags & WAIT_CLOSURE_ONLY))
   202     NotifyListener();
   204   return NS_OK;
   205 }
   207 // nsISeekableStream
   208 NS_IMETHODIMP
   209 CacheFileOutputStream::Seek(int32_t whence, int64_t offset)
   210 {
   211   CacheFileAutoLock lock(mFile);
   213   LOG(("CacheFileOutputStream::Seek() [this=%p, whence=%d, offset=%lld]",
   214        this, whence, offset));
   216   if (mClosed) {
   217     LOG(("CacheFileOutputStream::Seek() - Stream is closed. [this=%p]", this));
   218     return NS_BASE_STREAM_CLOSED;
   219   }
   221   int64_t newPos = offset;
   222   switch (whence) {
   223     case NS_SEEK_SET:
   224       break;
   225     case NS_SEEK_CUR:
   226       newPos += mPos;
   227       break;
   228     case NS_SEEK_END:
   229       newPos += mFile->mDataSize;
   230       break;
   231     default:
   232       NS_ERROR("invalid whence");
   233       return NS_ERROR_INVALID_ARG;
   234   }
   235   mPos = newPos;
   236   EnsureCorrectChunk(true);
   238   LOG(("CacheFileOutputStream::Seek() [this=%p, pos=%lld]", this, mPos));
   239   return NS_OK;
   240 }
   242 NS_IMETHODIMP
   243 CacheFileOutputStream::Tell(int64_t *_retval)
   244 {
   245   CacheFileAutoLock lock(mFile);
   247   if (mClosed) {
   248     LOG(("CacheFileOutputStream::Tell() - Stream is closed. [this=%p]", this));
   249     return NS_BASE_STREAM_CLOSED;
   250   }
   252   *_retval = mPos;
   254   LOG(("CacheFileOutputStream::Tell() [this=%p, retval=%lld]", this, *_retval));
   255   return NS_OK;
   256 }
   258 NS_IMETHODIMP
   259 CacheFileOutputStream::SetEOF()
   260 {
   261   MOZ_ASSERT(false, "CacheFileOutputStream::SetEOF() not implemented");
   262   // Right now we don't use SetEOF(). If we ever need this method, we need
   263   // to think about what to do with input streams that already points beyond
   264   // new EOF.
   265   return NS_ERROR_NOT_IMPLEMENTED;
   266 }
   268 // CacheFileChunkListener
   269 nsresult
   270 CacheFileOutputStream::OnChunkRead(nsresult aResult, CacheFileChunk *aChunk)
   271 {
   272   MOZ_CRASH("CacheFileOutputStream::OnChunkRead should not be called!");
   273   return NS_ERROR_UNEXPECTED;
   274 }
   276 nsresult
   277 CacheFileOutputStream::OnChunkWritten(nsresult aResult, CacheFileChunk *aChunk)
   278 {
   279   MOZ_CRASH(
   280     "CacheFileOutputStream::OnChunkWritten should not be called!");
   281   return NS_ERROR_UNEXPECTED;
   282 }
   284 nsresult
   285 CacheFileOutputStream::OnChunkAvailable(nsresult aResult,
   286                                         uint32_t aChunkIdx,
   287                                         CacheFileChunk *aChunk)
   288 {
   289   MOZ_CRASH(
   290     "CacheFileOutputStream::OnChunkAvailable should not be called!");
   291   return NS_ERROR_UNEXPECTED;
   292 }
   294 nsresult
   295 CacheFileOutputStream::OnChunkUpdated(CacheFileChunk *aChunk)
   296 {
   297   MOZ_CRASH(
   298     "CacheFileOutputStream::OnChunkUpdated should not be called!");
   299   return NS_ERROR_UNEXPECTED;
   300 }
   302 void CacheFileOutputStream::NotifyCloseListener()
   303 {
   304   nsRefPtr<CacheOutputCloseListener> listener;
   305   listener.swap(mCloseListener);
   306   if (!listener)
   307     return;
   309   listener->OnOutputClosed();
   310 }
   312 void
   313 CacheFileOutputStream::ReleaseChunk()
   314 {
   315   LOG(("CacheFileOutputStream::ReleaseChunk() [this=%p, idx=%d]",
   316        this, mChunk->Index()));
   318   mFile->ReleaseOutsideLock(mChunk.forget().take());
   319 }
   321 void
   322 CacheFileOutputStream::EnsureCorrectChunk(bool aReleaseOnly)
   323 {
   324   mFile->AssertOwnsLock();
   326   LOG(("CacheFileOutputStream::EnsureCorrectChunk() [this=%p, releaseOnly=%d]",
   327        this, aReleaseOnly));
   329   uint32_t chunkIdx = mPos / kChunkSize;
   331   if (mChunk) {
   332     if (mChunk->Index() == chunkIdx) {
   333       // we have a correct chunk
   334       LOG(("CacheFileOutputStream::EnsureCorrectChunk() - Have correct chunk "
   335            "[this=%p, idx=%d]", this, chunkIdx));
   337       return;
   338     }
   339     else {
   340       ReleaseChunk();
   341     }
   342   }
   344   if (aReleaseOnly)
   345     return;
   347   nsresult rv;
   348   rv = mFile->GetChunkLocked(chunkIdx, true, nullptr, getter_AddRefs(mChunk));
   349   if (NS_FAILED(rv)) {
   350     LOG(("CacheFileOutputStream::EnsureCorrectChunk() - GetChunkLocked failed. "
   351          "[this=%p, idx=%d, rv=0x%08x]", this, chunkIdx, rv));
   352     mStatus = rv;
   353   }
   354 }
   356 void
   357 CacheFileOutputStream::FillHole()
   358 {
   359   mFile->AssertOwnsLock();
   361   MOZ_ASSERT(mChunk);
   362   MOZ_ASSERT(mPos / kChunkSize == mChunk->Index());
   364   uint32_t pos = mPos - (mPos / kChunkSize) * kChunkSize;
   365   if (mChunk->DataSize() >= pos)
   366     return;
   368   LOG(("CacheFileOutputStream::FillHole() - Zeroing hole in chunk %d, range "
   369        "%d-%d [this=%p]", mChunk->Index(), mChunk->DataSize(), pos - 1, this));
   371   mChunk->EnsureBufSize(pos);
   372   memset(mChunk->BufForWriting() + mChunk->DataSize(), 0,
   373          pos - mChunk->DataSize());
   375   mChunk->UpdateDataSize(mChunk->DataSize(), pos - mChunk->DataSize(), false);
   376 }
   378 void
   379 CacheFileOutputStream::NotifyListener()
   380 {
   381   mFile->AssertOwnsLock();
   383   LOG(("CacheFileOutputStream::NotifyListener() [this=%p]", this));
   385   MOZ_ASSERT(mCallback);
   387   if (!mCallbackTarget)
   388     mCallbackTarget = NS_GetCurrentThread();
   390   nsCOMPtr<nsIOutputStreamCallback> asyncCallback =
   391     NS_NewOutputStreamReadyEvent(mCallback, mCallbackTarget);
   393   mCallback = nullptr;
   394   mCallbackTarget = nullptr;
   396   asyncCallback->OnOutputStreamReady(this);
   397 }
   399 // Memory reporting
   401 size_t
   402 CacheFileOutputStream::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
   403 {
   404   // Everything the stream keeps a reference to is already reported somewhere else.
   405   // mFile reports itself.
   406   // mChunk reported as part of CacheFile.
   407   // mCloseListener is CacheEntry, already reported.
   408   // mCallback is usually CacheFile or a class that is reported elsewhere.
   409   return mallocSizeOf(this);
   410 }
   412 } // net
   413 } // mozilla

mercurial