netwerk/cache2/CacheFile.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 "CacheFile.h"
     8 #include "CacheFileChunk.h"
     9 #include "CacheFileInputStream.h"
    10 #include "CacheFileOutputStream.h"
    11 #include "CacheIndex.h"
    12 #include "nsThreadUtils.h"
    13 #include "mozilla/DebugOnly.h"
    14 #include <algorithm>
    15 #include "nsComponentManagerUtils.h"
    16 #include "nsProxyRelease.h"
    18 // When CACHE_CHUNKS is defined we always cache unused chunks in mCacheChunks.
    19 // When it is not defined, we always release the chunks ASAP, i.e. we cache
    20 // unused chunks only when:
    21 //  - CacheFile is memory-only
    22 //  - CacheFile is still waiting for the handle
    24 //#define CACHE_CHUNKS
    26 namespace mozilla {
    27 namespace net {
    29 class NotifyCacheFileListenerEvent : public nsRunnable {
    30 public:
    31   NotifyCacheFileListenerEvent(CacheFileListener *aCallback,
    32                                nsresult aResult,
    33                                bool aIsNew)
    34     : mCallback(aCallback)
    35     , mRV(aResult)
    36     , mIsNew(aIsNew)
    37   {
    38     LOG(("NotifyCacheFileListenerEvent::NotifyCacheFileListenerEvent() "
    39          "[this=%p]", this));
    40     MOZ_COUNT_CTOR(NotifyCacheFileListenerEvent);
    41   }
    43   ~NotifyCacheFileListenerEvent()
    44   {
    45     LOG(("NotifyCacheFileListenerEvent::~NotifyCacheFileListenerEvent() "
    46          "[this=%p]", this));
    47     MOZ_COUNT_DTOR(NotifyCacheFileListenerEvent);
    48   }
    50   NS_IMETHOD Run()
    51   {
    52     LOG(("NotifyCacheFileListenerEvent::Run() [this=%p]", this));
    54     mCallback->OnFileReady(mRV, mIsNew);
    55     return NS_OK;
    56   }
    58 protected:
    59   nsCOMPtr<CacheFileListener> mCallback;
    60   nsresult                    mRV;
    61   bool                        mIsNew;
    62 };
    64 class NotifyChunkListenerEvent : public nsRunnable {
    65 public:
    66   NotifyChunkListenerEvent(CacheFileChunkListener *aCallback,
    67                            nsresult aResult,
    68                            uint32_t aChunkIdx,
    69                            CacheFileChunk *aChunk)
    70     : mCallback(aCallback)
    71     , mRV(aResult)
    72     , mChunkIdx(aChunkIdx)
    73     , mChunk(aChunk)
    74   {
    75     LOG(("NotifyChunkListenerEvent::NotifyChunkListenerEvent() [this=%p]",
    76          this));
    77     MOZ_COUNT_CTOR(NotifyChunkListenerEvent);
    78   }
    80   ~NotifyChunkListenerEvent()
    81   {
    82     LOG(("NotifyChunkListenerEvent::~NotifyChunkListenerEvent() [this=%p]",
    83          this));
    84     MOZ_COUNT_DTOR(NotifyChunkListenerEvent);
    85   }
    87   NS_IMETHOD Run()
    88   {
    89     LOG(("NotifyChunkListenerEvent::Run() [this=%p]", this));
    91     mCallback->OnChunkAvailable(mRV, mChunkIdx, mChunk);
    92     return NS_OK;
    93   }
    95 protected:
    96   nsCOMPtr<CacheFileChunkListener> mCallback;
    97   nsresult                         mRV;
    98   uint32_t                         mChunkIdx;
    99   nsRefPtr<CacheFileChunk>         mChunk;
   100 };
   103 class DoomFileHelper : public CacheFileIOListener
   104 {
   105 public:
   106   NS_DECL_THREADSAFE_ISUPPORTS
   108   DoomFileHelper(CacheFileListener *aListener)
   109     : mListener(aListener)
   110   {
   111     MOZ_COUNT_CTOR(DoomFileHelper);
   112   }
   115   NS_IMETHOD OnFileOpened(CacheFileHandle *aHandle, nsresult aResult)
   116   {
   117     MOZ_CRASH("DoomFileHelper::OnFileOpened should not be called!");
   118     return NS_ERROR_UNEXPECTED;
   119   }
   121   NS_IMETHOD OnDataWritten(CacheFileHandle *aHandle, const char *aBuf,
   122                            nsresult aResult)
   123   {
   124     MOZ_CRASH("DoomFileHelper::OnDataWritten should not be called!");
   125     return NS_ERROR_UNEXPECTED;
   126   }
   128   NS_IMETHOD OnDataRead(CacheFileHandle *aHandle, char *aBuf, nsresult aResult)
   129   {
   130     MOZ_CRASH("DoomFileHelper::OnDataRead should not be called!");
   131     return NS_ERROR_UNEXPECTED;
   132   }
   134   NS_IMETHOD OnFileDoomed(CacheFileHandle *aHandle, nsresult aResult)
   135   {
   136     if (mListener)
   137       mListener->OnFileDoomed(aResult);
   138     return NS_OK;
   139   }
   141   NS_IMETHOD OnEOFSet(CacheFileHandle *aHandle, nsresult aResult)
   142   {
   143     MOZ_CRASH("DoomFileHelper::OnEOFSet should not be called!");
   144     return NS_ERROR_UNEXPECTED;
   145   }
   147   NS_IMETHOD OnFileRenamed(CacheFileHandle *aHandle, nsresult aResult)
   148   {
   149     MOZ_CRASH("DoomFileHelper::OnFileRenamed should not be called!");
   150     return NS_ERROR_UNEXPECTED;
   151   }
   153 private:
   154   virtual ~DoomFileHelper()
   155   {
   156     MOZ_COUNT_DTOR(DoomFileHelper);
   157   }
   159   nsCOMPtr<CacheFileListener>  mListener;
   160 };
   162 NS_IMPL_ISUPPORTS(DoomFileHelper, CacheFileIOListener)
   165 NS_IMPL_ADDREF(CacheFile)
   166 NS_IMPL_RELEASE(CacheFile)
   167 NS_INTERFACE_MAP_BEGIN(CacheFile)
   168   NS_INTERFACE_MAP_ENTRY(mozilla::net::CacheFileChunkListener)
   169   NS_INTERFACE_MAP_ENTRY(mozilla::net::CacheFileIOListener)
   170   NS_INTERFACE_MAP_ENTRY(mozilla::net::CacheFileMetadataListener)
   171   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports,
   172                                    mozilla::net::CacheFileChunkListener)
   173 NS_INTERFACE_MAP_END_THREADSAFE
   175 CacheFile::CacheFile()
   176   : mLock("CacheFile.mLock")
   177   , mOpeningFile(false)
   178   , mReady(false)
   179   , mMemoryOnly(false)
   180   , mOpenAsMemoryOnly(false)
   181   , mDataAccessed(false)
   182   , mDataIsDirty(false)
   183   , mWritingMetadata(false)
   184   , mStatus(NS_OK)
   185   , mDataSize(-1)
   186   , mOutput(nullptr)
   187 {
   188   LOG(("CacheFile::CacheFile() [this=%p]", this));
   189 }
   191 CacheFile::~CacheFile()
   192 {
   193   LOG(("CacheFile::~CacheFile() [this=%p]", this));
   195   MutexAutoLock lock(mLock);
   196   if (!mMemoryOnly && mReady) {
   197     // mReady flag indicates we have metadata plus in a valid state.
   198     WriteMetadataIfNeededLocked(true);
   199   }
   200 }
   202 nsresult
   203 CacheFile::Init(const nsACString &aKey,
   204                 bool aCreateNew,
   205                 bool aMemoryOnly,
   206                 bool aPriority,
   207                 CacheFileListener *aCallback)
   208 {
   209   MOZ_ASSERT(!mListener);
   210   MOZ_ASSERT(!mHandle);
   212   nsresult rv;
   214   mKey = aKey;
   215   mOpenAsMemoryOnly = mMemoryOnly = aMemoryOnly;
   217   LOG(("CacheFile::Init() [this=%p, key=%s, createNew=%d, memoryOnly=%d, "
   218        "listener=%p]", this, mKey.get(), aCreateNew, aMemoryOnly, aCallback));
   220   if (mMemoryOnly) {
   221     MOZ_ASSERT(!aCallback);
   223     mMetadata = new CacheFileMetadata(mOpenAsMemoryOnly, mKey);
   224     mReady = true;
   225     mDataSize = mMetadata->Offset();
   226     return NS_OK;
   227   }
   228   else {
   229     uint32_t flags;
   230     if (aCreateNew) {
   231       MOZ_ASSERT(!aCallback);
   232       flags = CacheFileIOManager::CREATE_NEW;
   234       // make sure we can use this entry immediately
   235       mMetadata = new CacheFileMetadata(mOpenAsMemoryOnly, mKey);
   236       mReady = true;
   237       mDataSize = mMetadata->Offset();
   238     }
   239     else {
   240       flags = CacheFileIOManager::CREATE;
   242       // Have a look into index and change to CREATE_NEW when we are sure
   243       // that the entry does not exist.
   244       CacheIndex::EntryStatus status;
   245       rv = CacheIndex::HasEntry(mKey, &status);
   246       if (status == CacheIndex::DOES_NOT_EXIST) {
   247         LOG(("CacheFile::Init() - Forcing CREATE_NEW flag since we don't have"
   248              " this entry according to index"));
   249         flags = CacheFileIOManager::CREATE_NEW;
   251         // make sure we can use this entry immediately
   252         mMetadata = new CacheFileMetadata(mOpenAsMemoryOnly, mKey);
   253         mReady = true;
   254         mDataSize = mMetadata->Offset();
   256         // Notify callback now and don't store it in mListener, no further
   257         // operation can change the result.
   258         nsRefPtr<NotifyCacheFileListenerEvent> ev;
   259         ev = new NotifyCacheFileListenerEvent(aCallback, NS_OK, true);
   260         rv = NS_DispatchToCurrentThread(ev);
   261         NS_ENSURE_SUCCESS(rv, rv);
   263         aCallback = nullptr;
   264       }
   265     }
   267     if (aPriority)
   268       flags |= CacheFileIOManager::PRIORITY;
   270     mOpeningFile = true;
   271     mListener = aCallback;
   272     rv = CacheFileIOManager::OpenFile(mKey, flags, true, this);
   273     if (NS_FAILED(rv)) {
   274       mListener = nullptr;
   275       mOpeningFile = false;
   277       if (aCreateNew) {
   278         NS_WARNING("Forcing memory-only entry since OpenFile failed");
   279         LOG(("CacheFile::Init() - CacheFileIOManager::OpenFile() failed "
   280              "synchronously. We can continue in memory-only mode since "
   281              "aCreateNew == true. [this=%p]", this));
   283         mMemoryOnly = true;
   284       }
   285       else if (rv == NS_ERROR_NOT_INITIALIZED) {
   286         NS_WARNING("Forcing memory-only entry since CacheIOManager isn't "
   287                    "initialized.");
   288         LOG(("CacheFile::Init() - CacheFileIOManager isn't initialized, "
   289              "initializing entry as memory-only. [this=%p]", this));
   291         mMemoryOnly = true;
   292         mMetadata = new CacheFileMetadata(mOpenAsMemoryOnly, mKey);
   293         mReady = true;
   294         mDataSize = mMetadata->Offset();
   296         nsRefPtr<NotifyCacheFileListenerEvent> ev;
   297         ev = new NotifyCacheFileListenerEvent(aCallback, NS_OK, true);
   298         rv = NS_DispatchToCurrentThread(ev);
   299         NS_ENSURE_SUCCESS(rv, rv);
   300       }
   301       else {
   302         NS_ENSURE_SUCCESS(rv, rv);
   303       }
   304     }
   305   }
   307   return NS_OK;
   308 }
   310 nsresult
   311 CacheFile::OnChunkRead(nsresult aResult, CacheFileChunk *aChunk)
   312 {
   313   CacheFileAutoLock lock(this);
   315   nsresult rv;
   317   uint32_t index = aChunk->Index();
   319   LOG(("CacheFile::OnChunkRead() [this=%p, rv=0x%08x, chunk=%p, idx=%d]",
   320        this, aResult, aChunk, index));
   322   if (NS_FAILED(aResult)) {
   323     SetError(aResult);
   324     CacheFileIOManager::DoomFile(mHandle, nullptr);
   325   }
   327   if (HaveChunkListeners(index)) {
   328     rv = NotifyChunkListeners(index, aResult, aChunk);
   329     NS_ENSURE_SUCCESS(rv, rv);
   330   }
   332   return NS_OK;
   333 }
   335 nsresult
   336 CacheFile::OnChunkWritten(nsresult aResult, CacheFileChunk *aChunk)
   337 {
   338   CacheFileAutoLock lock(this);
   340   nsresult rv;
   342   LOG(("CacheFile::OnChunkWritten() [this=%p, rv=0x%08x, chunk=%p, idx=%d]",
   343        this, aResult, aChunk, aChunk->Index()));
   345   MOZ_ASSERT(!mMemoryOnly);
   346   MOZ_ASSERT(!mOpeningFile);
   347   MOZ_ASSERT(mHandle);
   349   if (NS_FAILED(aResult)) {
   350     SetError(aResult);
   351     CacheFileIOManager::DoomFile(mHandle, nullptr);
   352   }
   354   if (NS_SUCCEEDED(aResult) && !aChunk->IsDirty()) {
   355     // update hash value in metadata
   356     mMetadata->SetHash(aChunk->Index(), aChunk->Hash());
   357   }
   359   // notify listeners if there is any
   360   if (HaveChunkListeners(aChunk->Index())) {
   361     // don't release the chunk since there are some listeners queued
   362     rv = NotifyChunkListeners(aChunk->Index(), aResult, aChunk);
   363     if (NS_SUCCEEDED(rv)) {
   364       MOZ_ASSERT(aChunk->mRefCnt != 2);
   365       return NS_OK;
   366     }
   367   }
   369   if (aChunk->mRefCnt != 2) {
   370     LOG(("CacheFile::OnChunkWritten() - Chunk is still used [this=%p, chunk=%p,"
   371          " refcnt=%d]", this, aChunk, aChunk->mRefCnt.get()));
   373     return NS_OK;
   374   }
   376 #ifdef CACHE_CHUNKS
   377   if (NS_SUCCEEDED(aResult)) {
   378     LOG(("CacheFile::OnChunkWritten() - Caching unused chunk [this=%p, "
   379          "chunk=%p]", this, aChunk));
   380   } else {
   381     LOG(("CacheFile::OnChunkWritten() - Removing failed chunk [this=%p, "
   382          "chunk=%p]", this, aChunk));
   383   }
   384 #else
   385   LOG(("CacheFile::OnChunkWritten() - Releasing %s chunk [this=%p, chunk=%p]",
   386        NS_SUCCEEDED(aResult) ? "unused" : "failed", this, aChunk));
   387 #endif
   389   RemoveChunkInternal(aChunk,
   390 #ifdef CACHE_CHUNKS
   391                       NS_SUCCEEDED(aResult));
   392 #else
   393                       false);
   394 #endif
   396   WriteMetadataIfNeededLocked();
   398   return NS_OK;
   399 }
   401 nsresult
   402 CacheFile::OnChunkAvailable(nsresult aResult, uint32_t aChunkIdx,
   403                             CacheFileChunk *aChunk)
   404 {
   405   MOZ_CRASH("CacheFile::OnChunkAvailable should not be called!");
   406   return NS_ERROR_UNEXPECTED;
   407 }
   409 nsresult
   410 CacheFile::OnChunkUpdated(CacheFileChunk *aChunk)
   411 {
   412   MOZ_CRASH("CacheFile::OnChunkUpdated should not be called!");
   413   return NS_ERROR_UNEXPECTED;
   414 }
   416 nsresult
   417 CacheFile::OnFileOpened(CacheFileHandle *aHandle, nsresult aResult)
   418 {
   419   nsresult rv;
   421   // Using an 'auto' class to perform doom or fail the listener
   422   // outside the CacheFile's lock.
   423   class AutoFailDoomListener
   424   {
   425   public:
   426     AutoFailDoomListener(CacheFileHandle *aHandle)
   427       : mHandle(aHandle)
   428       , mAlreadyDoomed(false)
   429     {}
   430     ~AutoFailDoomListener()
   431     {
   432       if (!mListener)
   433         return;
   435       if (mHandle) {
   436         if (mAlreadyDoomed) {
   437           mListener->OnFileDoomed(mHandle, NS_OK);
   438         } else {
   439           CacheFileIOManager::DoomFile(mHandle, mListener);
   440         }
   441       } else {
   442         mListener->OnFileDoomed(nullptr, NS_ERROR_NOT_AVAILABLE);
   443       }
   444     }
   446     CacheFileHandle* mHandle;
   447     nsCOMPtr<CacheFileIOListener> mListener;
   448     bool mAlreadyDoomed;
   449   } autoDoom(aHandle);
   451   nsCOMPtr<CacheFileListener> listener;
   452   bool isNew = false;
   453   nsresult retval = NS_OK;
   455   {
   456     CacheFileAutoLock lock(this);
   458     MOZ_ASSERT(mOpeningFile);
   459     MOZ_ASSERT((NS_SUCCEEDED(aResult) && aHandle) ||
   460                (NS_FAILED(aResult) && !aHandle));
   461     MOZ_ASSERT((mListener && !mMetadata) || // !createNew
   462                (!mListener && mMetadata));  // createNew
   463     MOZ_ASSERT(!mMemoryOnly || mMetadata); // memory-only was set on new entry
   465     LOG(("CacheFile::OnFileOpened() [this=%p, rv=0x%08x, handle=%p]",
   466          this, aResult, aHandle));
   468     mOpeningFile = false;
   470     autoDoom.mListener.swap(mDoomAfterOpenListener);
   472     if (mMemoryOnly) {
   473       // We can be here only in case the entry was initilized as createNew and
   474       // SetMemoryOnly() was called.
   476       // Just don't store the handle into mHandle and exit
   477       autoDoom.mAlreadyDoomed = true;
   478       return NS_OK;
   479     }
   480     else if (NS_FAILED(aResult)) {
   481       if (mMetadata) {
   482         // This entry was initialized as createNew, just switch to memory-only
   483         // mode.
   484         NS_WARNING("Forcing memory-only entry since OpenFile failed");
   485         LOG(("CacheFile::OnFileOpened() - CacheFileIOManager::OpenFile() "
   486              "failed asynchronously. We can continue in memory-only mode since "
   487              "aCreateNew == true. [this=%p]", this));
   489         mMemoryOnly = true;
   490         return NS_OK;
   491       }
   492       else if (aResult == NS_ERROR_FILE_INVALID_PATH) {
   493         // CacheFileIOManager doesn't have mCacheDirectory, switch to
   494         // memory-only mode.
   495         NS_WARNING("Forcing memory-only entry since CacheFileIOManager doesn't "
   496                    "have mCacheDirectory.");
   497         LOG(("CacheFile::OnFileOpened() - CacheFileIOManager doesn't have "
   498              "mCacheDirectory, initializing entry as memory-only. [this=%p]",
   499              this));
   501         mMemoryOnly = true;
   502         mMetadata = new CacheFileMetadata(mOpenAsMemoryOnly, mKey);
   503         mReady = true;
   504         mDataSize = mMetadata->Offset();
   506         isNew = true;
   507         retval = NS_OK;
   508       }
   509       else {
   510         // CacheFileIOManager::OpenFile() failed for another reason.
   511         isNew = false;
   512         retval = aResult;
   513       }
   515       mListener.swap(listener);
   516     }
   517     else {
   518       mHandle = aHandle;
   520       if (mMetadata) {
   521         InitIndexEntry();
   523         // The entry was initialized as createNew, don't try to read metadata.
   524         mMetadata->SetHandle(mHandle);
   526         // Write all cached chunks, otherwise they may stay unwritten.
   527         mCachedChunks.Enumerate(&CacheFile::WriteAllCachedChunks, this);
   529         return NS_OK;
   530       }
   531     }
   532   }
   534   if (listener) {
   535     listener->OnFileReady(retval, isNew);
   536     return NS_OK;
   537   }
   539   MOZ_ASSERT(NS_SUCCEEDED(aResult));
   540   MOZ_ASSERT(!mMetadata);
   541   MOZ_ASSERT(mListener);
   543   mMetadata = new CacheFileMetadata(mHandle, mKey);
   545   rv = mMetadata->ReadMetadata(this);
   546   if (NS_FAILED(rv)) {
   547     mListener.swap(listener);
   548     listener->OnFileReady(rv, false);
   549   }
   551   return NS_OK;
   552 }
   554 nsresult
   555 CacheFile::OnDataWritten(CacheFileHandle *aHandle, const char *aBuf,
   556                          nsresult aResult)
   557 {
   558   MOZ_CRASH("CacheFile::OnDataWritten should not be called!");
   559   return NS_ERROR_UNEXPECTED;
   560 }
   562 nsresult
   563 CacheFile::OnDataRead(CacheFileHandle *aHandle, char *aBuf, nsresult aResult)
   564 {
   565   MOZ_CRASH("CacheFile::OnDataRead should not be called!");
   566   return NS_ERROR_UNEXPECTED;
   567 }
   569 nsresult
   570 CacheFile::OnMetadataRead(nsresult aResult)
   571 {
   572   MOZ_ASSERT(mListener);
   574   LOG(("CacheFile::OnMetadataRead() [this=%p, rv=0x%08x]", this, aResult));
   576   bool isNew = false;
   577   if (NS_SUCCEEDED(aResult)) {
   578     mReady = true;
   579     mDataSize = mMetadata->Offset();
   580     if (mDataSize == 0 && mMetadata->ElementsSize() == 0) {
   581       isNew = true;
   582       mMetadata->MarkDirty();
   583     }
   585     InitIndexEntry();
   586   }
   588   nsCOMPtr<CacheFileListener> listener;
   589   mListener.swap(listener);
   590   listener->OnFileReady(aResult, isNew);
   591   return NS_OK;
   592 }
   594 nsresult
   595 CacheFile::OnMetadataWritten(nsresult aResult)
   596 {
   597   CacheFileAutoLock lock(this);
   599   LOG(("CacheFile::OnMetadataWritten() [this=%p, rv=0x%08x]", this, aResult));
   601   MOZ_ASSERT(mWritingMetadata);
   602   mWritingMetadata = false;
   604   MOZ_ASSERT(!mMemoryOnly);
   605   MOZ_ASSERT(!mOpeningFile);
   607   if (NS_FAILED(aResult)) {
   608     // TODO close streams with an error ???
   609   }
   611   if (mOutput || mInputs.Length() || mChunks.Count())
   612     return NS_OK;
   614   if (IsDirty())
   615     WriteMetadataIfNeededLocked();
   617   if (!mWritingMetadata) {
   618     LOG(("CacheFile::OnMetadataWritten() - Releasing file handle [this=%p]",
   619          this));
   620     CacheFileIOManager::ReleaseNSPRHandle(mHandle);
   621   }
   623   return NS_OK;
   624 }
   626 nsresult
   627 CacheFile::OnFileDoomed(CacheFileHandle *aHandle, nsresult aResult)
   628 {
   629   nsCOMPtr<CacheFileListener> listener;
   631   {
   632     CacheFileAutoLock lock(this);
   634     MOZ_ASSERT(mListener);
   636     LOG(("CacheFile::OnFileDoomed() [this=%p, rv=0x%08x, handle=%p]",
   637          this, aResult, aHandle));
   639     mListener.swap(listener);
   640   }
   642   listener->OnFileDoomed(aResult);
   643   return NS_OK;
   644 }
   646 nsresult
   647 CacheFile::OnEOFSet(CacheFileHandle *aHandle, nsresult aResult)
   648 {
   649   MOZ_CRASH("CacheFile::OnEOFSet should not be called!");
   650   return NS_ERROR_UNEXPECTED;
   651 }
   653 nsresult
   654 CacheFile::OnFileRenamed(CacheFileHandle *aHandle, nsresult aResult)
   655 {
   656   MOZ_CRASH("CacheFile::OnFileRenamed should not be called!");
   657   return NS_ERROR_UNEXPECTED;
   658 }
   660 nsresult
   661 CacheFile::OpenInputStream(nsIInputStream **_retval)
   662 {
   663   CacheFileAutoLock lock(this);
   665   MOZ_ASSERT(mHandle || mMemoryOnly || mOpeningFile);
   667   if (!mReady) {
   668     LOG(("CacheFile::OpenInputStream() - CacheFile is not ready [this=%p]",
   669          this));
   671     return NS_ERROR_NOT_AVAILABLE;
   672   }
   674   CacheFileInputStream *input = new CacheFileInputStream(this);
   676   LOG(("CacheFile::OpenInputStream() - Creating new input stream %p [this=%p]",
   677        input, this));
   679   mInputs.AppendElement(input);
   680   NS_ADDREF(input);
   682   mDataAccessed = true;
   683   NS_ADDREF(*_retval = input);
   684   return NS_OK;
   685 }
   687 nsresult
   688 CacheFile::OpenOutputStream(CacheOutputCloseListener *aCloseListener, nsIOutputStream **_retval)
   689 {
   690   CacheFileAutoLock lock(this);
   692   MOZ_ASSERT(mHandle || mMemoryOnly || mOpeningFile);
   694   if (!mReady) {
   695     LOG(("CacheFile::OpenOutputStream() - CacheFile is not ready [this=%p]",
   696          this));
   698     return NS_ERROR_NOT_AVAILABLE;
   699   }
   701   if (mOutput) {
   702     LOG(("CacheFile::OpenOutputStream() - We already have output stream %p "
   703          "[this=%p]", mOutput, this));
   705     return NS_ERROR_NOT_AVAILABLE;
   706   }
   708   mOutput = new CacheFileOutputStream(this, aCloseListener);
   710   LOG(("CacheFile::OpenOutputStream() - Creating new output stream %p "
   711        "[this=%p]", mOutput, this));
   713   mDataAccessed = true;
   714   NS_ADDREF(*_retval = mOutput);
   715   return NS_OK;
   716 }
   718 nsresult
   719 CacheFile::SetMemoryOnly()
   720 {
   721   LOG(("CacheFile::SetMemoryOnly() mMemoryOnly=%d [this=%p]",
   722        mMemoryOnly, this));
   724   if (mMemoryOnly)
   725     return NS_OK;
   727   MOZ_ASSERT(mReady);
   729   if (!mReady) {
   730     LOG(("CacheFile::SetMemoryOnly() - CacheFile is not ready [this=%p]",
   731          this));
   733     return NS_ERROR_NOT_AVAILABLE;
   734   }
   736   if (mDataAccessed) {
   737     LOG(("CacheFile::SetMemoryOnly() - Data was already accessed [this=%p]", this));
   738     return NS_ERROR_NOT_AVAILABLE;
   739   }
   741   // TODO what to do when this isn't a new entry and has an existing metadata???
   742   mMemoryOnly = true;
   743   return NS_OK;
   744 }
   746 nsresult
   747 CacheFile::Doom(CacheFileListener *aCallback)
   748 {
   749   CacheFileAutoLock lock(this);
   751   MOZ_ASSERT(mHandle || mMemoryOnly || mOpeningFile);
   753   LOG(("CacheFile::Doom() [this=%p, listener=%p]", this, aCallback));
   755   nsresult rv = NS_OK;
   757   if (mMemoryOnly) {
   758     return NS_ERROR_FILE_NOT_FOUND;
   759   }
   761   if (mHandle && mHandle->IsDoomed()) {
   762     return NS_ERROR_FILE_NOT_FOUND;
   763   }
   765   nsCOMPtr<CacheFileIOListener> listener;
   766   if (aCallback || !mHandle) {
   767     listener = new DoomFileHelper(aCallback);
   768   }
   769   if (mHandle) {
   770     rv = CacheFileIOManager::DoomFile(mHandle, listener);
   771   } else if (mOpeningFile) {
   772     mDoomAfterOpenListener = listener;
   773   }
   775   return rv;
   776 }
   778 nsresult
   779 CacheFile::ThrowMemoryCachedData()
   780 {
   781   CacheFileAutoLock lock(this);
   783   LOG(("CacheFile::ThrowMemoryCachedData() [this=%p]", this));
   785   if (mMemoryOnly) {
   786     // This method should not be called when the CacheFile was initialized as
   787     // memory-only, but it can be called when CacheFile end up as memory-only
   788     // due to e.g. IO failure since CacheEntry doesn't know it.
   789     LOG(("CacheFile::ThrowMemoryCachedData() - Ignoring request because the "
   790          "entry is memory-only. [this=%p]", this));
   792     return NS_ERROR_NOT_AVAILABLE;
   793   }
   795   if (mOpeningFile) {
   796     // mayhemer, note: we shouldn't get here, since CacheEntry prevents loading
   797     // entries from being purged.
   799     LOG(("CacheFile::ThrowMemoryCachedData() - Ignoring request because the "
   800          "entry is still opening the file [this=%p]", this));
   802     return NS_ERROR_ABORT;
   803   }
   805 #ifdef CACHE_CHUNKS
   806   mCachedChunks.Clear();
   807 #else
   808   // If we don't cache all chunks, mCachedChunks must be empty.
   809   MOZ_ASSERT(mCachedChunks.Count() == 0);
   810 #endif
   812   return NS_OK;
   813 }
   815 nsresult
   816 CacheFile::GetElement(const char *aKey, char **_retval)
   817 {
   818   CacheFileAutoLock lock(this);
   819   MOZ_ASSERT(mMetadata);
   820   NS_ENSURE_TRUE(mMetadata, NS_ERROR_UNEXPECTED);
   822   const char *value;
   823   value = mMetadata->GetElement(aKey);
   824   if (!value)
   825     return NS_ERROR_NOT_AVAILABLE;
   827   *_retval = NS_strdup(value);
   828   return NS_OK;
   829 }
   831 nsresult
   832 CacheFile::SetElement(const char *aKey, const char *aValue)
   833 {
   834   CacheFileAutoLock lock(this);
   835   MOZ_ASSERT(mMetadata);
   836   NS_ENSURE_TRUE(mMetadata, NS_ERROR_UNEXPECTED);
   838   PostWriteTimer();
   839   return mMetadata->SetElement(aKey, aValue);
   840 }
   842 nsresult
   843 CacheFile::ElementsSize(uint32_t *_retval)
   844 {
   845   CacheFileAutoLock lock(this);
   847   if (!mMetadata)
   848     return NS_ERROR_NOT_AVAILABLE;
   850   *_retval = mMetadata->ElementsSize();
   851   return NS_OK;
   852 }
   854 nsresult
   855 CacheFile::SetExpirationTime(uint32_t aExpirationTime)
   856 {
   857   CacheFileAutoLock lock(this);
   858   MOZ_ASSERT(mMetadata);
   859   NS_ENSURE_TRUE(mMetadata, NS_ERROR_UNEXPECTED);
   861   PostWriteTimer();
   863   if (mHandle && !mHandle->IsDoomed())
   864     CacheFileIOManager::UpdateIndexEntry(mHandle, nullptr, &aExpirationTime);
   866   return mMetadata->SetExpirationTime(aExpirationTime);
   867 }
   869 nsresult
   870 CacheFile::GetExpirationTime(uint32_t *_retval)
   871 {
   872   CacheFileAutoLock lock(this);
   873   MOZ_ASSERT(mMetadata);
   874   NS_ENSURE_TRUE(mMetadata, NS_ERROR_UNEXPECTED);
   876   return mMetadata->GetExpirationTime(_retval);
   877 }
   879 nsresult
   880 CacheFile::SetLastModified(uint32_t aLastModified)
   881 {
   882   CacheFileAutoLock lock(this);
   883   MOZ_ASSERT(mMetadata);
   884   NS_ENSURE_TRUE(mMetadata, NS_ERROR_UNEXPECTED);
   886   PostWriteTimer();
   887   return mMetadata->SetLastModified(aLastModified);
   888 }
   890 nsresult
   891 CacheFile::GetLastModified(uint32_t *_retval)
   892 {
   893   CacheFileAutoLock lock(this);
   894   MOZ_ASSERT(mMetadata);
   895   NS_ENSURE_TRUE(mMetadata, NS_ERROR_UNEXPECTED);
   897   return mMetadata->GetLastModified(_retval);
   898 }
   900 nsresult
   901 CacheFile::SetFrecency(uint32_t aFrecency)
   902 {
   903   CacheFileAutoLock lock(this);
   904   MOZ_ASSERT(mMetadata);
   905   NS_ENSURE_TRUE(mMetadata, NS_ERROR_UNEXPECTED);
   907   PostWriteTimer();
   909   if (mHandle && !mHandle->IsDoomed())
   910     CacheFileIOManager::UpdateIndexEntry(mHandle, &aFrecency, nullptr);
   912   return mMetadata->SetFrecency(aFrecency);
   913 }
   915 nsresult
   916 CacheFile::GetFrecency(uint32_t *_retval)
   917 {
   918   CacheFileAutoLock lock(this);
   919   MOZ_ASSERT(mMetadata);
   920   NS_ENSURE_TRUE(mMetadata, NS_ERROR_UNEXPECTED);
   922   return mMetadata->GetFrecency(_retval);
   923 }
   925 nsresult
   926 CacheFile::GetLastFetched(uint32_t *_retval)
   927 {
   928   CacheFileAutoLock lock(this);
   929   MOZ_ASSERT(mMetadata);
   930   NS_ENSURE_TRUE(mMetadata, NS_ERROR_UNEXPECTED);
   932   return mMetadata->GetLastFetched(_retval);
   933 }
   935 nsresult
   936 CacheFile::GetFetchCount(uint32_t *_retval)
   937 {
   938   CacheFileAutoLock lock(this);
   939   MOZ_ASSERT(mMetadata);
   940   NS_ENSURE_TRUE(mMetadata, NS_ERROR_UNEXPECTED);
   942   return mMetadata->GetFetchCount(_retval);
   943 }
   945 void
   946 CacheFile::Lock()
   947 {
   948   mLock.Lock();
   949 }
   951 void
   952 CacheFile::Unlock()
   953 {
   954   nsTArray<nsISupports*> objs;
   955   objs.SwapElements(mObjsToRelease);
   957   mLock.Unlock();
   959   for (uint32_t i = 0; i < objs.Length(); i++)
   960     objs[i]->Release();
   961 }
   963 void
   964 CacheFile::AssertOwnsLock() const
   965 {
   966   mLock.AssertCurrentThreadOwns();
   967 }
   969 void
   970 CacheFile::ReleaseOutsideLock(nsISupports *aObject)
   971 {
   972   AssertOwnsLock();
   974   mObjsToRelease.AppendElement(aObject);
   975 }
   977 nsresult
   978 CacheFile::GetChunk(uint32_t aIndex, bool aWriter,
   979                     CacheFileChunkListener *aCallback, CacheFileChunk **_retval)
   980 {
   981   CacheFileAutoLock lock(this);
   982   return GetChunkLocked(aIndex, aWriter, aCallback, _retval);
   983 }
   985 nsresult
   986 CacheFile::GetChunkLocked(uint32_t aIndex, bool aWriter,
   987                           CacheFileChunkListener *aCallback,
   988                           CacheFileChunk **_retval)
   989 {
   990   AssertOwnsLock();
   992   LOG(("CacheFile::GetChunkLocked() [this=%p, idx=%d, writer=%d, listener=%p]",
   993        this, aIndex, aWriter, aCallback));
   995   MOZ_ASSERT(mReady);
   996   MOZ_ASSERT(mHandle || mMemoryOnly || mOpeningFile);
   997   MOZ_ASSERT((aWriter && !aCallback) || (!aWriter && aCallback));
   999   nsresult rv;
  1001   nsRefPtr<CacheFileChunk> chunk;
  1002   if (mChunks.Get(aIndex, getter_AddRefs(chunk))) {
  1003     LOG(("CacheFile::GetChunkLocked() - Found chunk %p in mChunks [this=%p]",
  1004          chunk.get(), this));
  1006     // We might get failed chunk between releasing the lock in
  1007     // CacheFileChunk::OnDataWritten/Read and CacheFile::OnChunkWritten/Read
  1008     rv = chunk->GetStatus();
  1009     if (NS_FAILED(rv)) {
  1010       SetError(rv);
  1011       LOG(("CacheFile::GetChunkLocked() - Found failed chunk in mChunks "
  1012            "[this=%p]", this));
  1013       return rv;
  1016     if (chunk->IsReady() || aWriter) {
  1017       chunk.swap(*_retval);
  1019     else {
  1020       rv = QueueChunkListener(aIndex, aCallback);
  1021       NS_ENSURE_SUCCESS(rv, rv);
  1024     return NS_OK;
  1027   if (mCachedChunks.Get(aIndex, getter_AddRefs(chunk))) {
  1028 #ifndef CACHE_CHUNKS
  1029     // We don't cache all chunks, so we must not have handle and we must be
  1030     // either waiting for the handle, or this is memory-only entry.
  1031     MOZ_ASSERT(!mHandle && (mMemoryOnly || mOpeningFile));
  1032 #endif
  1033     LOG(("CacheFile::GetChunkLocked() - Reusing cached chunk %p [this=%p]",
  1034          chunk.get(), this));
  1036     mChunks.Put(aIndex, chunk);
  1037     mCachedChunks.Remove(aIndex);
  1038     chunk->mFile = this;
  1039     chunk->mRemovingChunk = false;
  1041     MOZ_ASSERT(chunk->IsReady());
  1043     chunk.swap(*_retval);
  1044     return NS_OK;
  1047   int64_t off = aIndex * kChunkSize;
  1049   if (off < mDataSize) {
  1050     // We cannot be here if this is memory only entry since the chunk must exist
  1051     MOZ_ASSERT(!mMemoryOnly);
  1052     if (mMemoryOnly) {
  1053       // If this ever really happen it is better to fail rather than crashing on
  1054       // a null handle.
  1055       LOG(("CacheFile::GetChunkLocked() - Unexpected state! Offset < mDataSize "
  1056            "for memory-only entry. [this=%p, off=%lld, mDataSize=%lld]",
  1057            this, off, mDataSize));
  1059       return NS_ERROR_UNEXPECTED;
  1062     chunk = new CacheFileChunk(this, aIndex);
  1063     mChunks.Put(aIndex, chunk);
  1065     LOG(("CacheFile::GetChunkLocked() - Reading newly created chunk %p from "
  1066          "the disk [this=%p]", chunk.get(), this));
  1068     // Read the chunk from the disk
  1069     rv = chunk->Read(mHandle, std::min(static_cast<uint32_t>(mDataSize - off),
  1070                      static_cast<uint32_t>(kChunkSize)),
  1071                      mMetadata->GetHash(aIndex), this);
  1072     if (NS_WARN_IF(NS_FAILED(rv))) {
  1073       RemoveChunkInternal(chunk, false);
  1074       return rv;
  1077     if (aWriter) {
  1078       chunk.swap(*_retval);
  1080     else {
  1081       rv = QueueChunkListener(aIndex, aCallback);
  1082       NS_ENSURE_SUCCESS(rv, rv);
  1085     return NS_OK;
  1087   else if (off == mDataSize) {
  1088     if (aWriter) {
  1089       // this listener is going to write to the chunk
  1090       chunk = new CacheFileChunk(this, aIndex);
  1091       mChunks.Put(aIndex, chunk);
  1093       LOG(("CacheFile::GetChunkLocked() - Created new empty chunk %p [this=%p]",
  1094            chunk.get(), this));
  1096       chunk->InitNew(this);
  1097       mMetadata->SetHash(aIndex, chunk->Hash());
  1099       if (HaveChunkListeners(aIndex)) {
  1100         rv = NotifyChunkListeners(aIndex, NS_OK, chunk);
  1101         NS_ENSURE_SUCCESS(rv, rv);
  1104       chunk.swap(*_retval);
  1105       return NS_OK;
  1108   else {
  1109     if (aWriter) {
  1110       // this chunk was requested by writer, but we need to fill the gap first
  1112       // Fill with zero the last chunk if it is incomplete
  1113       if (mDataSize % kChunkSize) {
  1114         rv = PadChunkWithZeroes(mDataSize / kChunkSize);
  1115         NS_ENSURE_SUCCESS(rv, rv);
  1117         MOZ_ASSERT(!(mDataSize % kChunkSize));
  1120       uint32_t startChunk = mDataSize / kChunkSize;
  1122       if (mMemoryOnly) {
  1123         // We need to create all missing CacheFileChunks if this is memory-only
  1124         // entry
  1125         for (uint32_t i = startChunk ; i < aIndex ; i++) {
  1126           rv = PadChunkWithZeroes(i);
  1127           NS_ENSURE_SUCCESS(rv, rv);
  1130       else {
  1131         // We don't need to create CacheFileChunk for other empty chunks unless
  1132         // there is some input stream waiting for this chunk.
  1134         if (startChunk != aIndex) {
  1135           // Make sure the file contains zeroes at the end of the file
  1136           rv = CacheFileIOManager::TruncateSeekSetEOF(mHandle,
  1137                                                       startChunk * kChunkSize,
  1138                                                       aIndex * kChunkSize,
  1139                                                       nullptr);
  1140           NS_ENSURE_SUCCESS(rv, rv);
  1143         for (uint32_t i = startChunk ; i < aIndex ; i++) {
  1144           if (HaveChunkListeners(i)) {
  1145             rv = PadChunkWithZeroes(i);
  1146             NS_ENSURE_SUCCESS(rv, rv);
  1148           else {
  1149             mMetadata->SetHash(i, kEmptyChunkHash);
  1150             mDataSize = (i + 1) * kChunkSize;
  1155       MOZ_ASSERT(mDataSize == off);
  1156       rv = GetChunkLocked(aIndex, true, nullptr, getter_AddRefs(chunk));
  1157       NS_ENSURE_SUCCESS(rv, rv);
  1159       chunk.swap(*_retval);
  1160       return NS_OK;
  1164   if (mOutput) {
  1165     // the chunk doesn't exist but mOutput may create it
  1166     rv = QueueChunkListener(aIndex, aCallback);
  1167     NS_ENSURE_SUCCESS(rv, rv);
  1169   else {
  1170     return NS_ERROR_NOT_AVAILABLE;
  1173   return NS_OK;
  1176 nsresult
  1177 CacheFile::RemoveChunk(CacheFileChunk *aChunk)
  1179   nsresult rv;
  1181   // Avoid lock reentrancy by increasing the RefCnt
  1182   nsRefPtr<CacheFileChunk> chunk = aChunk;
  1185     CacheFileAutoLock lock(this);
  1187     LOG(("CacheFile::RemoveChunk() [this=%p, chunk=%p, idx=%d]",
  1188          this, aChunk, aChunk->Index()));
  1190     MOZ_ASSERT(mReady);
  1191     MOZ_ASSERT((mHandle && !mMemoryOnly && !mOpeningFile) ||
  1192                (!mHandle && mMemoryOnly && !mOpeningFile) ||
  1193                (!mHandle && !mMemoryOnly && mOpeningFile));
  1195     if (aChunk->mRefCnt != 2) {
  1196       LOG(("CacheFile::RemoveChunk() - Chunk is still used [this=%p, chunk=%p, "
  1197            "refcnt=%d]", this, aChunk, aChunk->mRefCnt.get()));
  1199       // somebody got the reference before the lock was acquired
  1200       return NS_OK;
  1203 #ifdef DEBUG
  1205       // We can be here iff the chunk is in the hash table
  1206       nsRefPtr<CacheFileChunk> chunkCheck;
  1207       mChunks.Get(chunk->Index(), getter_AddRefs(chunkCheck));
  1208       MOZ_ASSERT(chunkCheck == chunk);
  1210       // We also shouldn't have any queued listener for this chunk
  1211       ChunkListeners *listeners;
  1212       mChunkListeners.Get(chunk->Index(), &listeners);
  1213       MOZ_ASSERT(!listeners);
  1215 #endif
  1217     if (NS_FAILED(mStatus)) {
  1218       // Don't write any chunk to disk since this entry will be doomed
  1219       LOG(("CacheFile::RemoveChunk() - Removing chunk because of status "
  1220            "[this=%p, chunk=%p, mStatus=0x%08x]", this, chunk.get(), mStatus));
  1222       RemoveChunkInternal(chunk, false);
  1223       return mStatus;
  1226     if (chunk->IsDirty() && !mMemoryOnly && !mOpeningFile) {
  1227       LOG(("CacheFile::RemoveChunk() - Writing dirty chunk to the disk "
  1228            "[this=%p]", this));
  1230       mDataIsDirty = true;
  1232       rv = chunk->Write(mHandle, this);
  1233       if (NS_FAILED(rv)) {
  1234         LOG(("CacheFile::RemoveChunk() - CacheFileChunk::Write() failed "
  1235              "synchronously. Removing it. [this=%p, chunk=%p, rv=0x%08x]",
  1236              this, chunk.get(), rv));
  1238         RemoveChunkInternal(chunk, false);
  1240         SetError(rv);
  1241         CacheFileIOManager::DoomFile(mHandle, nullptr);
  1242         return rv;
  1244       else {
  1245         // Chunk will be removed in OnChunkWritten if it is still unused
  1247         // chunk needs to be released under the lock to be able to rely on
  1248         // CacheFileChunk::mRefCnt in CacheFile::OnChunkWritten()
  1249         chunk = nullptr;
  1250         return NS_OK;
  1254 #ifdef CACHE_CHUNKS
  1255     LOG(("CacheFile::RemoveChunk() - Caching unused chunk [this=%p, chunk=%p]",
  1256          this, chunk.get()));
  1257 #else
  1258     if (mMemoryOnly || mOpeningFile) {
  1259       LOG(("CacheFile::RemoveChunk() - Caching unused chunk [this=%p, chunk=%p,"
  1260            " reason=%s]", this, chunk.get(),
  1261            mMemoryOnly ? "memory-only" : "opening-file"));
  1262     } else {
  1263       LOG(("CacheFile::RemoveChunk() - Releasing unused chunk [this=%p, "
  1264            "chunk=%p]", this, chunk.get()));
  1266 #endif
  1268     RemoveChunkInternal(chunk,
  1269 #ifdef CACHE_CHUNKS
  1270                         true);
  1271 #else
  1272                         // Cache the chunk only when we have a reason to do so
  1273                         mMemoryOnly || mOpeningFile);
  1274 #endif
  1276     if (!mMemoryOnly)
  1277       WriteMetadataIfNeededLocked();
  1280   return NS_OK;
  1283 void
  1284 CacheFile::RemoveChunkInternal(CacheFileChunk *aChunk, bool aCacheChunk)
  1286   aChunk->mRemovingChunk = true;
  1287   ReleaseOutsideLock(static_cast<CacheFileChunkListener *>(
  1288                        aChunk->mFile.forget().take()));
  1290   if (aCacheChunk) {
  1291     mCachedChunks.Put(aChunk->Index(), aChunk);
  1294   mChunks.Remove(aChunk->Index());
  1297 nsresult
  1298 CacheFile::RemoveInput(CacheFileInputStream *aInput)
  1300   CacheFileAutoLock lock(this);
  1302   LOG(("CacheFile::RemoveInput() [this=%p, input=%p]", this, aInput));
  1304   DebugOnly<bool> found;
  1305   found = mInputs.RemoveElement(aInput);
  1306   MOZ_ASSERT(found);
  1308   ReleaseOutsideLock(static_cast<nsIInputStream*>(aInput));
  1310   if (!mMemoryOnly)
  1311     WriteMetadataIfNeededLocked();
  1313   return NS_OK;
  1316 nsresult
  1317 CacheFile::RemoveOutput(CacheFileOutputStream *aOutput)
  1319   AssertOwnsLock();
  1321   LOG(("CacheFile::RemoveOutput() [this=%p, output=%p]", this, aOutput));
  1323   if (mOutput != aOutput) {
  1324     LOG(("CacheFile::RemoveOutput() - This output was already removed, ignoring"
  1325          " call [this=%p]", this));
  1326     return NS_OK;
  1329   mOutput = nullptr;
  1331   // Cancel all queued chunk and update listeners that cannot be satisfied
  1332   NotifyListenersAboutOutputRemoval();
  1334   if (!mMemoryOnly)
  1335     WriteMetadataIfNeededLocked();
  1337   // Notify close listener as the last action
  1338   aOutput->NotifyCloseListener();
  1340   return NS_OK;
  1343 nsresult
  1344 CacheFile::NotifyChunkListener(CacheFileChunkListener *aCallback,
  1345                                nsIEventTarget *aTarget,
  1346                                nsresult aResult,
  1347                                uint32_t aChunkIdx,
  1348                                CacheFileChunk *aChunk)
  1350   LOG(("CacheFile::NotifyChunkListener() [this=%p, listener=%p, target=%p, "
  1351        "rv=0x%08x, idx=%d, chunk=%p]", this, aCallback, aTarget, aResult,
  1352        aChunkIdx, aChunk));
  1354   nsresult rv;
  1355   nsRefPtr<NotifyChunkListenerEvent> ev;
  1356   ev = new NotifyChunkListenerEvent(aCallback, aResult, aChunkIdx, aChunk);
  1357   if (aTarget)
  1358     rv = aTarget->Dispatch(ev, NS_DISPATCH_NORMAL);
  1359   else
  1360     rv = NS_DispatchToCurrentThread(ev);
  1361   NS_ENSURE_SUCCESS(rv, rv);
  1363   return NS_OK;
  1366 nsresult
  1367 CacheFile::QueueChunkListener(uint32_t aIndex,
  1368                               CacheFileChunkListener *aCallback)
  1370   LOG(("CacheFile::QueueChunkListener() [this=%p, idx=%d, listener=%p]",
  1371        this, aIndex, aCallback));
  1373   AssertOwnsLock();
  1375   MOZ_ASSERT(aCallback);
  1377   ChunkListenerItem *item = new ChunkListenerItem();
  1378   item->mTarget = NS_GetCurrentThread();
  1379   item->mCallback = aCallback;
  1381   ChunkListeners *listeners;
  1382   if (!mChunkListeners.Get(aIndex, &listeners)) {
  1383     listeners = new ChunkListeners();
  1384     mChunkListeners.Put(aIndex, listeners);
  1387   listeners->mItems.AppendElement(item);
  1388   return NS_OK;
  1391 nsresult
  1392 CacheFile::NotifyChunkListeners(uint32_t aIndex, nsresult aResult,
  1393                                 CacheFileChunk *aChunk)
  1395   LOG(("CacheFile::NotifyChunkListeners() [this=%p, idx=%d, rv=0x%08x, "
  1396        "chunk=%p]", this, aIndex, aResult, aChunk));
  1398   AssertOwnsLock();
  1400   nsresult rv, rv2;
  1402   ChunkListeners *listeners;
  1403   mChunkListeners.Get(aIndex, &listeners);
  1404   MOZ_ASSERT(listeners);
  1406   rv = NS_OK;
  1407   for (uint32_t i = 0 ; i < listeners->mItems.Length() ; i++) {
  1408     ChunkListenerItem *item = listeners->mItems[i];
  1409     rv2 = NotifyChunkListener(item->mCallback, item->mTarget, aResult, aIndex,
  1410                               aChunk);
  1411     if (NS_FAILED(rv2) && NS_SUCCEEDED(rv))
  1412       rv = rv2;
  1413     delete item;
  1416   mChunkListeners.Remove(aIndex);
  1418   return rv;
  1421 bool
  1422 CacheFile::HaveChunkListeners(uint32_t aIndex)
  1424   ChunkListeners *listeners;
  1425   mChunkListeners.Get(aIndex, &listeners);
  1426   return !!listeners;
  1429 void
  1430 CacheFile::NotifyListenersAboutOutputRemoval()
  1432   LOG(("CacheFile::NotifyListenersAboutOutputRemoval() [this=%p]", this));
  1434   AssertOwnsLock();
  1436   // First fail all chunk listeners that wait for non-existent chunk
  1437   mChunkListeners.Enumerate(&CacheFile::FailListenersIfNonExistentChunk,
  1438                             this);
  1440   // Fail all update listeners
  1441   mChunks.Enumerate(&CacheFile::FailUpdateListeners, this);
  1444 bool
  1445 CacheFile::DataSize(int64_t* aSize)
  1447   CacheFileAutoLock lock(this);
  1449   if (mOutput)
  1450     return false;
  1452   *aSize = mDataSize;
  1453   return true;
  1456 bool
  1457 CacheFile::IsDoomed()
  1459   CacheFileAutoLock lock(this);
  1461   if (!mHandle)
  1462     return false;
  1464   return mHandle->IsDoomed();
  1467 bool
  1468 CacheFile::IsWriteInProgress()
  1470   // Returns true when there is a potentially unfinished write operation.
  1471   // Not using lock for performance reasons.  mMetadata is never released
  1472   // during life time of CacheFile.
  1473   return
  1474     mDataIsDirty ||
  1475     (mMetadata && mMetadata->IsDirty()) ||
  1476     mWritingMetadata ||
  1477     mOpeningFile ||
  1478     mOutput ||
  1479     mChunks.Count();
  1482 bool
  1483 CacheFile::IsDirty()
  1485   return mDataIsDirty || mMetadata->IsDirty();
  1488 void
  1489 CacheFile::WriteMetadataIfNeeded()
  1491   LOG(("CacheFile::WriteMetadataIfNeeded() [this=%p]", this));
  1493   CacheFileAutoLock lock(this);
  1495   if (!mMemoryOnly)
  1496     WriteMetadataIfNeededLocked();
  1499 void
  1500 CacheFile::WriteMetadataIfNeededLocked(bool aFireAndForget)
  1502   // When aFireAndForget is set to true, we are called from dtor.
  1503   // |this| must not be referenced after this method returns!
  1505   LOG(("CacheFile::WriteMetadataIfNeededLocked() [this=%p]", this));
  1507   nsresult rv;
  1509   AssertOwnsLock();
  1510   MOZ_ASSERT(!mMemoryOnly);
  1512   if (!mMetadata) {
  1513     MOZ_CRASH("Must have metadata here");
  1514     return;
  1517   if (!aFireAndForget) {
  1518     // if aFireAndForget is set, we are called from dtor. Write
  1519     // scheduler hard-refers CacheFile otherwise, so we cannot be here.
  1520     CacheFileIOManager::UnscheduleMetadataWrite(this);
  1523   if (NS_FAILED(mStatus))
  1524     return;
  1526   if (!IsDirty() || mOutput || mInputs.Length() || mChunks.Count() ||
  1527       mWritingMetadata || mOpeningFile)
  1528     return;
  1530   LOG(("CacheFile::WriteMetadataIfNeededLocked() - Writing metadata [this=%p]",
  1531        this));
  1533   rv = mMetadata->WriteMetadata(mDataSize, aFireAndForget ? nullptr : this);
  1534   if (NS_SUCCEEDED(rv)) {
  1535     mWritingMetadata = true;
  1536     mDataIsDirty = false;
  1537   } else {
  1538     LOG(("CacheFile::WriteMetadataIfNeededLocked() - Writing synchronously "
  1539          "failed [this=%p]", this));
  1540     // TODO: close streams with error
  1541     SetError(rv);
  1545 void
  1546 CacheFile::PostWriteTimer()
  1548   LOG(("CacheFile::PostWriteTimer() [this=%p]", this));
  1550   CacheFileIOManager::ScheduleMetadataWrite(this);
  1553 PLDHashOperator
  1554 CacheFile::WriteAllCachedChunks(const uint32_t& aIdx,
  1555                                 nsRefPtr<CacheFileChunk>& aChunk,
  1556                                 void* aClosure)
  1558   CacheFile *file = static_cast<CacheFile*>(aClosure);
  1560   LOG(("CacheFile::WriteAllCachedChunks() [this=%p, idx=%d, chunk=%p]",
  1561        file, aIdx, aChunk.get()));
  1563   file->mChunks.Put(aIdx, aChunk);
  1564   aChunk->mFile = file;
  1565   aChunk->mRemovingChunk = false;
  1567   MOZ_ASSERT(aChunk->IsReady());
  1569   NS_ADDREF(aChunk);
  1570   file->ReleaseOutsideLock(aChunk);
  1572   return PL_DHASH_REMOVE;
  1575 PLDHashOperator
  1576 CacheFile::FailListenersIfNonExistentChunk(
  1577   const uint32_t& aIdx,
  1578   nsAutoPtr<ChunkListeners>& aListeners,
  1579   void* aClosure)
  1581   CacheFile *file = static_cast<CacheFile*>(aClosure);
  1583   LOG(("CacheFile::FailListenersIfNonExistentChunk() [this=%p, idx=%d]",
  1584        file, aIdx));
  1586   nsRefPtr<CacheFileChunk> chunk;
  1587   file->mChunks.Get(aIdx, getter_AddRefs(chunk));
  1588   if (chunk) {
  1589     MOZ_ASSERT(!chunk->IsReady());
  1590     return PL_DHASH_NEXT;
  1593   for (uint32_t i = 0 ; i < aListeners->mItems.Length() ; i++) {
  1594     ChunkListenerItem *item = aListeners->mItems[i];
  1595     file->NotifyChunkListener(item->mCallback, item->mTarget,
  1596                               NS_ERROR_NOT_AVAILABLE, aIdx, nullptr);
  1597     delete item;
  1600   return PL_DHASH_REMOVE;
  1603 PLDHashOperator
  1604 CacheFile::FailUpdateListeners(
  1605   const uint32_t& aIdx,
  1606   nsRefPtr<CacheFileChunk>& aChunk,
  1607   void* aClosure)
  1609 #ifdef PR_LOGGING
  1610   CacheFile *file = static_cast<CacheFile*>(aClosure);
  1611 #endif
  1613   LOG(("CacheFile::FailUpdateListeners() [this=%p, idx=%d]",
  1614        file, aIdx));
  1616   if (aChunk->IsReady()) {
  1617     aChunk->NotifyUpdateListeners();
  1620   return PL_DHASH_NEXT;
  1623 nsresult
  1624 CacheFile::PadChunkWithZeroes(uint32_t aChunkIdx)
  1626   AssertOwnsLock();
  1628   // This method is used to pad last incomplete chunk with zeroes or create
  1629   // a new chunk full of zeroes
  1630   MOZ_ASSERT(mDataSize / kChunkSize == aChunkIdx);
  1632   nsresult rv;
  1633   nsRefPtr<CacheFileChunk> chunk;
  1634   rv = GetChunkLocked(aChunkIdx, true, nullptr, getter_AddRefs(chunk));
  1635   NS_ENSURE_SUCCESS(rv, rv);
  1637   LOG(("CacheFile::PadChunkWithZeroes() - Zeroing hole in chunk %d, range %d-%d"
  1638        " [this=%p]", aChunkIdx, chunk->DataSize(), kChunkSize - 1, this));
  1640   chunk->EnsureBufSize(kChunkSize);
  1641   memset(chunk->BufForWriting() + chunk->DataSize(), 0, kChunkSize - chunk->DataSize());
  1643   chunk->UpdateDataSize(chunk->DataSize(), kChunkSize - chunk->DataSize(),
  1644                         false);
  1646   ReleaseOutsideLock(chunk.forget().take());
  1648   return NS_OK;
  1651 void
  1652 CacheFile::SetError(nsresult aStatus)
  1654   if (NS_SUCCEEDED(mStatus)) {
  1655     mStatus = aStatus;
  1659 nsresult
  1660 CacheFile::InitIndexEntry()
  1662   MOZ_ASSERT(mHandle);
  1664   if (mHandle->IsDoomed())
  1665     return NS_OK;
  1667   nsresult rv;
  1669   rv = CacheFileIOManager::InitIndexEntry(mHandle,
  1670                                           mMetadata->AppId(),
  1671                                           mMetadata->IsAnonymous(),
  1672                                           mMetadata->IsInBrowser());
  1673   NS_ENSURE_SUCCESS(rv, rv);
  1675   uint32_t expTime;
  1676   mMetadata->GetExpirationTime(&expTime);
  1678   uint32_t frecency;
  1679   mMetadata->GetFrecency(&frecency);
  1681   rv = CacheFileIOManager::UpdateIndexEntry(mHandle, &frecency, &expTime);
  1682   NS_ENSURE_SUCCESS(rv, rv);
  1684   return NS_OK;
  1687 // Memory reporting
  1689 namespace { // anon
  1691 size_t
  1692 CollectChunkSize(uint32_t const & aIdx,
  1693                  nsRefPtr<mozilla::net::CacheFileChunk> const & aChunk,
  1694                  mozilla::MallocSizeOf mallocSizeOf, void* aClosure)
  1696   return aChunk->SizeOfIncludingThis(mallocSizeOf);
  1699 } // anon
  1701 size_t
  1702 CacheFile::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
  1704   CacheFileAutoLock lock(const_cast<CacheFile*>(this));
  1706   size_t n = 0;
  1707   n += mKey.SizeOfExcludingThisIfUnshared(mallocSizeOf);
  1708   n += mChunks.SizeOfExcludingThis(CollectChunkSize, mallocSizeOf);
  1709   n += mCachedChunks.SizeOfExcludingThis(CollectChunkSize, mallocSizeOf);
  1710   if (mMetadata) {
  1711     n += mMetadata->SizeOfIncludingThis(mallocSizeOf);
  1714   // Input streams are not elsewhere reported.
  1715   n += mInputs.SizeOfExcludingThis(mallocSizeOf);
  1716   for (uint32_t i = 0; i < mInputs.Length(); ++i) {
  1717     n += mInputs[i]->SizeOfIncludingThis(mallocSizeOf);
  1720   // Output streams are not elsewhere reported.
  1721   if (mOutput) {
  1722     n += mOutput->SizeOfIncludingThis(mallocSizeOf);
  1725   // The listeners are usually classes reported just above.
  1726   n += mChunkListeners.SizeOfExcludingThis(nullptr, mallocSizeOf);
  1727   n += mObjsToRelease.SizeOfExcludingThis(mallocSizeOf);
  1729   // mHandle reported directly from CacheFileIOManager.
  1731   return n;
  1734 size_t
  1735 CacheFile::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
  1737   return mallocSizeOf(this) + SizeOfExcludingThis(mallocSizeOf);
  1740 } // net
  1741 } // mozilla

mercurial