netwerk/cache2/CacheFile.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/netwerk/cache2/CacheFile.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1741 @@
     1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.7 +
     1.8 +#include "CacheLog.h"
     1.9 +#include "CacheFile.h"
    1.10 +
    1.11 +#include "CacheFileChunk.h"
    1.12 +#include "CacheFileInputStream.h"
    1.13 +#include "CacheFileOutputStream.h"
    1.14 +#include "CacheIndex.h"
    1.15 +#include "nsThreadUtils.h"
    1.16 +#include "mozilla/DebugOnly.h"
    1.17 +#include <algorithm>
    1.18 +#include "nsComponentManagerUtils.h"
    1.19 +#include "nsProxyRelease.h"
    1.20 +
    1.21 +// When CACHE_CHUNKS is defined we always cache unused chunks in mCacheChunks.
    1.22 +// When it is not defined, we always release the chunks ASAP, i.e. we cache
    1.23 +// unused chunks only when:
    1.24 +//  - CacheFile is memory-only
    1.25 +//  - CacheFile is still waiting for the handle
    1.26 +
    1.27 +//#define CACHE_CHUNKS
    1.28 +
    1.29 +namespace mozilla {
    1.30 +namespace net {
    1.31 +
    1.32 +class NotifyCacheFileListenerEvent : public nsRunnable {
    1.33 +public:
    1.34 +  NotifyCacheFileListenerEvent(CacheFileListener *aCallback,
    1.35 +                               nsresult aResult,
    1.36 +                               bool aIsNew)
    1.37 +    : mCallback(aCallback)
    1.38 +    , mRV(aResult)
    1.39 +    , mIsNew(aIsNew)
    1.40 +  {
    1.41 +    LOG(("NotifyCacheFileListenerEvent::NotifyCacheFileListenerEvent() "
    1.42 +         "[this=%p]", this));
    1.43 +    MOZ_COUNT_CTOR(NotifyCacheFileListenerEvent);
    1.44 +  }
    1.45 +
    1.46 +  ~NotifyCacheFileListenerEvent()
    1.47 +  {
    1.48 +    LOG(("NotifyCacheFileListenerEvent::~NotifyCacheFileListenerEvent() "
    1.49 +         "[this=%p]", this));
    1.50 +    MOZ_COUNT_DTOR(NotifyCacheFileListenerEvent);
    1.51 +  }
    1.52 +
    1.53 +  NS_IMETHOD Run()
    1.54 +  {
    1.55 +    LOG(("NotifyCacheFileListenerEvent::Run() [this=%p]", this));
    1.56 +
    1.57 +    mCallback->OnFileReady(mRV, mIsNew);
    1.58 +    return NS_OK;
    1.59 +  }
    1.60 +
    1.61 +protected:
    1.62 +  nsCOMPtr<CacheFileListener> mCallback;
    1.63 +  nsresult                    mRV;
    1.64 +  bool                        mIsNew;
    1.65 +};
    1.66 +
    1.67 +class NotifyChunkListenerEvent : public nsRunnable {
    1.68 +public:
    1.69 +  NotifyChunkListenerEvent(CacheFileChunkListener *aCallback,
    1.70 +                           nsresult aResult,
    1.71 +                           uint32_t aChunkIdx,
    1.72 +                           CacheFileChunk *aChunk)
    1.73 +    : mCallback(aCallback)
    1.74 +    , mRV(aResult)
    1.75 +    , mChunkIdx(aChunkIdx)
    1.76 +    , mChunk(aChunk)
    1.77 +  {
    1.78 +    LOG(("NotifyChunkListenerEvent::NotifyChunkListenerEvent() [this=%p]",
    1.79 +         this));
    1.80 +    MOZ_COUNT_CTOR(NotifyChunkListenerEvent);
    1.81 +  }
    1.82 +
    1.83 +  ~NotifyChunkListenerEvent()
    1.84 +  {
    1.85 +    LOG(("NotifyChunkListenerEvent::~NotifyChunkListenerEvent() [this=%p]",
    1.86 +         this));
    1.87 +    MOZ_COUNT_DTOR(NotifyChunkListenerEvent);
    1.88 +  }
    1.89 +
    1.90 +  NS_IMETHOD Run()
    1.91 +  {
    1.92 +    LOG(("NotifyChunkListenerEvent::Run() [this=%p]", this));
    1.93 +
    1.94 +    mCallback->OnChunkAvailable(mRV, mChunkIdx, mChunk);
    1.95 +    return NS_OK;
    1.96 +  }
    1.97 +
    1.98 +protected:
    1.99 +  nsCOMPtr<CacheFileChunkListener> mCallback;
   1.100 +  nsresult                         mRV;
   1.101 +  uint32_t                         mChunkIdx;
   1.102 +  nsRefPtr<CacheFileChunk>         mChunk;
   1.103 +};
   1.104 +
   1.105 +
   1.106 +class DoomFileHelper : public CacheFileIOListener
   1.107 +{
   1.108 +public:
   1.109 +  NS_DECL_THREADSAFE_ISUPPORTS
   1.110 +
   1.111 +  DoomFileHelper(CacheFileListener *aListener)
   1.112 +    : mListener(aListener)
   1.113 +  {
   1.114 +    MOZ_COUNT_CTOR(DoomFileHelper);
   1.115 +  }
   1.116 +
   1.117 +
   1.118 +  NS_IMETHOD OnFileOpened(CacheFileHandle *aHandle, nsresult aResult)
   1.119 +  {
   1.120 +    MOZ_CRASH("DoomFileHelper::OnFileOpened should not be called!");
   1.121 +    return NS_ERROR_UNEXPECTED;
   1.122 +  }
   1.123 +
   1.124 +  NS_IMETHOD OnDataWritten(CacheFileHandle *aHandle, const char *aBuf,
   1.125 +                           nsresult aResult)
   1.126 +  {
   1.127 +    MOZ_CRASH("DoomFileHelper::OnDataWritten should not be called!");
   1.128 +    return NS_ERROR_UNEXPECTED;
   1.129 +  }
   1.130 +
   1.131 +  NS_IMETHOD OnDataRead(CacheFileHandle *aHandle, char *aBuf, nsresult aResult)
   1.132 +  {
   1.133 +    MOZ_CRASH("DoomFileHelper::OnDataRead should not be called!");
   1.134 +    return NS_ERROR_UNEXPECTED;
   1.135 +  }
   1.136 +
   1.137 +  NS_IMETHOD OnFileDoomed(CacheFileHandle *aHandle, nsresult aResult)
   1.138 +  {
   1.139 +    if (mListener)
   1.140 +      mListener->OnFileDoomed(aResult);
   1.141 +    return NS_OK;
   1.142 +  }
   1.143 +
   1.144 +  NS_IMETHOD OnEOFSet(CacheFileHandle *aHandle, nsresult aResult)
   1.145 +  {
   1.146 +    MOZ_CRASH("DoomFileHelper::OnEOFSet should not be called!");
   1.147 +    return NS_ERROR_UNEXPECTED;
   1.148 +  }
   1.149 +
   1.150 +  NS_IMETHOD OnFileRenamed(CacheFileHandle *aHandle, nsresult aResult)
   1.151 +  {
   1.152 +    MOZ_CRASH("DoomFileHelper::OnFileRenamed should not be called!");
   1.153 +    return NS_ERROR_UNEXPECTED;
   1.154 +  }
   1.155 +
   1.156 +private:
   1.157 +  virtual ~DoomFileHelper()
   1.158 +  {
   1.159 +    MOZ_COUNT_DTOR(DoomFileHelper);
   1.160 +  }
   1.161 +
   1.162 +  nsCOMPtr<CacheFileListener>  mListener;
   1.163 +};
   1.164 +
   1.165 +NS_IMPL_ISUPPORTS(DoomFileHelper, CacheFileIOListener)
   1.166 +
   1.167 +
   1.168 +NS_IMPL_ADDREF(CacheFile)
   1.169 +NS_IMPL_RELEASE(CacheFile)
   1.170 +NS_INTERFACE_MAP_BEGIN(CacheFile)
   1.171 +  NS_INTERFACE_MAP_ENTRY(mozilla::net::CacheFileChunkListener)
   1.172 +  NS_INTERFACE_MAP_ENTRY(mozilla::net::CacheFileIOListener)
   1.173 +  NS_INTERFACE_MAP_ENTRY(mozilla::net::CacheFileMetadataListener)
   1.174 +  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports,
   1.175 +                                   mozilla::net::CacheFileChunkListener)
   1.176 +NS_INTERFACE_MAP_END_THREADSAFE
   1.177 +
   1.178 +CacheFile::CacheFile()
   1.179 +  : mLock("CacheFile.mLock")
   1.180 +  , mOpeningFile(false)
   1.181 +  , mReady(false)
   1.182 +  , mMemoryOnly(false)
   1.183 +  , mOpenAsMemoryOnly(false)
   1.184 +  , mDataAccessed(false)
   1.185 +  , mDataIsDirty(false)
   1.186 +  , mWritingMetadata(false)
   1.187 +  , mStatus(NS_OK)
   1.188 +  , mDataSize(-1)
   1.189 +  , mOutput(nullptr)
   1.190 +{
   1.191 +  LOG(("CacheFile::CacheFile() [this=%p]", this));
   1.192 +}
   1.193 +
   1.194 +CacheFile::~CacheFile()
   1.195 +{
   1.196 +  LOG(("CacheFile::~CacheFile() [this=%p]", this));
   1.197 +
   1.198 +  MutexAutoLock lock(mLock);
   1.199 +  if (!mMemoryOnly && mReady) {
   1.200 +    // mReady flag indicates we have metadata plus in a valid state.
   1.201 +    WriteMetadataIfNeededLocked(true);
   1.202 +  }
   1.203 +}
   1.204 +
   1.205 +nsresult
   1.206 +CacheFile::Init(const nsACString &aKey,
   1.207 +                bool aCreateNew,
   1.208 +                bool aMemoryOnly,
   1.209 +                bool aPriority,
   1.210 +                CacheFileListener *aCallback)
   1.211 +{
   1.212 +  MOZ_ASSERT(!mListener);
   1.213 +  MOZ_ASSERT(!mHandle);
   1.214 +
   1.215 +  nsresult rv;
   1.216 +
   1.217 +  mKey = aKey;
   1.218 +  mOpenAsMemoryOnly = mMemoryOnly = aMemoryOnly;
   1.219 +
   1.220 +  LOG(("CacheFile::Init() [this=%p, key=%s, createNew=%d, memoryOnly=%d, "
   1.221 +       "listener=%p]", this, mKey.get(), aCreateNew, aMemoryOnly, aCallback));
   1.222 +
   1.223 +  if (mMemoryOnly) {
   1.224 +    MOZ_ASSERT(!aCallback);
   1.225 +
   1.226 +    mMetadata = new CacheFileMetadata(mOpenAsMemoryOnly, mKey);
   1.227 +    mReady = true;
   1.228 +    mDataSize = mMetadata->Offset();
   1.229 +    return NS_OK;
   1.230 +  }
   1.231 +  else {
   1.232 +    uint32_t flags;
   1.233 +    if (aCreateNew) {
   1.234 +      MOZ_ASSERT(!aCallback);
   1.235 +      flags = CacheFileIOManager::CREATE_NEW;
   1.236 +
   1.237 +      // make sure we can use this entry immediately
   1.238 +      mMetadata = new CacheFileMetadata(mOpenAsMemoryOnly, mKey);
   1.239 +      mReady = true;
   1.240 +      mDataSize = mMetadata->Offset();
   1.241 +    }
   1.242 +    else {
   1.243 +      flags = CacheFileIOManager::CREATE;
   1.244 +
   1.245 +      // Have a look into index and change to CREATE_NEW when we are sure
   1.246 +      // that the entry does not exist.
   1.247 +      CacheIndex::EntryStatus status;
   1.248 +      rv = CacheIndex::HasEntry(mKey, &status);
   1.249 +      if (status == CacheIndex::DOES_NOT_EXIST) {
   1.250 +        LOG(("CacheFile::Init() - Forcing CREATE_NEW flag since we don't have"
   1.251 +             " this entry according to index"));
   1.252 +        flags = CacheFileIOManager::CREATE_NEW;
   1.253 +
   1.254 +        // make sure we can use this entry immediately
   1.255 +        mMetadata = new CacheFileMetadata(mOpenAsMemoryOnly, mKey);
   1.256 +        mReady = true;
   1.257 +        mDataSize = mMetadata->Offset();
   1.258 +
   1.259 +        // Notify callback now and don't store it in mListener, no further
   1.260 +        // operation can change the result.
   1.261 +        nsRefPtr<NotifyCacheFileListenerEvent> ev;
   1.262 +        ev = new NotifyCacheFileListenerEvent(aCallback, NS_OK, true);
   1.263 +        rv = NS_DispatchToCurrentThread(ev);
   1.264 +        NS_ENSURE_SUCCESS(rv, rv);
   1.265 +
   1.266 +        aCallback = nullptr;
   1.267 +      }
   1.268 +    }
   1.269 +
   1.270 +    if (aPriority)
   1.271 +      flags |= CacheFileIOManager::PRIORITY;
   1.272 +
   1.273 +    mOpeningFile = true;
   1.274 +    mListener = aCallback;
   1.275 +    rv = CacheFileIOManager::OpenFile(mKey, flags, true, this);
   1.276 +    if (NS_FAILED(rv)) {
   1.277 +      mListener = nullptr;
   1.278 +      mOpeningFile = false;
   1.279 +
   1.280 +      if (aCreateNew) {
   1.281 +        NS_WARNING("Forcing memory-only entry since OpenFile failed");
   1.282 +        LOG(("CacheFile::Init() - CacheFileIOManager::OpenFile() failed "
   1.283 +             "synchronously. We can continue in memory-only mode since "
   1.284 +             "aCreateNew == true. [this=%p]", this));
   1.285 +
   1.286 +        mMemoryOnly = true;
   1.287 +      }
   1.288 +      else if (rv == NS_ERROR_NOT_INITIALIZED) {
   1.289 +        NS_WARNING("Forcing memory-only entry since CacheIOManager isn't "
   1.290 +                   "initialized.");
   1.291 +        LOG(("CacheFile::Init() - CacheFileIOManager isn't initialized, "
   1.292 +             "initializing entry as memory-only. [this=%p]", this));
   1.293 +
   1.294 +        mMemoryOnly = true;
   1.295 +        mMetadata = new CacheFileMetadata(mOpenAsMemoryOnly, mKey);
   1.296 +        mReady = true;
   1.297 +        mDataSize = mMetadata->Offset();
   1.298 +
   1.299 +        nsRefPtr<NotifyCacheFileListenerEvent> ev;
   1.300 +        ev = new NotifyCacheFileListenerEvent(aCallback, NS_OK, true);
   1.301 +        rv = NS_DispatchToCurrentThread(ev);
   1.302 +        NS_ENSURE_SUCCESS(rv, rv);
   1.303 +      }
   1.304 +      else {
   1.305 +        NS_ENSURE_SUCCESS(rv, rv);
   1.306 +      }
   1.307 +    }
   1.308 +  }
   1.309 +
   1.310 +  return NS_OK;
   1.311 +}
   1.312 +
   1.313 +nsresult
   1.314 +CacheFile::OnChunkRead(nsresult aResult, CacheFileChunk *aChunk)
   1.315 +{
   1.316 +  CacheFileAutoLock lock(this);
   1.317 +
   1.318 +  nsresult rv;
   1.319 +
   1.320 +  uint32_t index = aChunk->Index();
   1.321 +
   1.322 +  LOG(("CacheFile::OnChunkRead() [this=%p, rv=0x%08x, chunk=%p, idx=%d]",
   1.323 +       this, aResult, aChunk, index));
   1.324 +
   1.325 +  if (NS_FAILED(aResult)) {
   1.326 +    SetError(aResult);
   1.327 +    CacheFileIOManager::DoomFile(mHandle, nullptr);
   1.328 +  }
   1.329 +
   1.330 +  if (HaveChunkListeners(index)) {
   1.331 +    rv = NotifyChunkListeners(index, aResult, aChunk);
   1.332 +    NS_ENSURE_SUCCESS(rv, rv);
   1.333 +  }
   1.334 +
   1.335 +  return NS_OK;
   1.336 +}
   1.337 +
   1.338 +nsresult
   1.339 +CacheFile::OnChunkWritten(nsresult aResult, CacheFileChunk *aChunk)
   1.340 +{
   1.341 +  CacheFileAutoLock lock(this);
   1.342 +
   1.343 +  nsresult rv;
   1.344 +
   1.345 +  LOG(("CacheFile::OnChunkWritten() [this=%p, rv=0x%08x, chunk=%p, idx=%d]",
   1.346 +       this, aResult, aChunk, aChunk->Index()));
   1.347 +
   1.348 +  MOZ_ASSERT(!mMemoryOnly);
   1.349 +  MOZ_ASSERT(!mOpeningFile);
   1.350 +  MOZ_ASSERT(mHandle);
   1.351 +
   1.352 +  if (NS_FAILED(aResult)) {
   1.353 +    SetError(aResult);
   1.354 +    CacheFileIOManager::DoomFile(mHandle, nullptr);
   1.355 +  }
   1.356 +
   1.357 +  if (NS_SUCCEEDED(aResult) && !aChunk->IsDirty()) {
   1.358 +    // update hash value in metadata
   1.359 +    mMetadata->SetHash(aChunk->Index(), aChunk->Hash());
   1.360 +  }
   1.361 +
   1.362 +  // notify listeners if there is any
   1.363 +  if (HaveChunkListeners(aChunk->Index())) {
   1.364 +    // don't release the chunk since there are some listeners queued
   1.365 +    rv = NotifyChunkListeners(aChunk->Index(), aResult, aChunk);
   1.366 +    if (NS_SUCCEEDED(rv)) {
   1.367 +      MOZ_ASSERT(aChunk->mRefCnt != 2);
   1.368 +      return NS_OK;
   1.369 +    }
   1.370 +  }
   1.371 +
   1.372 +  if (aChunk->mRefCnt != 2) {
   1.373 +    LOG(("CacheFile::OnChunkWritten() - Chunk is still used [this=%p, chunk=%p,"
   1.374 +         " refcnt=%d]", this, aChunk, aChunk->mRefCnt.get()));
   1.375 +
   1.376 +    return NS_OK;
   1.377 +  }
   1.378 +
   1.379 +#ifdef CACHE_CHUNKS
   1.380 +  if (NS_SUCCEEDED(aResult)) {
   1.381 +    LOG(("CacheFile::OnChunkWritten() - Caching unused chunk [this=%p, "
   1.382 +         "chunk=%p]", this, aChunk));
   1.383 +  } else {
   1.384 +    LOG(("CacheFile::OnChunkWritten() - Removing failed chunk [this=%p, "
   1.385 +         "chunk=%p]", this, aChunk));
   1.386 +  }
   1.387 +#else
   1.388 +  LOG(("CacheFile::OnChunkWritten() - Releasing %s chunk [this=%p, chunk=%p]",
   1.389 +       NS_SUCCEEDED(aResult) ? "unused" : "failed", this, aChunk));
   1.390 +#endif
   1.391 +
   1.392 +  RemoveChunkInternal(aChunk,
   1.393 +#ifdef CACHE_CHUNKS
   1.394 +                      NS_SUCCEEDED(aResult));
   1.395 +#else
   1.396 +                      false);
   1.397 +#endif
   1.398 +
   1.399 +  WriteMetadataIfNeededLocked();
   1.400 +
   1.401 +  return NS_OK;
   1.402 +}
   1.403 +
   1.404 +nsresult
   1.405 +CacheFile::OnChunkAvailable(nsresult aResult, uint32_t aChunkIdx,
   1.406 +                            CacheFileChunk *aChunk)
   1.407 +{
   1.408 +  MOZ_CRASH("CacheFile::OnChunkAvailable should not be called!");
   1.409 +  return NS_ERROR_UNEXPECTED;
   1.410 +}
   1.411 +
   1.412 +nsresult
   1.413 +CacheFile::OnChunkUpdated(CacheFileChunk *aChunk)
   1.414 +{
   1.415 +  MOZ_CRASH("CacheFile::OnChunkUpdated should not be called!");
   1.416 +  return NS_ERROR_UNEXPECTED;
   1.417 +}
   1.418 +
   1.419 +nsresult
   1.420 +CacheFile::OnFileOpened(CacheFileHandle *aHandle, nsresult aResult)
   1.421 +{
   1.422 +  nsresult rv;
   1.423 +
   1.424 +  // Using an 'auto' class to perform doom or fail the listener
   1.425 +  // outside the CacheFile's lock.
   1.426 +  class AutoFailDoomListener
   1.427 +  {
   1.428 +  public:
   1.429 +    AutoFailDoomListener(CacheFileHandle *aHandle)
   1.430 +      : mHandle(aHandle)
   1.431 +      , mAlreadyDoomed(false)
   1.432 +    {}
   1.433 +    ~AutoFailDoomListener()
   1.434 +    {
   1.435 +      if (!mListener)
   1.436 +        return;
   1.437 +
   1.438 +      if (mHandle) {
   1.439 +        if (mAlreadyDoomed) {
   1.440 +          mListener->OnFileDoomed(mHandle, NS_OK);
   1.441 +        } else {
   1.442 +          CacheFileIOManager::DoomFile(mHandle, mListener);
   1.443 +        }
   1.444 +      } else {
   1.445 +        mListener->OnFileDoomed(nullptr, NS_ERROR_NOT_AVAILABLE);
   1.446 +      }
   1.447 +    }
   1.448 +
   1.449 +    CacheFileHandle* mHandle;
   1.450 +    nsCOMPtr<CacheFileIOListener> mListener;
   1.451 +    bool mAlreadyDoomed;
   1.452 +  } autoDoom(aHandle);
   1.453 +
   1.454 +  nsCOMPtr<CacheFileListener> listener;
   1.455 +  bool isNew = false;
   1.456 +  nsresult retval = NS_OK;
   1.457 +
   1.458 +  {
   1.459 +    CacheFileAutoLock lock(this);
   1.460 +
   1.461 +    MOZ_ASSERT(mOpeningFile);
   1.462 +    MOZ_ASSERT((NS_SUCCEEDED(aResult) && aHandle) ||
   1.463 +               (NS_FAILED(aResult) && !aHandle));
   1.464 +    MOZ_ASSERT((mListener && !mMetadata) || // !createNew
   1.465 +               (!mListener && mMetadata));  // createNew
   1.466 +    MOZ_ASSERT(!mMemoryOnly || mMetadata); // memory-only was set on new entry
   1.467 +
   1.468 +    LOG(("CacheFile::OnFileOpened() [this=%p, rv=0x%08x, handle=%p]",
   1.469 +         this, aResult, aHandle));
   1.470 +
   1.471 +    mOpeningFile = false;
   1.472 +
   1.473 +    autoDoom.mListener.swap(mDoomAfterOpenListener);
   1.474 +
   1.475 +    if (mMemoryOnly) {
   1.476 +      // We can be here only in case the entry was initilized as createNew and
   1.477 +      // SetMemoryOnly() was called.
   1.478 +
   1.479 +      // Just don't store the handle into mHandle and exit
   1.480 +      autoDoom.mAlreadyDoomed = true;
   1.481 +      return NS_OK;
   1.482 +    }
   1.483 +    else if (NS_FAILED(aResult)) {
   1.484 +      if (mMetadata) {
   1.485 +        // This entry was initialized as createNew, just switch to memory-only
   1.486 +        // mode.
   1.487 +        NS_WARNING("Forcing memory-only entry since OpenFile failed");
   1.488 +        LOG(("CacheFile::OnFileOpened() - CacheFileIOManager::OpenFile() "
   1.489 +             "failed asynchronously. We can continue in memory-only mode since "
   1.490 +             "aCreateNew == true. [this=%p]", this));
   1.491 +
   1.492 +        mMemoryOnly = true;
   1.493 +        return NS_OK;
   1.494 +      }
   1.495 +      else if (aResult == NS_ERROR_FILE_INVALID_PATH) {
   1.496 +        // CacheFileIOManager doesn't have mCacheDirectory, switch to
   1.497 +        // memory-only mode.
   1.498 +        NS_WARNING("Forcing memory-only entry since CacheFileIOManager doesn't "
   1.499 +                   "have mCacheDirectory.");
   1.500 +        LOG(("CacheFile::OnFileOpened() - CacheFileIOManager doesn't have "
   1.501 +             "mCacheDirectory, initializing entry as memory-only. [this=%p]",
   1.502 +             this));
   1.503 +
   1.504 +        mMemoryOnly = true;
   1.505 +        mMetadata = new CacheFileMetadata(mOpenAsMemoryOnly, mKey);
   1.506 +        mReady = true;
   1.507 +        mDataSize = mMetadata->Offset();
   1.508 +
   1.509 +        isNew = true;
   1.510 +        retval = NS_OK;
   1.511 +      }
   1.512 +      else {
   1.513 +        // CacheFileIOManager::OpenFile() failed for another reason.
   1.514 +        isNew = false;
   1.515 +        retval = aResult;
   1.516 +      }
   1.517 +
   1.518 +      mListener.swap(listener);
   1.519 +    }
   1.520 +    else {
   1.521 +      mHandle = aHandle;
   1.522 +
   1.523 +      if (mMetadata) {
   1.524 +        InitIndexEntry();
   1.525 +
   1.526 +        // The entry was initialized as createNew, don't try to read metadata.
   1.527 +        mMetadata->SetHandle(mHandle);
   1.528 +
   1.529 +        // Write all cached chunks, otherwise they may stay unwritten.
   1.530 +        mCachedChunks.Enumerate(&CacheFile::WriteAllCachedChunks, this);
   1.531 +
   1.532 +        return NS_OK;
   1.533 +      }
   1.534 +    }
   1.535 +  }
   1.536 +
   1.537 +  if (listener) {
   1.538 +    listener->OnFileReady(retval, isNew);
   1.539 +    return NS_OK;
   1.540 +  }
   1.541 +
   1.542 +  MOZ_ASSERT(NS_SUCCEEDED(aResult));
   1.543 +  MOZ_ASSERT(!mMetadata);
   1.544 +  MOZ_ASSERT(mListener);
   1.545 +
   1.546 +  mMetadata = new CacheFileMetadata(mHandle, mKey);
   1.547 +
   1.548 +  rv = mMetadata->ReadMetadata(this);
   1.549 +  if (NS_FAILED(rv)) {
   1.550 +    mListener.swap(listener);
   1.551 +    listener->OnFileReady(rv, false);
   1.552 +  }
   1.553 +
   1.554 +  return NS_OK;
   1.555 +}
   1.556 +
   1.557 +nsresult
   1.558 +CacheFile::OnDataWritten(CacheFileHandle *aHandle, const char *aBuf,
   1.559 +                         nsresult aResult)
   1.560 +{
   1.561 +  MOZ_CRASH("CacheFile::OnDataWritten should not be called!");
   1.562 +  return NS_ERROR_UNEXPECTED;
   1.563 +}
   1.564 +
   1.565 +nsresult
   1.566 +CacheFile::OnDataRead(CacheFileHandle *aHandle, char *aBuf, nsresult aResult)
   1.567 +{
   1.568 +  MOZ_CRASH("CacheFile::OnDataRead should not be called!");
   1.569 +  return NS_ERROR_UNEXPECTED;
   1.570 +}
   1.571 +
   1.572 +nsresult
   1.573 +CacheFile::OnMetadataRead(nsresult aResult)
   1.574 +{
   1.575 +  MOZ_ASSERT(mListener);
   1.576 +
   1.577 +  LOG(("CacheFile::OnMetadataRead() [this=%p, rv=0x%08x]", this, aResult));
   1.578 +
   1.579 +  bool isNew = false;
   1.580 +  if (NS_SUCCEEDED(aResult)) {
   1.581 +    mReady = true;
   1.582 +    mDataSize = mMetadata->Offset();
   1.583 +    if (mDataSize == 0 && mMetadata->ElementsSize() == 0) {
   1.584 +      isNew = true;
   1.585 +      mMetadata->MarkDirty();
   1.586 +    }
   1.587 +
   1.588 +    InitIndexEntry();
   1.589 +  }
   1.590 +
   1.591 +  nsCOMPtr<CacheFileListener> listener;
   1.592 +  mListener.swap(listener);
   1.593 +  listener->OnFileReady(aResult, isNew);
   1.594 +  return NS_OK;
   1.595 +}
   1.596 +
   1.597 +nsresult
   1.598 +CacheFile::OnMetadataWritten(nsresult aResult)
   1.599 +{
   1.600 +  CacheFileAutoLock lock(this);
   1.601 +
   1.602 +  LOG(("CacheFile::OnMetadataWritten() [this=%p, rv=0x%08x]", this, aResult));
   1.603 +
   1.604 +  MOZ_ASSERT(mWritingMetadata);
   1.605 +  mWritingMetadata = false;
   1.606 +
   1.607 +  MOZ_ASSERT(!mMemoryOnly);
   1.608 +  MOZ_ASSERT(!mOpeningFile);
   1.609 +
   1.610 +  if (NS_FAILED(aResult)) {
   1.611 +    // TODO close streams with an error ???
   1.612 +  }
   1.613 +
   1.614 +  if (mOutput || mInputs.Length() || mChunks.Count())
   1.615 +    return NS_OK;
   1.616 +
   1.617 +  if (IsDirty())
   1.618 +    WriteMetadataIfNeededLocked();
   1.619 +
   1.620 +  if (!mWritingMetadata) {
   1.621 +    LOG(("CacheFile::OnMetadataWritten() - Releasing file handle [this=%p]",
   1.622 +         this));
   1.623 +    CacheFileIOManager::ReleaseNSPRHandle(mHandle);
   1.624 +  }
   1.625 +
   1.626 +  return NS_OK;
   1.627 +}
   1.628 +
   1.629 +nsresult
   1.630 +CacheFile::OnFileDoomed(CacheFileHandle *aHandle, nsresult aResult)
   1.631 +{
   1.632 +  nsCOMPtr<CacheFileListener> listener;
   1.633 +
   1.634 +  {
   1.635 +    CacheFileAutoLock lock(this);
   1.636 +
   1.637 +    MOZ_ASSERT(mListener);
   1.638 +
   1.639 +    LOG(("CacheFile::OnFileDoomed() [this=%p, rv=0x%08x, handle=%p]",
   1.640 +         this, aResult, aHandle));
   1.641 +
   1.642 +    mListener.swap(listener);
   1.643 +  }
   1.644 +
   1.645 +  listener->OnFileDoomed(aResult);
   1.646 +  return NS_OK;
   1.647 +}
   1.648 +
   1.649 +nsresult
   1.650 +CacheFile::OnEOFSet(CacheFileHandle *aHandle, nsresult aResult)
   1.651 +{
   1.652 +  MOZ_CRASH("CacheFile::OnEOFSet should not be called!");
   1.653 +  return NS_ERROR_UNEXPECTED;
   1.654 +}
   1.655 +
   1.656 +nsresult
   1.657 +CacheFile::OnFileRenamed(CacheFileHandle *aHandle, nsresult aResult)
   1.658 +{
   1.659 +  MOZ_CRASH("CacheFile::OnFileRenamed should not be called!");
   1.660 +  return NS_ERROR_UNEXPECTED;
   1.661 +}
   1.662 +
   1.663 +nsresult
   1.664 +CacheFile::OpenInputStream(nsIInputStream **_retval)
   1.665 +{
   1.666 +  CacheFileAutoLock lock(this);
   1.667 +
   1.668 +  MOZ_ASSERT(mHandle || mMemoryOnly || mOpeningFile);
   1.669 +
   1.670 +  if (!mReady) {
   1.671 +    LOG(("CacheFile::OpenInputStream() - CacheFile is not ready [this=%p]",
   1.672 +         this));
   1.673 +
   1.674 +    return NS_ERROR_NOT_AVAILABLE;
   1.675 +  }
   1.676 +
   1.677 +  CacheFileInputStream *input = new CacheFileInputStream(this);
   1.678 +
   1.679 +  LOG(("CacheFile::OpenInputStream() - Creating new input stream %p [this=%p]",
   1.680 +       input, this));
   1.681 +
   1.682 +  mInputs.AppendElement(input);
   1.683 +  NS_ADDREF(input);
   1.684 +
   1.685 +  mDataAccessed = true;
   1.686 +  NS_ADDREF(*_retval = input);
   1.687 +  return NS_OK;
   1.688 +}
   1.689 +
   1.690 +nsresult
   1.691 +CacheFile::OpenOutputStream(CacheOutputCloseListener *aCloseListener, nsIOutputStream **_retval)
   1.692 +{
   1.693 +  CacheFileAutoLock lock(this);
   1.694 +
   1.695 +  MOZ_ASSERT(mHandle || mMemoryOnly || mOpeningFile);
   1.696 +
   1.697 +  if (!mReady) {
   1.698 +    LOG(("CacheFile::OpenOutputStream() - CacheFile is not ready [this=%p]",
   1.699 +         this));
   1.700 +
   1.701 +    return NS_ERROR_NOT_AVAILABLE;
   1.702 +  }
   1.703 +
   1.704 +  if (mOutput) {
   1.705 +    LOG(("CacheFile::OpenOutputStream() - We already have output stream %p "
   1.706 +         "[this=%p]", mOutput, this));
   1.707 +
   1.708 +    return NS_ERROR_NOT_AVAILABLE;
   1.709 +  }
   1.710 +
   1.711 +  mOutput = new CacheFileOutputStream(this, aCloseListener);
   1.712 +
   1.713 +  LOG(("CacheFile::OpenOutputStream() - Creating new output stream %p "
   1.714 +       "[this=%p]", mOutput, this));
   1.715 +
   1.716 +  mDataAccessed = true;
   1.717 +  NS_ADDREF(*_retval = mOutput);
   1.718 +  return NS_OK;
   1.719 +}
   1.720 +
   1.721 +nsresult
   1.722 +CacheFile::SetMemoryOnly()
   1.723 +{
   1.724 +  LOG(("CacheFile::SetMemoryOnly() mMemoryOnly=%d [this=%p]",
   1.725 +       mMemoryOnly, this));
   1.726 +
   1.727 +  if (mMemoryOnly)
   1.728 +    return NS_OK;
   1.729 +
   1.730 +  MOZ_ASSERT(mReady);
   1.731 +
   1.732 +  if (!mReady) {
   1.733 +    LOG(("CacheFile::SetMemoryOnly() - CacheFile is not ready [this=%p]",
   1.734 +         this));
   1.735 +
   1.736 +    return NS_ERROR_NOT_AVAILABLE;
   1.737 +  }
   1.738 +
   1.739 +  if (mDataAccessed) {
   1.740 +    LOG(("CacheFile::SetMemoryOnly() - Data was already accessed [this=%p]", this));
   1.741 +    return NS_ERROR_NOT_AVAILABLE;
   1.742 +  }
   1.743 +
   1.744 +  // TODO what to do when this isn't a new entry and has an existing metadata???
   1.745 +  mMemoryOnly = true;
   1.746 +  return NS_OK;
   1.747 +}
   1.748 +
   1.749 +nsresult
   1.750 +CacheFile::Doom(CacheFileListener *aCallback)
   1.751 +{
   1.752 +  CacheFileAutoLock lock(this);
   1.753 +
   1.754 +  MOZ_ASSERT(mHandle || mMemoryOnly || mOpeningFile);
   1.755 +
   1.756 +  LOG(("CacheFile::Doom() [this=%p, listener=%p]", this, aCallback));
   1.757 +
   1.758 +  nsresult rv = NS_OK;
   1.759 +
   1.760 +  if (mMemoryOnly) {
   1.761 +    return NS_ERROR_FILE_NOT_FOUND;
   1.762 +  }
   1.763 +
   1.764 +  if (mHandle && mHandle->IsDoomed()) {
   1.765 +    return NS_ERROR_FILE_NOT_FOUND;
   1.766 +  }
   1.767 +
   1.768 +  nsCOMPtr<CacheFileIOListener> listener;
   1.769 +  if (aCallback || !mHandle) {
   1.770 +    listener = new DoomFileHelper(aCallback);
   1.771 +  }
   1.772 +  if (mHandle) {
   1.773 +    rv = CacheFileIOManager::DoomFile(mHandle, listener);
   1.774 +  } else if (mOpeningFile) {
   1.775 +    mDoomAfterOpenListener = listener;
   1.776 +  }
   1.777 +
   1.778 +  return rv;
   1.779 +}
   1.780 +
   1.781 +nsresult
   1.782 +CacheFile::ThrowMemoryCachedData()
   1.783 +{
   1.784 +  CacheFileAutoLock lock(this);
   1.785 +
   1.786 +  LOG(("CacheFile::ThrowMemoryCachedData() [this=%p]", this));
   1.787 +
   1.788 +  if (mMemoryOnly) {
   1.789 +    // This method should not be called when the CacheFile was initialized as
   1.790 +    // memory-only, but it can be called when CacheFile end up as memory-only
   1.791 +    // due to e.g. IO failure since CacheEntry doesn't know it.
   1.792 +    LOG(("CacheFile::ThrowMemoryCachedData() - Ignoring request because the "
   1.793 +         "entry is memory-only. [this=%p]", this));
   1.794 +
   1.795 +    return NS_ERROR_NOT_AVAILABLE;
   1.796 +  }
   1.797 +
   1.798 +  if (mOpeningFile) {
   1.799 +    // mayhemer, note: we shouldn't get here, since CacheEntry prevents loading
   1.800 +    // entries from being purged.
   1.801 +
   1.802 +    LOG(("CacheFile::ThrowMemoryCachedData() - Ignoring request because the "
   1.803 +         "entry is still opening the file [this=%p]", this));
   1.804 +
   1.805 +    return NS_ERROR_ABORT;
   1.806 +  }
   1.807 +
   1.808 +#ifdef CACHE_CHUNKS
   1.809 +  mCachedChunks.Clear();
   1.810 +#else
   1.811 +  // If we don't cache all chunks, mCachedChunks must be empty.
   1.812 +  MOZ_ASSERT(mCachedChunks.Count() == 0);
   1.813 +#endif
   1.814 +
   1.815 +  return NS_OK;
   1.816 +}
   1.817 +
   1.818 +nsresult
   1.819 +CacheFile::GetElement(const char *aKey, char **_retval)
   1.820 +{
   1.821 +  CacheFileAutoLock lock(this);
   1.822 +  MOZ_ASSERT(mMetadata);
   1.823 +  NS_ENSURE_TRUE(mMetadata, NS_ERROR_UNEXPECTED);
   1.824 +
   1.825 +  const char *value;
   1.826 +  value = mMetadata->GetElement(aKey);
   1.827 +  if (!value)
   1.828 +    return NS_ERROR_NOT_AVAILABLE;
   1.829 +
   1.830 +  *_retval = NS_strdup(value);
   1.831 +  return NS_OK;
   1.832 +}
   1.833 +
   1.834 +nsresult
   1.835 +CacheFile::SetElement(const char *aKey, const char *aValue)
   1.836 +{
   1.837 +  CacheFileAutoLock lock(this);
   1.838 +  MOZ_ASSERT(mMetadata);
   1.839 +  NS_ENSURE_TRUE(mMetadata, NS_ERROR_UNEXPECTED);
   1.840 +
   1.841 +  PostWriteTimer();
   1.842 +  return mMetadata->SetElement(aKey, aValue);
   1.843 +}
   1.844 +
   1.845 +nsresult
   1.846 +CacheFile::ElementsSize(uint32_t *_retval)
   1.847 +{
   1.848 +  CacheFileAutoLock lock(this);
   1.849 +
   1.850 +  if (!mMetadata)
   1.851 +    return NS_ERROR_NOT_AVAILABLE;
   1.852 +
   1.853 +  *_retval = mMetadata->ElementsSize();
   1.854 +  return NS_OK;
   1.855 +}
   1.856 +
   1.857 +nsresult
   1.858 +CacheFile::SetExpirationTime(uint32_t aExpirationTime)
   1.859 +{
   1.860 +  CacheFileAutoLock lock(this);
   1.861 +  MOZ_ASSERT(mMetadata);
   1.862 +  NS_ENSURE_TRUE(mMetadata, NS_ERROR_UNEXPECTED);
   1.863 +
   1.864 +  PostWriteTimer();
   1.865 +
   1.866 +  if (mHandle && !mHandle->IsDoomed())
   1.867 +    CacheFileIOManager::UpdateIndexEntry(mHandle, nullptr, &aExpirationTime);
   1.868 +
   1.869 +  return mMetadata->SetExpirationTime(aExpirationTime);
   1.870 +}
   1.871 +
   1.872 +nsresult
   1.873 +CacheFile::GetExpirationTime(uint32_t *_retval)
   1.874 +{
   1.875 +  CacheFileAutoLock lock(this);
   1.876 +  MOZ_ASSERT(mMetadata);
   1.877 +  NS_ENSURE_TRUE(mMetadata, NS_ERROR_UNEXPECTED);
   1.878 +
   1.879 +  return mMetadata->GetExpirationTime(_retval);
   1.880 +}
   1.881 +
   1.882 +nsresult
   1.883 +CacheFile::SetLastModified(uint32_t aLastModified)
   1.884 +{
   1.885 +  CacheFileAutoLock lock(this);
   1.886 +  MOZ_ASSERT(mMetadata);
   1.887 +  NS_ENSURE_TRUE(mMetadata, NS_ERROR_UNEXPECTED);
   1.888 +
   1.889 +  PostWriteTimer();
   1.890 +  return mMetadata->SetLastModified(aLastModified);
   1.891 +}
   1.892 +
   1.893 +nsresult
   1.894 +CacheFile::GetLastModified(uint32_t *_retval)
   1.895 +{
   1.896 +  CacheFileAutoLock lock(this);
   1.897 +  MOZ_ASSERT(mMetadata);
   1.898 +  NS_ENSURE_TRUE(mMetadata, NS_ERROR_UNEXPECTED);
   1.899 +
   1.900 +  return mMetadata->GetLastModified(_retval);
   1.901 +}
   1.902 +
   1.903 +nsresult
   1.904 +CacheFile::SetFrecency(uint32_t aFrecency)
   1.905 +{
   1.906 +  CacheFileAutoLock lock(this);
   1.907 +  MOZ_ASSERT(mMetadata);
   1.908 +  NS_ENSURE_TRUE(mMetadata, NS_ERROR_UNEXPECTED);
   1.909 +
   1.910 +  PostWriteTimer();
   1.911 +
   1.912 +  if (mHandle && !mHandle->IsDoomed())
   1.913 +    CacheFileIOManager::UpdateIndexEntry(mHandle, &aFrecency, nullptr);
   1.914 +
   1.915 +  return mMetadata->SetFrecency(aFrecency);
   1.916 +}
   1.917 +
   1.918 +nsresult
   1.919 +CacheFile::GetFrecency(uint32_t *_retval)
   1.920 +{
   1.921 +  CacheFileAutoLock lock(this);
   1.922 +  MOZ_ASSERT(mMetadata);
   1.923 +  NS_ENSURE_TRUE(mMetadata, NS_ERROR_UNEXPECTED);
   1.924 +
   1.925 +  return mMetadata->GetFrecency(_retval);
   1.926 +}
   1.927 +
   1.928 +nsresult
   1.929 +CacheFile::GetLastFetched(uint32_t *_retval)
   1.930 +{
   1.931 +  CacheFileAutoLock lock(this);
   1.932 +  MOZ_ASSERT(mMetadata);
   1.933 +  NS_ENSURE_TRUE(mMetadata, NS_ERROR_UNEXPECTED);
   1.934 +
   1.935 +  return mMetadata->GetLastFetched(_retval);
   1.936 +}
   1.937 +
   1.938 +nsresult
   1.939 +CacheFile::GetFetchCount(uint32_t *_retval)
   1.940 +{
   1.941 +  CacheFileAutoLock lock(this);
   1.942 +  MOZ_ASSERT(mMetadata);
   1.943 +  NS_ENSURE_TRUE(mMetadata, NS_ERROR_UNEXPECTED);
   1.944 +
   1.945 +  return mMetadata->GetFetchCount(_retval);
   1.946 +}
   1.947 +
   1.948 +void
   1.949 +CacheFile::Lock()
   1.950 +{
   1.951 +  mLock.Lock();
   1.952 +}
   1.953 +
   1.954 +void
   1.955 +CacheFile::Unlock()
   1.956 +{
   1.957 +  nsTArray<nsISupports*> objs;
   1.958 +  objs.SwapElements(mObjsToRelease);
   1.959 +
   1.960 +  mLock.Unlock();
   1.961 +
   1.962 +  for (uint32_t i = 0; i < objs.Length(); i++)
   1.963 +    objs[i]->Release();
   1.964 +}
   1.965 +
   1.966 +void
   1.967 +CacheFile::AssertOwnsLock() const
   1.968 +{
   1.969 +  mLock.AssertCurrentThreadOwns();
   1.970 +}
   1.971 +
   1.972 +void
   1.973 +CacheFile::ReleaseOutsideLock(nsISupports *aObject)
   1.974 +{
   1.975 +  AssertOwnsLock();
   1.976 +
   1.977 +  mObjsToRelease.AppendElement(aObject);
   1.978 +}
   1.979 +
   1.980 +nsresult
   1.981 +CacheFile::GetChunk(uint32_t aIndex, bool aWriter,
   1.982 +                    CacheFileChunkListener *aCallback, CacheFileChunk **_retval)
   1.983 +{
   1.984 +  CacheFileAutoLock lock(this);
   1.985 +  return GetChunkLocked(aIndex, aWriter, aCallback, _retval);
   1.986 +}
   1.987 +
   1.988 +nsresult
   1.989 +CacheFile::GetChunkLocked(uint32_t aIndex, bool aWriter,
   1.990 +                          CacheFileChunkListener *aCallback,
   1.991 +                          CacheFileChunk **_retval)
   1.992 +{
   1.993 +  AssertOwnsLock();
   1.994 +
   1.995 +  LOG(("CacheFile::GetChunkLocked() [this=%p, idx=%d, writer=%d, listener=%p]",
   1.996 +       this, aIndex, aWriter, aCallback));
   1.997 +
   1.998 +  MOZ_ASSERT(mReady);
   1.999 +  MOZ_ASSERT(mHandle || mMemoryOnly || mOpeningFile);
  1.1000 +  MOZ_ASSERT((aWriter && !aCallback) || (!aWriter && aCallback));
  1.1001 +
  1.1002 +  nsresult rv;
  1.1003 +
  1.1004 +  nsRefPtr<CacheFileChunk> chunk;
  1.1005 +  if (mChunks.Get(aIndex, getter_AddRefs(chunk))) {
  1.1006 +    LOG(("CacheFile::GetChunkLocked() - Found chunk %p in mChunks [this=%p]",
  1.1007 +         chunk.get(), this));
  1.1008 +
  1.1009 +    // We might get failed chunk between releasing the lock in
  1.1010 +    // CacheFileChunk::OnDataWritten/Read and CacheFile::OnChunkWritten/Read
  1.1011 +    rv = chunk->GetStatus();
  1.1012 +    if (NS_FAILED(rv)) {
  1.1013 +      SetError(rv);
  1.1014 +      LOG(("CacheFile::GetChunkLocked() - Found failed chunk in mChunks "
  1.1015 +           "[this=%p]", this));
  1.1016 +      return rv;
  1.1017 +    }
  1.1018 +
  1.1019 +    if (chunk->IsReady() || aWriter) {
  1.1020 +      chunk.swap(*_retval);
  1.1021 +    }
  1.1022 +    else {
  1.1023 +      rv = QueueChunkListener(aIndex, aCallback);
  1.1024 +      NS_ENSURE_SUCCESS(rv, rv);
  1.1025 +    }
  1.1026 +
  1.1027 +    return NS_OK;
  1.1028 +  }
  1.1029 +
  1.1030 +  if (mCachedChunks.Get(aIndex, getter_AddRefs(chunk))) {
  1.1031 +#ifndef CACHE_CHUNKS
  1.1032 +    // We don't cache all chunks, so we must not have handle and we must be
  1.1033 +    // either waiting for the handle, or this is memory-only entry.
  1.1034 +    MOZ_ASSERT(!mHandle && (mMemoryOnly || mOpeningFile));
  1.1035 +#endif
  1.1036 +    LOG(("CacheFile::GetChunkLocked() - Reusing cached chunk %p [this=%p]",
  1.1037 +         chunk.get(), this));
  1.1038 +
  1.1039 +    mChunks.Put(aIndex, chunk);
  1.1040 +    mCachedChunks.Remove(aIndex);
  1.1041 +    chunk->mFile = this;
  1.1042 +    chunk->mRemovingChunk = false;
  1.1043 +
  1.1044 +    MOZ_ASSERT(chunk->IsReady());
  1.1045 +
  1.1046 +    chunk.swap(*_retval);
  1.1047 +    return NS_OK;
  1.1048 +  }
  1.1049 +
  1.1050 +  int64_t off = aIndex * kChunkSize;
  1.1051 +
  1.1052 +  if (off < mDataSize) {
  1.1053 +    // We cannot be here if this is memory only entry since the chunk must exist
  1.1054 +    MOZ_ASSERT(!mMemoryOnly);
  1.1055 +    if (mMemoryOnly) {
  1.1056 +      // If this ever really happen it is better to fail rather than crashing on
  1.1057 +      // a null handle.
  1.1058 +      LOG(("CacheFile::GetChunkLocked() - Unexpected state! Offset < mDataSize "
  1.1059 +           "for memory-only entry. [this=%p, off=%lld, mDataSize=%lld]",
  1.1060 +           this, off, mDataSize));
  1.1061 +
  1.1062 +      return NS_ERROR_UNEXPECTED;
  1.1063 +    }
  1.1064 +
  1.1065 +    chunk = new CacheFileChunk(this, aIndex);
  1.1066 +    mChunks.Put(aIndex, chunk);
  1.1067 +
  1.1068 +    LOG(("CacheFile::GetChunkLocked() - Reading newly created chunk %p from "
  1.1069 +         "the disk [this=%p]", chunk.get(), this));
  1.1070 +
  1.1071 +    // Read the chunk from the disk
  1.1072 +    rv = chunk->Read(mHandle, std::min(static_cast<uint32_t>(mDataSize - off),
  1.1073 +                     static_cast<uint32_t>(kChunkSize)),
  1.1074 +                     mMetadata->GetHash(aIndex), this);
  1.1075 +    if (NS_WARN_IF(NS_FAILED(rv))) {
  1.1076 +      RemoveChunkInternal(chunk, false);
  1.1077 +      return rv;
  1.1078 +    }
  1.1079 +
  1.1080 +    if (aWriter) {
  1.1081 +      chunk.swap(*_retval);
  1.1082 +    }
  1.1083 +    else {
  1.1084 +      rv = QueueChunkListener(aIndex, aCallback);
  1.1085 +      NS_ENSURE_SUCCESS(rv, rv);
  1.1086 +    }
  1.1087 +
  1.1088 +    return NS_OK;
  1.1089 +  }
  1.1090 +  else if (off == mDataSize) {
  1.1091 +    if (aWriter) {
  1.1092 +      // this listener is going to write to the chunk
  1.1093 +      chunk = new CacheFileChunk(this, aIndex);
  1.1094 +      mChunks.Put(aIndex, chunk);
  1.1095 +
  1.1096 +      LOG(("CacheFile::GetChunkLocked() - Created new empty chunk %p [this=%p]",
  1.1097 +           chunk.get(), this));
  1.1098 +
  1.1099 +      chunk->InitNew(this);
  1.1100 +      mMetadata->SetHash(aIndex, chunk->Hash());
  1.1101 +
  1.1102 +      if (HaveChunkListeners(aIndex)) {
  1.1103 +        rv = NotifyChunkListeners(aIndex, NS_OK, chunk);
  1.1104 +        NS_ENSURE_SUCCESS(rv, rv);
  1.1105 +      }
  1.1106 +
  1.1107 +      chunk.swap(*_retval);
  1.1108 +      return NS_OK;
  1.1109 +    }
  1.1110 +  }
  1.1111 +  else {
  1.1112 +    if (aWriter) {
  1.1113 +      // this chunk was requested by writer, but we need to fill the gap first
  1.1114 +
  1.1115 +      // Fill with zero the last chunk if it is incomplete
  1.1116 +      if (mDataSize % kChunkSize) {
  1.1117 +        rv = PadChunkWithZeroes(mDataSize / kChunkSize);
  1.1118 +        NS_ENSURE_SUCCESS(rv, rv);
  1.1119 +
  1.1120 +        MOZ_ASSERT(!(mDataSize % kChunkSize));
  1.1121 +      }
  1.1122 +
  1.1123 +      uint32_t startChunk = mDataSize / kChunkSize;
  1.1124 +
  1.1125 +      if (mMemoryOnly) {
  1.1126 +        // We need to create all missing CacheFileChunks if this is memory-only
  1.1127 +        // entry
  1.1128 +        for (uint32_t i = startChunk ; i < aIndex ; i++) {
  1.1129 +          rv = PadChunkWithZeroes(i);
  1.1130 +          NS_ENSURE_SUCCESS(rv, rv);
  1.1131 +        }
  1.1132 +      }
  1.1133 +      else {
  1.1134 +        // We don't need to create CacheFileChunk for other empty chunks unless
  1.1135 +        // there is some input stream waiting for this chunk.
  1.1136 +
  1.1137 +        if (startChunk != aIndex) {
  1.1138 +          // Make sure the file contains zeroes at the end of the file
  1.1139 +          rv = CacheFileIOManager::TruncateSeekSetEOF(mHandle,
  1.1140 +                                                      startChunk * kChunkSize,
  1.1141 +                                                      aIndex * kChunkSize,
  1.1142 +                                                      nullptr);
  1.1143 +          NS_ENSURE_SUCCESS(rv, rv);
  1.1144 +        }
  1.1145 +
  1.1146 +        for (uint32_t i = startChunk ; i < aIndex ; i++) {
  1.1147 +          if (HaveChunkListeners(i)) {
  1.1148 +            rv = PadChunkWithZeroes(i);
  1.1149 +            NS_ENSURE_SUCCESS(rv, rv);
  1.1150 +          }
  1.1151 +          else {
  1.1152 +            mMetadata->SetHash(i, kEmptyChunkHash);
  1.1153 +            mDataSize = (i + 1) * kChunkSize;
  1.1154 +          }
  1.1155 +        }
  1.1156 +      }
  1.1157 +
  1.1158 +      MOZ_ASSERT(mDataSize == off);
  1.1159 +      rv = GetChunkLocked(aIndex, true, nullptr, getter_AddRefs(chunk));
  1.1160 +      NS_ENSURE_SUCCESS(rv, rv);
  1.1161 +
  1.1162 +      chunk.swap(*_retval);
  1.1163 +      return NS_OK;
  1.1164 +    }
  1.1165 +  }
  1.1166 +
  1.1167 +  if (mOutput) {
  1.1168 +    // the chunk doesn't exist but mOutput may create it
  1.1169 +    rv = QueueChunkListener(aIndex, aCallback);
  1.1170 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1171 +  }
  1.1172 +  else {
  1.1173 +    return NS_ERROR_NOT_AVAILABLE;
  1.1174 +  }
  1.1175 +
  1.1176 +  return NS_OK;
  1.1177 +}
  1.1178 +
  1.1179 +nsresult
  1.1180 +CacheFile::RemoveChunk(CacheFileChunk *aChunk)
  1.1181 +{
  1.1182 +  nsresult rv;
  1.1183 +
  1.1184 +  // Avoid lock reentrancy by increasing the RefCnt
  1.1185 +  nsRefPtr<CacheFileChunk> chunk = aChunk;
  1.1186 +
  1.1187 +  {
  1.1188 +    CacheFileAutoLock lock(this);
  1.1189 +
  1.1190 +    LOG(("CacheFile::RemoveChunk() [this=%p, chunk=%p, idx=%d]",
  1.1191 +         this, aChunk, aChunk->Index()));
  1.1192 +
  1.1193 +    MOZ_ASSERT(mReady);
  1.1194 +    MOZ_ASSERT((mHandle && !mMemoryOnly && !mOpeningFile) ||
  1.1195 +               (!mHandle && mMemoryOnly && !mOpeningFile) ||
  1.1196 +               (!mHandle && !mMemoryOnly && mOpeningFile));
  1.1197 +
  1.1198 +    if (aChunk->mRefCnt != 2) {
  1.1199 +      LOG(("CacheFile::RemoveChunk() - Chunk is still used [this=%p, chunk=%p, "
  1.1200 +           "refcnt=%d]", this, aChunk, aChunk->mRefCnt.get()));
  1.1201 +
  1.1202 +      // somebody got the reference before the lock was acquired
  1.1203 +      return NS_OK;
  1.1204 +    }
  1.1205 +
  1.1206 +#ifdef DEBUG
  1.1207 +    {
  1.1208 +      // We can be here iff the chunk is in the hash table
  1.1209 +      nsRefPtr<CacheFileChunk> chunkCheck;
  1.1210 +      mChunks.Get(chunk->Index(), getter_AddRefs(chunkCheck));
  1.1211 +      MOZ_ASSERT(chunkCheck == chunk);
  1.1212 +
  1.1213 +      // We also shouldn't have any queued listener for this chunk
  1.1214 +      ChunkListeners *listeners;
  1.1215 +      mChunkListeners.Get(chunk->Index(), &listeners);
  1.1216 +      MOZ_ASSERT(!listeners);
  1.1217 +    }
  1.1218 +#endif
  1.1219 +
  1.1220 +    if (NS_FAILED(mStatus)) {
  1.1221 +      // Don't write any chunk to disk since this entry will be doomed
  1.1222 +      LOG(("CacheFile::RemoveChunk() - Removing chunk because of status "
  1.1223 +           "[this=%p, chunk=%p, mStatus=0x%08x]", this, chunk.get(), mStatus));
  1.1224 +
  1.1225 +      RemoveChunkInternal(chunk, false);
  1.1226 +      return mStatus;
  1.1227 +    }
  1.1228 +
  1.1229 +    if (chunk->IsDirty() && !mMemoryOnly && !mOpeningFile) {
  1.1230 +      LOG(("CacheFile::RemoveChunk() - Writing dirty chunk to the disk "
  1.1231 +           "[this=%p]", this));
  1.1232 +
  1.1233 +      mDataIsDirty = true;
  1.1234 +
  1.1235 +      rv = chunk->Write(mHandle, this);
  1.1236 +      if (NS_FAILED(rv)) {
  1.1237 +        LOG(("CacheFile::RemoveChunk() - CacheFileChunk::Write() failed "
  1.1238 +             "synchronously. Removing it. [this=%p, chunk=%p, rv=0x%08x]",
  1.1239 +             this, chunk.get(), rv));
  1.1240 +
  1.1241 +        RemoveChunkInternal(chunk, false);
  1.1242 +
  1.1243 +        SetError(rv);
  1.1244 +        CacheFileIOManager::DoomFile(mHandle, nullptr);
  1.1245 +        return rv;
  1.1246 +      }
  1.1247 +      else {
  1.1248 +        // Chunk will be removed in OnChunkWritten if it is still unused
  1.1249 +
  1.1250 +        // chunk needs to be released under the lock to be able to rely on
  1.1251 +        // CacheFileChunk::mRefCnt in CacheFile::OnChunkWritten()
  1.1252 +        chunk = nullptr;
  1.1253 +        return NS_OK;
  1.1254 +      }
  1.1255 +    }
  1.1256 +
  1.1257 +#ifdef CACHE_CHUNKS
  1.1258 +    LOG(("CacheFile::RemoveChunk() - Caching unused chunk [this=%p, chunk=%p]",
  1.1259 +         this, chunk.get()));
  1.1260 +#else
  1.1261 +    if (mMemoryOnly || mOpeningFile) {
  1.1262 +      LOG(("CacheFile::RemoveChunk() - Caching unused chunk [this=%p, chunk=%p,"
  1.1263 +           " reason=%s]", this, chunk.get(),
  1.1264 +           mMemoryOnly ? "memory-only" : "opening-file"));
  1.1265 +    } else {
  1.1266 +      LOG(("CacheFile::RemoveChunk() - Releasing unused chunk [this=%p, "
  1.1267 +           "chunk=%p]", this, chunk.get()));
  1.1268 +    }
  1.1269 +#endif
  1.1270 +
  1.1271 +    RemoveChunkInternal(chunk,
  1.1272 +#ifdef CACHE_CHUNKS
  1.1273 +                        true);
  1.1274 +#else
  1.1275 +                        // Cache the chunk only when we have a reason to do so
  1.1276 +                        mMemoryOnly || mOpeningFile);
  1.1277 +#endif
  1.1278 +
  1.1279 +    if (!mMemoryOnly)
  1.1280 +      WriteMetadataIfNeededLocked();
  1.1281 +  }
  1.1282 +
  1.1283 +  return NS_OK;
  1.1284 +}
  1.1285 +
  1.1286 +void
  1.1287 +CacheFile::RemoveChunkInternal(CacheFileChunk *aChunk, bool aCacheChunk)
  1.1288 +{
  1.1289 +  aChunk->mRemovingChunk = true;
  1.1290 +  ReleaseOutsideLock(static_cast<CacheFileChunkListener *>(
  1.1291 +                       aChunk->mFile.forget().take()));
  1.1292 +
  1.1293 +  if (aCacheChunk) {
  1.1294 +    mCachedChunks.Put(aChunk->Index(), aChunk);
  1.1295 +  }
  1.1296 +
  1.1297 +  mChunks.Remove(aChunk->Index());
  1.1298 +}
  1.1299 +
  1.1300 +nsresult
  1.1301 +CacheFile::RemoveInput(CacheFileInputStream *aInput)
  1.1302 +{
  1.1303 +  CacheFileAutoLock lock(this);
  1.1304 +
  1.1305 +  LOG(("CacheFile::RemoveInput() [this=%p, input=%p]", this, aInput));
  1.1306 +
  1.1307 +  DebugOnly<bool> found;
  1.1308 +  found = mInputs.RemoveElement(aInput);
  1.1309 +  MOZ_ASSERT(found);
  1.1310 +
  1.1311 +  ReleaseOutsideLock(static_cast<nsIInputStream*>(aInput));
  1.1312 +
  1.1313 +  if (!mMemoryOnly)
  1.1314 +    WriteMetadataIfNeededLocked();
  1.1315 +
  1.1316 +  return NS_OK;
  1.1317 +}
  1.1318 +
  1.1319 +nsresult
  1.1320 +CacheFile::RemoveOutput(CacheFileOutputStream *aOutput)
  1.1321 +{
  1.1322 +  AssertOwnsLock();
  1.1323 +
  1.1324 +  LOG(("CacheFile::RemoveOutput() [this=%p, output=%p]", this, aOutput));
  1.1325 +
  1.1326 +  if (mOutput != aOutput) {
  1.1327 +    LOG(("CacheFile::RemoveOutput() - This output was already removed, ignoring"
  1.1328 +         " call [this=%p]", this));
  1.1329 +    return NS_OK;
  1.1330 +  }
  1.1331 +
  1.1332 +  mOutput = nullptr;
  1.1333 +
  1.1334 +  // Cancel all queued chunk and update listeners that cannot be satisfied
  1.1335 +  NotifyListenersAboutOutputRemoval();
  1.1336 +
  1.1337 +  if (!mMemoryOnly)
  1.1338 +    WriteMetadataIfNeededLocked();
  1.1339 +
  1.1340 +  // Notify close listener as the last action
  1.1341 +  aOutput->NotifyCloseListener();
  1.1342 +
  1.1343 +  return NS_OK;
  1.1344 +}
  1.1345 +
  1.1346 +nsresult
  1.1347 +CacheFile::NotifyChunkListener(CacheFileChunkListener *aCallback,
  1.1348 +                               nsIEventTarget *aTarget,
  1.1349 +                               nsresult aResult,
  1.1350 +                               uint32_t aChunkIdx,
  1.1351 +                               CacheFileChunk *aChunk)
  1.1352 +{
  1.1353 +  LOG(("CacheFile::NotifyChunkListener() [this=%p, listener=%p, target=%p, "
  1.1354 +       "rv=0x%08x, idx=%d, chunk=%p]", this, aCallback, aTarget, aResult,
  1.1355 +       aChunkIdx, aChunk));
  1.1356 +
  1.1357 +  nsresult rv;
  1.1358 +  nsRefPtr<NotifyChunkListenerEvent> ev;
  1.1359 +  ev = new NotifyChunkListenerEvent(aCallback, aResult, aChunkIdx, aChunk);
  1.1360 +  if (aTarget)
  1.1361 +    rv = aTarget->Dispatch(ev, NS_DISPATCH_NORMAL);
  1.1362 +  else
  1.1363 +    rv = NS_DispatchToCurrentThread(ev);
  1.1364 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1365 +
  1.1366 +  return NS_OK;
  1.1367 +}
  1.1368 +
  1.1369 +nsresult
  1.1370 +CacheFile::QueueChunkListener(uint32_t aIndex,
  1.1371 +                              CacheFileChunkListener *aCallback)
  1.1372 +{
  1.1373 +  LOG(("CacheFile::QueueChunkListener() [this=%p, idx=%d, listener=%p]",
  1.1374 +       this, aIndex, aCallback));
  1.1375 +
  1.1376 +  AssertOwnsLock();
  1.1377 +
  1.1378 +  MOZ_ASSERT(aCallback);
  1.1379 +
  1.1380 +  ChunkListenerItem *item = new ChunkListenerItem();
  1.1381 +  item->mTarget = NS_GetCurrentThread();
  1.1382 +  item->mCallback = aCallback;
  1.1383 +
  1.1384 +  ChunkListeners *listeners;
  1.1385 +  if (!mChunkListeners.Get(aIndex, &listeners)) {
  1.1386 +    listeners = new ChunkListeners();
  1.1387 +    mChunkListeners.Put(aIndex, listeners);
  1.1388 +  }
  1.1389 +
  1.1390 +  listeners->mItems.AppendElement(item);
  1.1391 +  return NS_OK;
  1.1392 +}
  1.1393 +
  1.1394 +nsresult
  1.1395 +CacheFile::NotifyChunkListeners(uint32_t aIndex, nsresult aResult,
  1.1396 +                                CacheFileChunk *aChunk)
  1.1397 +{
  1.1398 +  LOG(("CacheFile::NotifyChunkListeners() [this=%p, idx=%d, rv=0x%08x, "
  1.1399 +       "chunk=%p]", this, aIndex, aResult, aChunk));
  1.1400 +
  1.1401 +  AssertOwnsLock();
  1.1402 +
  1.1403 +  nsresult rv, rv2;
  1.1404 +
  1.1405 +  ChunkListeners *listeners;
  1.1406 +  mChunkListeners.Get(aIndex, &listeners);
  1.1407 +  MOZ_ASSERT(listeners);
  1.1408 +
  1.1409 +  rv = NS_OK;
  1.1410 +  for (uint32_t i = 0 ; i < listeners->mItems.Length() ; i++) {
  1.1411 +    ChunkListenerItem *item = listeners->mItems[i];
  1.1412 +    rv2 = NotifyChunkListener(item->mCallback, item->mTarget, aResult, aIndex,
  1.1413 +                              aChunk);
  1.1414 +    if (NS_FAILED(rv2) && NS_SUCCEEDED(rv))
  1.1415 +      rv = rv2;
  1.1416 +    delete item;
  1.1417 +  }
  1.1418 +
  1.1419 +  mChunkListeners.Remove(aIndex);
  1.1420 +
  1.1421 +  return rv;
  1.1422 +}
  1.1423 +
  1.1424 +bool
  1.1425 +CacheFile::HaveChunkListeners(uint32_t aIndex)
  1.1426 +{
  1.1427 +  ChunkListeners *listeners;
  1.1428 +  mChunkListeners.Get(aIndex, &listeners);
  1.1429 +  return !!listeners;
  1.1430 +}
  1.1431 +
  1.1432 +void
  1.1433 +CacheFile::NotifyListenersAboutOutputRemoval()
  1.1434 +{
  1.1435 +  LOG(("CacheFile::NotifyListenersAboutOutputRemoval() [this=%p]", this));
  1.1436 +
  1.1437 +  AssertOwnsLock();
  1.1438 +
  1.1439 +  // First fail all chunk listeners that wait for non-existent chunk
  1.1440 +  mChunkListeners.Enumerate(&CacheFile::FailListenersIfNonExistentChunk,
  1.1441 +                            this);
  1.1442 +
  1.1443 +  // Fail all update listeners
  1.1444 +  mChunks.Enumerate(&CacheFile::FailUpdateListeners, this);
  1.1445 +}
  1.1446 +
  1.1447 +bool
  1.1448 +CacheFile::DataSize(int64_t* aSize)
  1.1449 +{
  1.1450 +  CacheFileAutoLock lock(this);
  1.1451 +
  1.1452 +  if (mOutput)
  1.1453 +    return false;
  1.1454 +
  1.1455 +  *aSize = mDataSize;
  1.1456 +  return true;
  1.1457 +}
  1.1458 +
  1.1459 +bool
  1.1460 +CacheFile::IsDoomed()
  1.1461 +{
  1.1462 +  CacheFileAutoLock lock(this);
  1.1463 +
  1.1464 +  if (!mHandle)
  1.1465 +    return false;
  1.1466 +
  1.1467 +  return mHandle->IsDoomed();
  1.1468 +}
  1.1469 +
  1.1470 +bool
  1.1471 +CacheFile::IsWriteInProgress()
  1.1472 +{
  1.1473 +  // Returns true when there is a potentially unfinished write operation.
  1.1474 +  // Not using lock for performance reasons.  mMetadata is never released
  1.1475 +  // during life time of CacheFile.
  1.1476 +  return
  1.1477 +    mDataIsDirty ||
  1.1478 +    (mMetadata && mMetadata->IsDirty()) ||
  1.1479 +    mWritingMetadata ||
  1.1480 +    mOpeningFile ||
  1.1481 +    mOutput ||
  1.1482 +    mChunks.Count();
  1.1483 +}
  1.1484 +
  1.1485 +bool
  1.1486 +CacheFile::IsDirty()
  1.1487 +{
  1.1488 +  return mDataIsDirty || mMetadata->IsDirty();
  1.1489 +}
  1.1490 +
  1.1491 +void
  1.1492 +CacheFile::WriteMetadataIfNeeded()
  1.1493 +{
  1.1494 +  LOG(("CacheFile::WriteMetadataIfNeeded() [this=%p]", this));
  1.1495 +
  1.1496 +  CacheFileAutoLock lock(this);
  1.1497 +
  1.1498 +  if (!mMemoryOnly)
  1.1499 +    WriteMetadataIfNeededLocked();
  1.1500 +}
  1.1501 +
  1.1502 +void
  1.1503 +CacheFile::WriteMetadataIfNeededLocked(bool aFireAndForget)
  1.1504 +{
  1.1505 +  // When aFireAndForget is set to true, we are called from dtor.
  1.1506 +  // |this| must not be referenced after this method returns!
  1.1507 +
  1.1508 +  LOG(("CacheFile::WriteMetadataIfNeededLocked() [this=%p]", this));
  1.1509 +
  1.1510 +  nsresult rv;
  1.1511 +
  1.1512 +  AssertOwnsLock();
  1.1513 +  MOZ_ASSERT(!mMemoryOnly);
  1.1514 +
  1.1515 +  if (!mMetadata) {
  1.1516 +    MOZ_CRASH("Must have metadata here");
  1.1517 +    return;
  1.1518 +  }
  1.1519 +
  1.1520 +  if (!aFireAndForget) {
  1.1521 +    // if aFireAndForget is set, we are called from dtor. Write
  1.1522 +    // scheduler hard-refers CacheFile otherwise, so we cannot be here.
  1.1523 +    CacheFileIOManager::UnscheduleMetadataWrite(this);
  1.1524 +  }
  1.1525 +
  1.1526 +  if (NS_FAILED(mStatus))
  1.1527 +    return;
  1.1528 +
  1.1529 +  if (!IsDirty() || mOutput || mInputs.Length() || mChunks.Count() ||
  1.1530 +      mWritingMetadata || mOpeningFile)
  1.1531 +    return;
  1.1532 +
  1.1533 +  LOG(("CacheFile::WriteMetadataIfNeededLocked() - Writing metadata [this=%p]",
  1.1534 +       this));
  1.1535 +
  1.1536 +  rv = mMetadata->WriteMetadata(mDataSize, aFireAndForget ? nullptr : this);
  1.1537 +  if (NS_SUCCEEDED(rv)) {
  1.1538 +    mWritingMetadata = true;
  1.1539 +    mDataIsDirty = false;
  1.1540 +  } else {
  1.1541 +    LOG(("CacheFile::WriteMetadataIfNeededLocked() - Writing synchronously "
  1.1542 +         "failed [this=%p]", this));
  1.1543 +    // TODO: close streams with error
  1.1544 +    SetError(rv);
  1.1545 +  }
  1.1546 +}
  1.1547 +
  1.1548 +void
  1.1549 +CacheFile::PostWriteTimer()
  1.1550 +{
  1.1551 +  LOG(("CacheFile::PostWriteTimer() [this=%p]", this));
  1.1552 +
  1.1553 +  CacheFileIOManager::ScheduleMetadataWrite(this);
  1.1554 +}
  1.1555 +
  1.1556 +PLDHashOperator
  1.1557 +CacheFile::WriteAllCachedChunks(const uint32_t& aIdx,
  1.1558 +                                nsRefPtr<CacheFileChunk>& aChunk,
  1.1559 +                                void* aClosure)
  1.1560 +{
  1.1561 +  CacheFile *file = static_cast<CacheFile*>(aClosure);
  1.1562 +
  1.1563 +  LOG(("CacheFile::WriteAllCachedChunks() [this=%p, idx=%d, chunk=%p]",
  1.1564 +       file, aIdx, aChunk.get()));
  1.1565 +
  1.1566 +  file->mChunks.Put(aIdx, aChunk);
  1.1567 +  aChunk->mFile = file;
  1.1568 +  aChunk->mRemovingChunk = false;
  1.1569 +
  1.1570 +  MOZ_ASSERT(aChunk->IsReady());
  1.1571 +
  1.1572 +  NS_ADDREF(aChunk);
  1.1573 +  file->ReleaseOutsideLock(aChunk);
  1.1574 +
  1.1575 +  return PL_DHASH_REMOVE;
  1.1576 +}
  1.1577 +
  1.1578 +PLDHashOperator
  1.1579 +CacheFile::FailListenersIfNonExistentChunk(
  1.1580 +  const uint32_t& aIdx,
  1.1581 +  nsAutoPtr<ChunkListeners>& aListeners,
  1.1582 +  void* aClosure)
  1.1583 +{
  1.1584 +  CacheFile *file = static_cast<CacheFile*>(aClosure);
  1.1585 +
  1.1586 +  LOG(("CacheFile::FailListenersIfNonExistentChunk() [this=%p, idx=%d]",
  1.1587 +       file, aIdx));
  1.1588 +
  1.1589 +  nsRefPtr<CacheFileChunk> chunk;
  1.1590 +  file->mChunks.Get(aIdx, getter_AddRefs(chunk));
  1.1591 +  if (chunk) {
  1.1592 +    MOZ_ASSERT(!chunk->IsReady());
  1.1593 +    return PL_DHASH_NEXT;
  1.1594 +  }
  1.1595 +
  1.1596 +  for (uint32_t i = 0 ; i < aListeners->mItems.Length() ; i++) {
  1.1597 +    ChunkListenerItem *item = aListeners->mItems[i];
  1.1598 +    file->NotifyChunkListener(item->mCallback, item->mTarget,
  1.1599 +                              NS_ERROR_NOT_AVAILABLE, aIdx, nullptr);
  1.1600 +    delete item;
  1.1601 +  }
  1.1602 +
  1.1603 +  return PL_DHASH_REMOVE;
  1.1604 +}
  1.1605 +
  1.1606 +PLDHashOperator
  1.1607 +CacheFile::FailUpdateListeners(
  1.1608 +  const uint32_t& aIdx,
  1.1609 +  nsRefPtr<CacheFileChunk>& aChunk,
  1.1610 +  void* aClosure)
  1.1611 +{
  1.1612 +#ifdef PR_LOGGING
  1.1613 +  CacheFile *file = static_cast<CacheFile*>(aClosure);
  1.1614 +#endif
  1.1615 +
  1.1616 +  LOG(("CacheFile::FailUpdateListeners() [this=%p, idx=%d]",
  1.1617 +       file, aIdx));
  1.1618 +
  1.1619 +  if (aChunk->IsReady()) {
  1.1620 +    aChunk->NotifyUpdateListeners();
  1.1621 +  }
  1.1622 +
  1.1623 +  return PL_DHASH_NEXT;
  1.1624 +}
  1.1625 +
  1.1626 +nsresult
  1.1627 +CacheFile::PadChunkWithZeroes(uint32_t aChunkIdx)
  1.1628 +{
  1.1629 +  AssertOwnsLock();
  1.1630 +
  1.1631 +  // This method is used to pad last incomplete chunk with zeroes or create
  1.1632 +  // a new chunk full of zeroes
  1.1633 +  MOZ_ASSERT(mDataSize / kChunkSize == aChunkIdx);
  1.1634 +
  1.1635 +  nsresult rv;
  1.1636 +  nsRefPtr<CacheFileChunk> chunk;
  1.1637 +  rv = GetChunkLocked(aChunkIdx, true, nullptr, getter_AddRefs(chunk));
  1.1638 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1639 +
  1.1640 +  LOG(("CacheFile::PadChunkWithZeroes() - Zeroing hole in chunk %d, range %d-%d"
  1.1641 +       " [this=%p]", aChunkIdx, chunk->DataSize(), kChunkSize - 1, this));
  1.1642 +
  1.1643 +  chunk->EnsureBufSize(kChunkSize);
  1.1644 +  memset(chunk->BufForWriting() + chunk->DataSize(), 0, kChunkSize - chunk->DataSize());
  1.1645 +
  1.1646 +  chunk->UpdateDataSize(chunk->DataSize(), kChunkSize - chunk->DataSize(),
  1.1647 +                        false);
  1.1648 +
  1.1649 +  ReleaseOutsideLock(chunk.forget().take());
  1.1650 +
  1.1651 +  return NS_OK;
  1.1652 +}
  1.1653 +
  1.1654 +void
  1.1655 +CacheFile::SetError(nsresult aStatus)
  1.1656 +{
  1.1657 +  if (NS_SUCCEEDED(mStatus)) {
  1.1658 +    mStatus = aStatus;
  1.1659 +  }
  1.1660 +}
  1.1661 +
  1.1662 +nsresult
  1.1663 +CacheFile::InitIndexEntry()
  1.1664 +{
  1.1665 +  MOZ_ASSERT(mHandle);
  1.1666 +
  1.1667 +  if (mHandle->IsDoomed())
  1.1668 +    return NS_OK;
  1.1669 +
  1.1670 +  nsresult rv;
  1.1671 +
  1.1672 +  rv = CacheFileIOManager::InitIndexEntry(mHandle,
  1.1673 +                                          mMetadata->AppId(),
  1.1674 +                                          mMetadata->IsAnonymous(),
  1.1675 +                                          mMetadata->IsInBrowser());
  1.1676 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1677 +
  1.1678 +  uint32_t expTime;
  1.1679 +  mMetadata->GetExpirationTime(&expTime);
  1.1680 +
  1.1681 +  uint32_t frecency;
  1.1682 +  mMetadata->GetFrecency(&frecency);
  1.1683 +
  1.1684 +  rv = CacheFileIOManager::UpdateIndexEntry(mHandle, &frecency, &expTime);
  1.1685 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1686 +
  1.1687 +  return NS_OK;
  1.1688 +}
  1.1689 +
  1.1690 +// Memory reporting
  1.1691 +
  1.1692 +namespace { // anon
  1.1693 +
  1.1694 +size_t
  1.1695 +CollectChunkSize(uint32_t const & aIdx,
  1.1696 +                 nsRefPtr<mozilla::net::CacheFileChunk> const & aChunk,
  1.1697 +                 mozilla::MallocSizeOf mallocSizeOf, void* aClosure)
  1.1698 +{
  1.1699 +  return aChunk->SizeOfIncludingThis(mallocSizeOf);
  1.1700 +}
  1.1701 +
  1.1702 +} // anon
  1.1703 +
  1.1704 +size_t
  1.1705 +CacheFile::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
  1.1706 +{
  1.1707 +  CacheFileAutoLock lock(const_cast<CacheFile*>(this));
  1.1708 +
  1.1709 +  size_t n = 0;
  1.1710 +  n += mKey.SizeOfExcludingThisIfUnshared(mallocSizeOf);
  1.1711 +  n += mChunks.SizeOfExcludingThis(CollectChunkSize, mallocSizeOf);
  1.1712 +  n += mCachedChunks.SizeOfExcludingThis(CollectChunkSize, mallocSizeOf);
  1.1713 +  if (mMetadata) {
  1.1714 +    n += mMetadata->SizeOfIncludingThis(mallocSizeOf);
  1.1715 +  }
  1.1716 +
  1.1717 +  // Input streams are not elsewhere reported.
  1.1718 +  n += mInputs.SizeOfExcludingThis(mallocSizeOf);
  1.1719 +  for (uint32_t i = 0; i < mInputs.Length(); ++i) {
  1.1720 +    n += mInputs[i]->SizeOfIncludingThis(mallocSizeOf);
  1.1721 +  }
  1.1722 +
  1.1723 +  // Output streams are not elsewhere reported.
  1.1724 +  if (mOutput) {
  1.1725 +    n += mOutput->SizeOfIncludingThis(mallocSizeOf);
  1.1726 +  }
  1.1727 +
  1.1728 +  // The listeners are usually classes reported just above.
  1.1729 +  n += mChunkListeners.SizeOfExcludingThis(nullptr, mallocSizeOf);
  1.1730 +  n += mObjsToRelease.SizeOfExcludingThis(mallocSizeOf);
  1.1731 +
  1.1732 +  // mHandle reported directly from CacheFileIOManager.
  1.1733 +
  1.1734 +  return n;
  1.1735 +}
  1.1736 +
  1.1737 +size_t
  1.1738 +CacheFile::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
  1.1739 +{
  1.1740 +  return mallocSizeOf(this) + SizeOfExcludingThis(mallocSizeOf);
  1.1741 +}
  1.1742 +
  1.1743 +} // net
  1.1744 +} // mozilla

mercurial