netwerk/cache2/CacheFileIOManager.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/netwerk/cache2/CacheFileIOManager.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,3801 @@
     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 "CacheFileIOManager.h"
    1.10 +
    1.11 +#include "../cache/nsCacheUtils.h"
    1.12 +#include "CacheHashUtils.h"
    1.13 +#include "CacheStorageService.h"
    1.14 +#include "CacheIndex.h"
    1.15 +#include "CacheFileUtils.h"
    1.16 +#include "nsThreadUtils.h"
    1.17 +#include "CacheFile.h"
    1.18 +#include "CacheObserver.h"
    1.19 +#include "nsIFile.h"
    1.20 +#include "CacheFileContextEvictor.h"
    1.21 +#include "nsITimer.h"
    1.22 +#include "nsISimpleEnumerator.h"
    1.23 +#include "nsIDirectoryEnumerator.h"
    1.24 +#include "nsIObserverService.h"
    1.25 +#include "nsISizeOf.h"
    1.26 +#include "mozilla/Telemetry.h"
    1.27 +#include "mozilla/DebugOnly.h"
    1.28 +#include "mozilla/Services.h"
    1.29 +#include "nsDirectoryServiceUtils.h"
    1.30 +#include "nsAppDirectoryServiceDefs.h"
    1.31 +#include "private/pprio.h"
    1.32 +#include "mozilla/VisualEventTracer.h"
    1.33 +#include "mozilla/Preferences.h"
    1.34 +
    1.35 +// include files for ftruncate (or equivalent)
    1.36 +#if defined(XP_UNIX)
    1.37 +#include <unistd.h>
    1.38 +#elif defined(XP_WIN)
    1.39 +#include <windows.h>
    1.40 +#undef CreateFile
    1.41 +#undef CREATE_NEW
    1.42 +#else
    1.43 +// XXX add necessary include file for ftruncate (or equivalent)
    1.44 +#endif
    1.45 +
    1.46 +
    1.47 +namespace mozilla {
    1.48 +namespace net {
    1.49 +
    1.50 +#define kOpenHandlesLimit        64
    1.51 +#define kMetadataWriteDelay      5000
    1.52 +#define kRemoveTrashStartDelay   60000 // in milliseconds
    1.53 +#define kSmartSizeUpdateInterval 60000 // in milliseconds
    1.54 +
    1.55 +#ifdef ANDROID
    1.56 +const uint32_t kMaxCacheSizeKB = 200*1024; // 200 MB
    1.57 +#else
    1.58 +const uint32_t kMaxCacheSizeKB = 350*1024; // 350 MB
    1.59 +#endif
    1.60 +
    1.61 +bool
    1.62 +CacheFileHandle::DispatchRelease()
    1.63 +{
    1.64 +  if (CacheFileIOManager::IsOnIOThreadOrCeased()) {
    1.65 +    return false;
    1.66 +  }
    1.67 +
    1.68 +  nsCOMPtr<nsIEventTarget> ioTarget = CacheFileIOManager::IOTarget();
    1.69 +  if (!ioTarget) {
    1.70 +    return false;
    1.71 +  }
    1.72 +
    1.73 +  nsRefPtr<nsRunnableMethod<CacheFileHandle, MozExternalRefCountType, false> > event =
    1.74 +    NS_NewNonOwningRunnableMethod(this, &CacheFileHandle::Release);
    1.75 +  nsresult rv = ioTarget->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL);
    1.76 +  if (NS_FAILED(rv)) {
    1.77 +    return false;
    1.78 +  }
    1.79 +
    1.80 +  return true;
    1.81 +}
    1.82 +
    1.83 +NS_IMPL_ADDREF(CacheFileHandle)
    1.84 +NS_IMETHODIMP_(MozExternalRefCountType)
    1.85 +CacheFileHandle::Release()
    1.86 +{
    1.87 +  nsrefcnt count = mRefCnt - 1;
    1.88 +  if (DispatchRelease()) {
    1.89 +    // Redispatched to the IO thread.
    1.90 +    return count;
    1.91 +  }
    1.92 +
    1.93 +  MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
    1.94 +
    1.95 +  LOG(("CacheFileHandle::Release() [this=%p, refcnt=%d]", this, mRefCnt.get()));
    1.96 +  NS_PRECONDITION(0 != mRefCnt, "dup release");
    1.97 +  count = --mRefCnt;
    1.98 +  NS_LOG_RELEASE(this, count, "CacheFileHandle");
    1.99 +
   1.100 +  if (0 == count) {
   1.101 +    mRefCnt = 1;
   1.102 +    delete (this);
   1.103 +    return 0;
   1.104 +  }
   1.105 +
   1.106 +  return count;
   1.107 +}
   1.108 +
   1.109 +NS_INTERFACE_MAP_BEGIN(CacheFileHandle)
   1.110 +  NS_INTERFACE_MAP_ENTRY(nsISupports)
   1.111 +NS_INTERFACE_MAP_END_THREADSAFE
   1.112 +
   1.113 +CacheFileHandle::CacheFileHandle(const SHA1Sum::Hash *aHash, bool aPriority)
   1.114 +  : mHash(aHash)
   1.115 +  , mIsDoomed(false)
   1.116 +  , mPriority(aPriority)
   1.117 +  , mClosed(false)
   1.118 +  , mInvalid(false)
   1.119 +  , mFileExists(false)
   1.120 +  , mFileSize(-1)
   1.121 +  , mFD(nullptr)
   1.122 +{
   1.123 +  LOG(("CacheFileHandle::CacheFileHandle() [this=%p, hash=%08x%08x%08x%08x%08x]"
   1.124 +       , this, LOGSHA1(aHash)));
   1.125 +}
   1.126 +
   1.127 +CacheFileHandle::CacheFileHandle(const nsACString &aKey, bool aPriority)
   1.128 +  : mHash(nullptr)
   1.129 +  , mIsDoomed(false)
   1.130 +  , mPriority(aPriority)
   1.131 +  , mClosed(false)
   1.132 +  , mInvalid(false)
   1.133 +  , mFileExists(false)
   1.134 +  , mFileSize(-1)
   1.135 +  , mFD(nullptr)
   1.136 +  , mKey(aKey)
   1.137 +{
   1.138 +  LOG(("CacheFileHandle::CacheFileHandle() [this=%p, key=%s]", this,
   1.139 +       PromiseFlatCString(aKey).get()));
   1.140 +}
   1.141 +
   1.142 +CacheFileHandle::~CacheFileHandle()
   1.143 +{
   1.144 +  LOG(("CacheFileHandle::~CacheFileHandle() [this=%p]", this));
   1.145 +
   1.146 +  MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
   1.147 +
   1.148 +  nsRefPtr<CacheFileIOManager> ioMan = CacheFileIOManager::gInstance;
   1.149 +  if (ioMan) {
   1.150 +    ioMan->CloseHandleInternal(this);
   1.151 +  }
   1.152 +}
   1.153 +
   1.154 +void
   1.155 +CacheFileHandle::Log()
   1.156 +{
   1.157 +  nsAutoCString leafName;
   1.158 +  if (mFile) {
   1.159 +    mFile->GetNativeLeafName(leafName);
   1.160 +  }
   1.161 +
   1.162 +  if (!mHash) {
   1.163 +    // special file
   1.164 +    LOG(("CacheFileHandle::Log() [this=%p, hash=nullptr, isDoomed=%d, "
   1.165 +         "priority=%d, closed=%d, invalid=%d, "
   1.166 +         "fileExists=%d, fileSize=%lld, leafName=%s, key=%s]",
   1.167 +         this, mIsDoomed, mPriority, mClosed, mInvalid,
   1.168 +         mFileExists, mFileSize, leafName.get(), mKey.get()));
   1.169 +  } else {
   1.170 +    LOG(("CacheFileHandle::Log() [this=%p, hash=%08x%08x%08x%08x%08x, "
   1.171 +         "isDoomed=%d, priority=%d, closed=%d, invalid=%d, "
   1.172 +         "fileExists=%d, fileSize=%lld, leafName=%s, key=%s]",
   1.173 +         this, LOGSHA1(mHash), mIsDoomed, mPriority, mClosed,
   1.174 +         mInvalid, mFileExists, mFileSize, leafName.get(), mKey.get()));
   1.175 +  }
   1.176 +}
   1.177 +
   1.178 +uint32_t
   1.179 +CacheFileHandle::FileSizeInK() const
   1.180 +{
   1.181 +  MOZ_ASSERT(mFileSize != -1);
   1.182 +  uint64_t size64 = mFileSize;
   1.183 +
   1.184 +  size64 += 0x3FF;
   1.185 +  size64 >>= 10;
   1.186 +
   1.187 +  uint32_t size;
   1.188 +  if (size64 >> 32) {
   1.189 +    NS_WARNING("CacheFileHandle::FileSizeInK() - FileSize is too large, "
   1.190 +               "truncating to PR_UINT32_MAX");
   1.191 +    size = PR_UINT32_MAX;
   1.192 +  } else {
   1.193 +    size = static_cast<uint32_t>(size64);
   1.194 +  }
   1.195 +
   1.196 +  return size;
   1.197 +}
   1.198 +
   1.199 +// Memory reporting
   1.200 +
   1.201 +size_t
   1.202 +CacheFileHandle::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
   1.203 +{
   1.204 +  size_t n = 0;
   1.205 +  nsCOMPtr<nsISizeOf> sizeOf;
   1.206 +
   1.207 +  sizeOf = do_QueryInterface(mFile);
   1.208 +  if (sizeOf) {
   1.209 +    n += sizeOf->SizeOfIncludingThis(mallocSizeOf);
   1.210 +  }
   1.211 +
   1.212 +  n += mallocSizeOf(mFD);
   1.213 +  n += mKey.SizeOfExcludingThisIfUnshared(mallocSizeOf);
   1.214 +  return n;
   1.215 +}
   1.216 +
   1.217 +size_t
   1.218 +CacheFileHandle::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
   1.219 +{
   1.220 +  return mallocSizeOf(this) + SizeOfExcludingThis(mallocSizeOf);
   1.221 +}
   1.222 +
   1.223 +/******************************************************************************
   1.224 + *  CacheFileHandles::HandleHashKey
   1.225 + *****************************************************************************/
   1.226 +
   1.227 +void
   1.228 +CacheFileHandles::HandleHashKey::AddHandle(CacheFileHandle* aHandle)
   1.229 +{
   1.230 +  MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
   1.231 +
   1.232 +  mHandles.InsertElementAt(0, aHandle);
   1.233 +}
   1.234 +
   1.235 +void
   1.236 +CacheFileHandles::HandleHashKey::RemoveHandle(CacheFileHandle* aHandle)
   1.237 +{
   1.238 +  MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
   1.239 +
   1.240 +  DebugOnly<bool> found;
   1.241 +  found = mHandles.RemoveElement(aHandle);
   1.242 +  MOZ_ASSERT(found);
   1.243 +}
   1.244 +
   1.245 +already_AddRefed<CacheFileHandle>
   1.246 +CacheFileHandles::HandleHashKey::GetNewestHandle()
   1.247 +{
   1.248 +  MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
   1.249 +
   1.250 +  nsRefPtr<CacheFileHandle> handle;
   1.251 +  if (mHandles.Length()) {
   1.252 +    handle = mHandles[0];
   1.253 +  }
   1.254 +
   1.255 +  return handle.forget();
   1.256 +}
   1.257 +
   1.258 +void
   1.259 +CacheFileHandles::HandleHashKey::GetHandles(nsTArray<nsRefPtr<CacheFileHandle> > &aResult)
   1.260 +{
   1.261 +  MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
   1.262 +
   1.263 +  for (uint32_t i = 0; i < mHandles.Length(); ++i) {
   1.264 +    CacheFileHandle* handle = mHandles[i];
   1.265 +    aResult.AppendElement(handle);
   1.266 +  }
   1.267 +}
   1.268 +
   1.269 +#ifdef DEBUG
   1.270 +
   1.271 +void
   1.272 +CacheFileHandles::HandleHashKey::AssertHandlesState()
   1.273 +{
   1.274 +  for (uint32_t i = 0; i < mHandles.Length(); ++i) {
   1.275 +    CacheFileHandle* handle = mHandles[i];
   1.276 +    MOZ_ASSERT(handle->IsDoomed());
   1.277 +  }
   1.278 +}
   1.279 +
   1.280 +#endif
   1.281 +
   1.282 +size_t
   1.283 +CacheFileHandles::HandleHashKey::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
   1.284 +{
   1.285 +  MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
   1.286 +
   1.287 +  size_t n = 0;
   1.288 +  n += mallocSizeOf(mHash);
   1.289 +  for (uint32_t i = 0; i < mHandles.Length(); ++i) {
   1.290 +    n += mHandles[i]->SizeOfIncludingThis(mallocSizeOf);
   1.291 +  }
   1.292 +
   1.293 +  return n;
   1.294 +}
   1.295 +
   1.296 +/******************************************************************************
   1.297 + *  CacheFileHandles
   1.298 + *****************************************************************************/
   1.299 +
   1.300 +CacheFileHandles::CacheFileHandles()
   1.301 +{
   1.302 +  LOG(("CacheFileHandles::CacheFileHandles() [this=%p]", this));
   1.303 +  MOZ_COUNT_CTOR(CacheFileHandles);
   1.304 +}
   1.305 +
   1.306 +CacheFileHandles::~CacheFileHandles()
   1.307 +{
   1.308 +  LOG(("CacheFileHandles::~CacheFileHandles() [this=%p]", this));
   1.309 +  MOZ_COUNT_DTOR(CacheFileHandles);
   1.310 +}
   1.311 +
   1.312 +nsresult
   1.313 +CacheFileHandles::GetHandle(const SHA1Sum::Hash *aHash,
   1.314 +                            bool aReturnDoomed,
   1.315 +                            CacheFileHandle **_retval)
   1.316 +{
   1.317 +  MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
   1.318 +  MOZ_ASSERT(aHash);
   1.319 +
   1.320 +#ifdef DEBUG_HANDLES
   1.321 +  LOG(("CacheFileHandles::GetHandle() [hash=%08x%08x%08x%08x%08x]",
   1.322 +       LOGSHA1(aHash)));
   1.323 +#endif
   1.324 +
   1.325 +  // find hash entry for key
   1.326 +  HandleHashKey *entry = mTable.GetEntry(*aHash);
   1.327 +  if (!entry) {
   1.328 +    LOG(("CacheFileHandles::GetHandle() hash=%08x%08x%08x%08x%08x "
   1.329 +         "no handle entries found", LOGSHA1(aHash)));
   1.330 +    return NS_ERROR_NOT_AVAILABLE;
   1.331 +  }
   1.332 +
   1.333 +#ifdef DEBUG_HANDLES
   1.334 +  Log(entry);
   1.335 +#endif
   1.336 +
   1.337 +  // Check if the entry is doomed
   1.338 +  nsRefPtr<CacheFileHandle> handle = entry->GetNewestHandle();
   1.339 +  if (!handle) {
   1.340 +    LOG(("CacheFileHandles::GetHandle() hash=%08x%08x%08x%08x%08x "
   1.341 +         "no handle found %p, entry %p", LOGSHA1(aHash), handle.get(), entry));
   1.342 +    return NS_ERROR_NOT_AVAILABLE;
   1.343 +  }
   1.344 +
   1.345 +  if (handle->IsDoomed()) {
   1.346 +    LOG(("CacheFileHandles::GetHandle() hash=%08x%08x%08x%08x%08x "
   1.347 +         "found doomed handle %p, entry %p", LOGSHA1(aHash), handle.get(), entry));
   1.348 +
   1.349 +    // If the consumer doesn't want doomed handles, exit with NOT_AVAIL.
   1.350 +    if (!aReturnDoomed) {
   1.351 +      return NS_ERROR_NOT_AVAILABLE;
   1.352 +    }
   1.353 +  } else {
   1.354 +    LOG(("CacheFileHandles::GetHandle() hash=%08x%08x%08x%08x%08x "
   1.355 +         "found handle %p, entry %p", LOGSHA1(aHash), handle.get(), entry));
   1.356 +  }
   1.357 +
   1.358 +  handle.forget(_retval);
   1.359 +  return NS_OK;
   1.360 +}
   1.361 +
   1.362 +
   1.363 +nsresult
   1.364 +CacheFileHandles::NewHandle(const SHA1Sum::Hash *aHash,
   1.365 +                            bool aPriority,
   1.366 +                            CacheFileHandle **_retval)
   1.367 +{
   1.368 +  MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
   1.369 +  MOZ_ASSERT(aHash);
   1.370 +
   1.371 +#ifdef DEBUG_HANDLES
   1.372 +  LOG(("CacheFileHandles::NewHandle() [hash=%08x%08x%08x%08x%08x]", LOGSHA1(aHash)));
   1.373 +#endif
   1.374 +
   1.375 +  // find hash entry for key
   1.376 +  HandleHashKey *entry = mTable.PutEntry(*aHash);
   1.377 +
   1.378 +#ifdef DEBUG_HANDLES
   1.379 +  Log(entry);
   1.380 +#endif
   1.381 +
   1.382 +#ifdef DEBUG
   1.383 +  entry->AssertHandlesState();
   1.384 +#endif
   1.385 +
   1.386 +  nsRefPtr<CacheFileHandle> handle = new CacheFileHandle(entry->Hash(), aPriority);
   1.387 +  entry->AddHandle(handle);
   1.388 +
   1.389 +  LOG(("CacheFileHandles::NewHandle() hash=%08x%08x%08x%08x%08x "
   1.390 +       "created new handle %p, entry=%p", LOGSHA1(aHash), handle.get(), entry));
   1.391 +
   1.392 +  handle.forget(_retval);
   1.393 +  return NS_OK;
   1.394 +}
   1.395 +
   1.396 +void
   1.397 +CacheFileHandles::RemoveHandle(CacheFileHandle *aHandle)
   1.398 +{
   1.399 +  MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
   1.400 +  MOZ_ASSERT(aHandle);
   1.401 +
   1.402 +  if (!aHandle) {
   1.403 +    return;
   1.404 +  }
   1.405 +
   1.406 +#ifdef DEBUG_HANDLES
   1.407 +  LOG(("CacheFileHandles::RemoveHandle() [handle=%p, hash=%08x%08x%08x%08x%08x]"
   1.408 +       , aHandle, LOGSHA1(aHandle->Hash())));
   1.409 +#endif
   1.410 +
   1.411 +  // find hash entry for key
   1.412 +  HandleHashKey *entry = mTable.GetEntry(*aHandle->Hash());
   1.413 +  if (!entry) {
   1.414 +    MOZ_ASSERT(CacheFileIOManager::IsShutdown(),
   1.415 +      "Should find entry when removing a handle before shutdown");
   1.416 +
   1.417 +    LOG(("CacheFileHandles::RemoveHandle() hash=%08x%08x%08x%08x%08x "
   1.418 +         "no entries found", LOGSHA1(aHandle->Hash())));
   1.419 +    return;
   1.420 +  }
   1.421 +
   1.422 +#ifdef DEBUG_HANDLES
   1.423 +  Log(entry);
   1.424 +#endif
   1.425 +
   1.426 +  LOG(("CacheFileHandles::RemoveHandle() hash=%08x%08x%08x%08x%08x "
   1.427 +       "removing handle %p", LOGSHA1(entry->Hash()), aHandle));
   1.428 +  entry->RemoveHandle(aHandle);
   1.429 +
   1.430 +  if (entry->IsEmpty()) {
   1.431 +    LOG(("CacheFileHandles::RemoveHandle() hash=%08x%08x%08x%08x%08x "
   1.432 +         "list is empty, removing entry %p", LOGSHA1(entry->Hash()), entry));
   1.433 +    mTable.RemoveEntry(*entry->Hash());
   1.434 +  }
   1.435 +}
   1.436 +
   1.437 +static PLDHashOperator
   1.438 +GetAllHandlesEnum(CacheFileHandles::HandleHashKey* aEntry, void *aClosure)
   1.439 +{
   1.440 +  nsTArray<nsRefPtr<CacheFileHandle> > *array =
   1.441 +    static_cast<nsTArray<nsRefPtr<CacheFileHandle> > *>(aClosure);
   1.442 +
   1.443 +  aEntry->GetHandles(*array);
   1.444 +  return PL_DHASH_NEXT;
   1.445 +}
   1.446 +
   1.447 +void
   1.448 +CacheFileHandles::GetAllHandles(nsTArray<nsRefPtr<CacheFileHandle> > *_retval)
   1.449 +{
   1.450 +  MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
   1.451 +  mTable.EnumerateEntries(&GetAllHandlesEnum, _retval);
   1.452 +}
   1.453 +
   1.454 +static PLDHashOperator
   1.455 +GetActiveHandlesEnum(CacheFileHandles::HandleHashKey* aEntry, void *aClosure)
   1.456 +{
   1.457 +  nsTArray<nsRefPtr<CacheFileHandle> > *array =
   1.458 +    static_cast<nsTArray<nsRefPtr<CacheFileHandle> > *>(aClosure);
   1.459 +
   1.460 +  nsRefPtr<CacheFileHandle> handle = aEntry->GetNewestHandle();
   1.461 +  MOZ_ASSERT(handle);
   1.462 +
   1.463 +  if (!handle->IsDoomed()) {
   1.464 +    array->AppendElement(handle);
   1.465 +  }
   1.466 +
   1.467 +  return PL_DHASH_NEXT;
   1.468 +}
   1.469 +
   1.470 +void
   1.471 +CacheFileHandles::GetActiveHandles(
   1.472 +  nsTArray<nsRefPtr<CacheFileHandle> > *_retval)
   1.473 +{
   1.474 +  MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
   1.475 +  mTable.EnumerateEntries(&GetActiveHandlesEnum, _retval);
   1.476 +}
   1.477 +
   1.478 +void
   1.479 +CacheFileHandles::ClearAll()
   1.480 +{
   1.481 +  MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
   1.482 +  mTable.Clear();
   1.483 +}
   1.484 +
   1.485 +uint32_t
   1.486 +CacheFileHandles::HandleCount()
   1.487 +{
   1.488 +  return mTable.Count();
   1.489 +}
   1.490 +
   1.491 +#ifdef DEBUG_HANDLES
   1.492 +void
   1.493 +CacheFileHandles::Log(CacheFileHandlesEntry *entry)
   1.494 +{
   1.495 +  LOG(("CacheFileHandles::Log() BEGIN [entry=%p]", entry));
   1.496 +
   1.497 +  nsTArray<nsRefPtr<CacheFileHandle> > array;
   1.498 +  aEntry->GetHandles(array);
   1.499 +
   1.500 +  for (uint32_t i = 0; i < array.Length(); ++i) {
   1.501 +    CacheFileHandle *handle = array[i];
   1.502 +    handle->Log();
   1.503 +  }
   1.504 +
   1.505 +  LOG(("CacheFileHandles::Log() END [entry=%p]", entry));
   1.506 +}
   1.507 +#endif
   1.508 +
   1.509 +// Memory reporting
   1.510 +
   1.511 +namespace { // anon
   1.512 +
   1.513 +size_t
   1.514 +CollectHandlesMemory(CacheFileHandles::HandleHashKey* key,
   1.515 +                     mozilla::MallocSizeOf mallocSizeOf,
   1.516 +                     void *arg)
   1.517 +{
   1.518 +  return key->SizeOfExcludingThis(mallocSizeOf);
   1.519 +}
   1.520 +
   1.521 +} // anon
   1.522 +
   1.523 +size_t
   1.524 +CacheFileHandles::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
   1.525 +{
   1.526 +  MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
   1.527 +
   1.528 +  return mTable.SizeOfExcludingThis(&CollectHandlesMemory, mallocSizeOf);
   1.529 +}
   1.530 +
   1.531 +// Events
   1.532 +
   1.533 +class ShutdownEvent : public nsRunnable {
   1.534 +public:
   1.535 +  ShutdownEvent(mozilla::Mutex *aLock, mozilla::CondVar *aCondVar)
   1.536 +    : mLock(aLock)
   1.537 +    , mCondVar(aCondVar)
   1.538 +  {
   1.539 +    MOZ_COUNT_CTOR(ShutdownEvent);
   1.540 +  }
   1.541 +
   1.542 +  ~ShutdownEvent()
   1.543 +  {
   1.544 +    MOZ_COUNT_DTOR(ShutdownEvent);
   1.545 +  }
   1.546 +
   1.547 +  NS_IMETHOD Run()
   1.548 +  {
   1.549 +    MutexAutoLock lock(*mLock);
   1.550 +
   1.551 +    CacheFileIOManager::gInstance->ShutdownInternal();
   1.552 +
   1.553 +    mCondVar->Notify();
   1.554 +    return NS_OK;
   1.555 +  }
   1.556 +
   1.557 +protected:
   1.558 +  mozilla::Mutex   *mLock;
   1.559 +  mozilla::CondVar *mCondVar;
   1.560 +};
   1.561 +
   1.562 +class OpenFileEvent : public nsRunnable {
   1.563 +public:
   1.564 +  OpenFileEvent(const nsACString &aKey,
   1.565 +                uint32_t aFlags, bool aResultOnAnyThread,
   1.566 +                CacheFileIOListener *aCallback)
   1.567 +    : mFlags(aFlags)
   1.568 +    , mResultOnAnyThread(aResultOnAnyThread)
   1.569 +    , mCallback(aCallback)
   1.570 +    , mRV(NS_ERROR_FAILURE)
   1.571 +    , mKey(aKey)
   1.572 +  {
   1.573 +    MOZ_COUNT_CTOR(OpenFileEvent);
   1.574 +
   1.575 +    if (!aResultOnAnyThread) {
   1.576 +      mTarget = static_cast<nsIEventTarget*>(NS_GetCurrentThread());
   1.577 +      MOZ_ASSERT(mTarget);
   1.578 +    }
   1.579 +
   1.580 +    mIOMan = CacheFileIOManager::gInstance;
   1.581 +
   1.582 +    MOZ_EVENT_TRACER_NAME_OBJECT(static_cast<nsIRunnable*>(this), aKey.BeginReading());
   1.583 +    MOZ_EVENT_TRACER_WAIT(static_cast<nsIRunnable*>(this), "net::cache::open-background");
   1.584 +  }
   1.585 +
   1.586 +  ~OpenFileEvent()
   1.587 +  {
   1.588 +    MOZ_COUNT_DTOR(OpenFileEvent);
   1.589 +  }
   1.590 +
   1.591 +  NS_IMETHOD Run()
   1.592 +  {
   1.593 +    if (mResultOnAnyThread || mTarget) {
   1.594 +      mRV = NS_OK;
   1.595 +
   1.596 +      if (!(mFlags & CacheFileIOManager::SPECIAL_FILE)) {
   1.597 +        SHA1Sum sum;
   1.598 +        sum.update(mKey.BeginReading(), mKey.Length());
   1.599 +        sum.finish(mHash);
   1.600 +      }
   1.601 +
   1.602 +      MOZ_EVENT_TRACER_EXEC(static_cast<nsIRunnable*>(this),
   1.603 +                            "net::cache::open-background");
   1.604 +      if (NS_SUCCEEDED(mRV)) {
   1.605 +        if (!mIOMan) {
   1.606 +          mRV = NS_ERROR_NOT_INITIALIZED;
   1.607 +        } else {
   1.608 +          if (mFlags & CacheFileIOManager::SPECIAL_FILE) {
   1.609 +            mRV = mIOMan->OpenSpecialFileInternal(mKey, mFlags,
   1.610 +                                                  getter_AddRefs(mHandle));
   1.611 +          } else {
   1.612 +            mRV = mIOMan->OpenFileInternal(&mHash, mKey, mFlags,
   1.613 +                                           getter_AddRefs(mHandle));
   1.614 +          }
   1.615 +          mIOMan = nullptr;
   1.616 +          if (mHandle) {
   1.617 +            MOZ_EVENT_TRACER_NAME_OBJECT(mHandle.get(), mKey.get());
   1.618 +            if (mHandle->Key().IsEmpty()) {
   1.619 +              mHandle->Key() = mKey;
   1.620 +            }
   1.621 +          }
   1.622 +        }
   1.623 +      }
   1.624 +      MOZ_EVENT_TRACER_DONE(static_cast<nsIRunnable*>(this), "net::cache::open-background");
   1.625 +
   1.626 +      MOZ_EVENT_TRACER_WAIT(static_cast<nsIRunnable*>(this), "net::cache::open-result");
   1.627 +
   1.628 +      if (mTarget) {
   1.629 +        nsCOMPtr<nsIEventTarget> target;
   1.630 +        mTarget.swap(target);
   1.631 +        return target->Dispatch(this, nsIEventTarget::DISPATCH_NORMAL);
   1.632 +      }
   1.633 +    }
   1.634 +
   1.635 +    if (!mTarget) {
   1.636 +      MOZ_EVENT_TRACER_EXEC(static_cast<nsIRunnable*>(this), "net::cache::open-result");
   1.637 +      mCallback->OnFileOpened(mHandle, mRV);
   1.638 +      MOZ_EVENT_TRACER_DONE(static_cast<nsIRunnable*>(this), "net::cache::open-result");
   1.639 +    }
   1.640 +
   1.641 +    return NS_OK;
   1.642 +  }
   1.643 +
   1.644 +protected:
   1.645 +  SHA1Sum::Hash                 mHash;
   1.646 +  uint32_t                      mFlags;
   1.647 +  bool                          mResultOnAnyThread;
   1.648 +  nsCOMPtr<CacheFileIOListener> mCallback;
   1.649 +  nsCOMPtr<nsIEventTarget>      mTarget;
   1.650 +  nsRefPtr<CacheFileIOManager>  mIOMan;
   1.651 +  nsRefPtr<CacheFileHandle>     mHandle;
   1.652 +  nsresult                      mRV;
   1.653 +  nsCString                     mKey;
   1.654 +};
   1.655 +
   1.656 +class ReadEvent : public nsRunnable {
   1.657 +public:
   1.658 +  ReadEvent(CacheFileHandle *aHandle, int64_t aOffset, char *aBuf,
   1.659 +            int32_t aCount, bool aResultOnAnyThread, CacheFileIOListener *aCallback)
   1.660 +    : mHandle(aHandle)
   1.661 +    , mOffset(aOffset)
   1.662 +    , mBuf(aBuf)
   1.663 +    , mCount(aCount)
   1.664 +    , mResultOnAnyThread(aResultOnAnyThread)
   1.665 +    , mCallback(aCallback)
   1.666 +    , mRV(NS_ERROR_FAILURE)
   1.667 +  {
   1.668 +    MOZ_COUNT_CTOR(ReadEvent);
   1.669 +
   1.670 +    if (!aResultOnAnyThread) {
   1.671 +      mTarget = static_cast<nsIEventTarget*>(NS_GetCurrentThread());
   1.672 +    }
   1.673 +
   1.674 +    MOZ_EVENT_TRACER_NAME_OBJECT(static_cast<nsIRunnable*>(this), aHandle->Key().get());
   1.675 +    MOZ_EVENT_TRACER_WAIT(static_cast<nsIRunnable*>(this), "net::cache::read-background");
   1.676 +  }
   1.677 +
   1.678 +  ~ReadEvent()
   1.679 +  {
   1.680 +    MOZ_COUNT_DTOR(ReadEvent);
   1.681 +  }
   1.682 +
   1.683 +  NS_IMETHOD Run()
   1.684 +  {
   1.685 +    if (mResultOnAnyThread || mTarget) {
   1.686 +      MOZ_EVENT_TRACER_EXEC(static_cast<nsIRunnable*>(this), "net::cache::read-background");
   1.687 +      if (mHandle->IsClosed()) {
   1.688 +        mRV = NS_ERROR_NOT_INITIALIZED;
   1.689 +      } else {
   1.690 +        mRV = CacheFileIOManager::gInstance->ReadInternal(
   1.691 +          mHandle, mOffset, mBuf, mCount);
   1.692 +      }
   1.693 +      MOZ_EVENT_TRACER_DONE(static_cast<nsIRunnable*>(this), "net::cache::read-background");
   1.694 +
   1.695 +      MOZ_EVENT_TRACER_WAIT(static_cast<nsIRunnable*>(this), "net::cache::read-result");
   1.696 +
   1.697 +      if (mTarget) {
   1.698 +        nsCOMPtr<nsIEventTarget> target;
   1.699 +        mTarget.swap(target);
   1.700 +        return target->Dispatch(this, nsIEventTarget::DISPATCH_NORMAL);
   1.701 +      }
   1.702 +    }
   1.703 +
   1.704 +    if (!mTarget && mCallback) {
   1.705 +      MOZ_EVENT_TRACER_EXEC(static_cast<nsIRunnable*>(this), "net::cache::read-result");
   1.706 +      mCallback->OnDataRead(mHandle, mBuf, mRV);
   1.707 +      MOZ_EVENT_TRACER_DONE(static_cast<nsIRunnable*>(this), "net::cache::read-result");
   1.708 +    }
   1.709 +
   1.710 +    return NS_OK;
   1.711 +  }
   1.712 +
   1.713 +protected:
   1.714 +  nsRefPtr<CacheFileHandle>     mHandle;
   1.715 +  int64_t                       mOffset;
   1.716 +  char                         *mBuf;
   1.717 +  int32_t                       mCount;
   1.718 +  bool                          mResultOnAnyThread;
   1.719 +  nsCOMPtr<CacheFileIOListener> mCallback;
   1.720 +  nsCOMPtr<nsIEventTarget>      mTarget;
   1.721 +  nsresult                      mRV;
   1.722 +};
   1.723 +
   1.724 +class WriteEvent : public nsRunnable {
   1.725 +public:
   1.726 +  WriteEvent(CacheFileHandle *aHandle, int64_t aOffset, const char *aBuf,
   1.727 +             int32_t aCount, bool aValidate, CacheFileIOListener *aCallback)
   1.728 +    : mHandle(aHandle)
   1.729 +    , mOffset(aOffset)
   1.730 +    , mBuf(aBuf)
   1.731 +    , mCount(aCount)
   1.732 +    , mValidate(aValidate)
   1.733 +    , mCallback(aCallback)
   1.734 +    , mRV(NS_ERROR_FAILURE)
   1.735 +  {
   1.736 +    MOZ_COUNT_CTOR(WriteEvent);
   1.737 +    mTarget = static_cast<nsIEventTarget*>(NS_GetCurrentThread());
   1.738 +
   1.739 +    MOZ_EVENT_TRACER_NAME_OBJECT(static_cast<nsIRunnable*>(this), aHandle->Key().get());
   1.740 +    MOZ_EVENT_TRACER_WAIT(static_cast<nsIRunnable*>(this), "net::cache::write-background");
   1.741 +  }
   1.742 +
   1.743 +  ~WriteEvent()
   1.744 +  {
   1.745 +    MOZ_COUNT_DTOR(WriteEvent);
   1.746 +
   1.747 +    if (!mCallback && mBuf) {
   1.748 +      free(const_cast<char *>(mBuf));
   1.749 +    }
   1.750 +  }
   1.751 +
   1.752 +  NS_IMETHOD Run()
   1.753 +  {
   1.754 +    if (mTarget) {
   1.755 +      MOZ_EVENT_TRACER_EXEC(static_cast<nsIRunnable*>(this), "net::cache::write-background");
   1.756 +      if (mHandle->IsClosed()) {
   1.757 +        mRV = NS_ERROR_NOT_INITIALIZED;
   1.758 +      } else {
   1.759 +        mRV = CacheFileIOManager::gInstance->WriteInternal(
   1.760 +          mHandle, mOffset, mBuf, mCount, mValidate);
   1.761 +      }
   1.762 +      MOZ_EVENT_TRACER_DONE(static_cast<nsIRunnable*>(this), "net::cache::write-background");
   1.763 +
   1.764 +      MOZ_EVENT_TRACER_WAIT(static_cast<nsIRunnable*>(this), "net::cache::write-result");
   1.765 +      nsCOMPtr<nsIEventTarget> target;
   1.766 +      mTarget.swap(target);
   1.767 +      target->Dispatch(this, nsIEventTarget::DISPATCH_NORMAL);
   1.768 +    } else {
   1.769 +      MOZ_EVENT_TRACER_EXEC(static_cast<nsIRunnable*>(this), "net::cache::write-result");
   1.770 +      if (mCallback) {
   1.771 +        mCallback->OnDataWritten(mHandle, mBuf, mRV);
   1.772 +      } else {
   1.773 +        free(const_cast<char *>(mBuf));
   1.774 +        mBuf = nullptr;
   1.775 +      }
   1.776 +      MOZ_EVENT_TRACER_DONE(static_cast<nsIRunnable*>(this), "net::cache::write-result");
   1.777 +    }
   1.778 +    return NS_OK;
   1.779 +  }
   1.780 +
   1.781 +protected:
   1.782 +  nsRefPtr<CacheFileHandle>     mHandle;
   1.783 +  int64_t                       mOffset;
   1.784 +  const char                   *mBuf;
   1.785 +  int32_t                       mCount;
   1.786 +  bool                          mValidate;
   1.787 +  nsCOMPtr<CacheFileIOListener> mCallback;
   1.788 +  nsCOMPtr<nsIEventTarget>      mTarget;
   1.789 +  nsresult                      mRV;
   1.790 +};
   1.791 +
   1.792 +class DoomFileEvent : public nsRunnable {
   1.793 +public:
   1.794 +  DoomFileEvent(CacheFileHandle *aHandle,
   1.795 +                CacheFileIOListener *aCallback)
   1.796 +    : mCallback(aCallback)
   1.797 +    , mHandle(aHandle)
   1.798 +    , mRV(NS_ERROR_FAILURE)
   1.799 +  {
   1.800 +    MOZ_COUNT_CTOR(DoomFileEvent);
   1.801 +    mTarget = static_cast<nsIEventTarget*>(NS_GetCurrentThread());
   1.802 +
   1.803 +    MOZ_EVENT_TRACER_NAME_OBJECT(static_cast<nsIRunnable*>(this), aHandle->Key().get());
   1.804 +    MOZ_EVENT_TRACER_WAIT(static_cast<nsIRunnable*>(this), "net::cache::doom-background");
   1.805 +  }
   1.806 +
   1.807 +  ~DoomFileEvent()
   1.808 +  {
   1.809 +    MOZ_COUNT_DTOR(DoomFileEvent);
   1.810 +  }
   1.811 +
   1.812 +  NS_IMETHOD Run()
   1.813 +  {
   1.814 +    if (mTarget) {
   1.815 +      MOZ_EVENT_TRACER_EXEC(static_cast<nsIRunnable*>(this), "net::cache::doom-background");
   1.816 +      if (mHandle->IsClosed()) {
   1.817 +        mRV = NS_ERROR_NOT_INITIALIZED;
   1.818 +      } else {
   1.819 +        mRV = CacheFileIOManager::gInstance->DoomFileInternal(mHandle);
   1.820 +      }
   1.821 +      MOZ_EVENT_TRACER_DONE(static_cast<nsIRunnable*>(this), "net::cache::doom-background");
   1.822 +
   1.823 +      MOZ_EVENT_TRACER_WAIT(static_cast<nsIRunnable*>(this), "net::cache::doom-result");
   1.824 +      nsCOMPtr<nsIEventTarget> target;
   1.825 +      mTarget.swap(target);
   1.826 +      target->Dispatch(this, nsIEventTarget::DISPATCH_NORMAL);
   1.827 +    } else {
   1.828 +      MOZ_EVENT_TRACER_EXEC(static_cast<nsIRunnable*>(this), "net::cache::doom-result");
   1.829 +      if (mCallback) {
   1.830 +        mCallback->OnFileDoomed(mHandle, mRV);
   1.831 +      }
   1.832 +      MOZ_EVENT_TRACER_DONE(static_cast<nsIRunnable*>(this), "net::cache::doom-result");
   1.833 +    }
   1.834 +    return NS_OK;
   1.835 +  }
   1.836 +
   1.837 +protected:
   1.838 +  nsCOMPtr<CacheFileIOListener> mCallback;
   1.839 +  nsCOMPtr<nsIEventTarget>      mTarget;
   1.840 +  nsRefPtr<CacheFileHandle>     mHandle;
   1.841 +  nsresult                      mRV;
   1.842 +};
   1.843 +
   1.844 +class DoomFileByKeyEvent : public nsRunnable {
   1.845 +public:
   1.846 +  DoomFileByKeyEvent(const nsACString &aKey,
   1.847 +                     CacheFileIOListener *aCallback)
   1.848 +    : mCallback(aCallback)
   1.849 +    , mRV(NS_ERROR_FAILURE)
   1.850 +  {
   1.851 +    MOZ_COUNT_CTOR(DoomFileByKeyEvent);
   1.852 +
   1.853 +    SHA1Sum sum;
   1.854 +    sum.update(aKey.BeginReading(), aKey.Length());
   1.855 +    sum.finish(mHash);
   1.856 +
   1.857 +    mTarget = static_cast<nsIEventTarget*>(NS_GetCurrentThread());
   1.858 +    mIOMan = CacheFileIOManager::gInstance;
   1.859 +    MOZ_ASSERT(mTarget);
   1.860 +  }
   1.861 +
   1.862 +  ~DoomFileByKeyEvent()
   1.863 +  {
   1.864 +    MOZ_COUNT_DTOR(DoomFileByKeyEvent);
   1.865 +  }
   1.866 +
   1.867 +  NS_IMETHOD Run()
   1.868 +  {
   1.869 +    if (mTarget) {
   1.870 +      if (!mIOMan) {
   1.871 +        mRV = NS_ERROR_NOT_INITIALIZED;
   1.872 +      } else {
   1.873 +        mRV = mIOMan->DoomFileByKeyInternal(&mHash);
   1.874 +        mIOMan = nullptr;
   1.875 +      }
   1.876 +
   1.877 +      nsCOMPtr<nsIEventTarget> target;
   1.878 +      mTarget.swap(target);
   1.879 +      target->Dispatch(this, nsIEventTarget::DISPATCH_NORMAL);
   1.880 +    } else {
   1.881 +      if (mCallback) {
   1.882 +        mCallback->OnFileDoomed(nullptr, mRV);
   1.883 +      }
   1.884 +    }
   1.885 +    return NS_OK;
   1.886 +  }
   1.887 +
   1.888 +protected:
   1.889 +  SHA1Sum::Hash                 mHash;
   1.890 +  nsCOMPtr<CacheFileIOListener> mCallback;
   1.891 +  nsCOMPtr<nsIEventTarget>      mTarget;
   1.892 +  nsRefPtr<CacheFileIOManager>  mIOMan;
   1.893 +  nsresult                      mRV;
   1.894 +};
   1.895 +
   1.896 +class ReleaseNSPRHandleEvent : public nsRunnable {
   1.897 +public:
   1.898 +  ReleaseNSPRHandleEvent(CacheFileHandle *aHandle)
   1.899 +    : mHandle(aHandle)
   1.900 +  {
   1.901 +    MOZ_COUNT_CTOR(ReleaseNSPRHandleEvent);
   1.902 +  }
   1.903 +
   1.904 +  ~ReleaseNSPRHandleEvent()
   1.905 +  {
   1.906 +    MOZ_COUNT_DTOR(ReleaseNSPRHandleEvent);
   1.907 +  }
   1.908 +
   1.909 +  NS_IMETHOD Run()
   1.910 +  {
   1.911 +    if (mHandle->mFD && !mHandle->IsClosed()) {
   1.912 +      CacheFileIOManager::gInstance->ReleaseNSPRHandleInternal(mHandle);
   1.913 +    }
   1.914 +
   1.915 +    return NS_OK;
   1.916 +  }
   1.917 +
   1.918 +protected:
   1.919 +  nsRefPtr<CacheFileHandle>     mHandle;
   1.920 +};
   1.921 +
   1.922 +class TruncateSeekSetEOFEvent : public nsRunnable {
   1.923 +public:
   1.924 +  TruncateSeekSetEOFEvent(CacheFileHandle *aHandle, int64_t aTruncatePos,
   1.925 +                          int64_t aEOFPos, CacheFileIOListener *aCallback)
   1.926 +    : mHandle(aHandle)
   1.927 +    , mTruncatePos(aTruncatePos)
   1.928 +    , mEOFPos(aEOFPos)
   1.929 +    , mCallback(aCallback)
   1.930 +    , mRV(NS_ERROR_FAILURE)
   1.931 +  {
   1.932 +    MOZ_COUNT_CTOR(TruncateSeekSetEOFEvent);
   1.933 +    mTarget = static_cast<nsIEventTarget*>(NS_GetCurrentThread());
   1.934 +  }
   1.935 +
   1.936 +  ~TruncateSeekSetEOFEvent()
   1.937 +  {
   1.938 +    MOZ_COUNT_DTOR(TruncateSeekSetEOFEvent);
   1.939 +  }
   1.940 +
   1.941 +  NS_IMETHOD Run()
   1.942 +  {
   1.943 +    if (mTarget) {
   1.944 +      if (mHandle->IsClosed()) {
   1.945 +        mRV = NS_ERROR_NOT_INITIALIZED;
   1.946 +      } else {
   1.947 +        mRV = CacheFileIOManager::gInstance->TruncateSeekSetEOFInternal(
   1.948 +          mHandle, mTruncatePos, mEOFPos);
   1.949 +      }
   1.950 +
   1.951 +      nsCOMPtr<nsIEventTarget> target;
   1.952 +      mTarget.swap(target);
   1.953 +      target->Dispatch(this, nsIEventTarget::DISPATCH_NORMAL);
   1.954 +    } else {
   1.955 +      if (mCallback) {
   1.956 +        mCallback->OnEOFSet(mHandle, mRV);
   1.957 +      }
   1.958 +    }
   1.959 +    return NS_OK;
   1.960 +  }
   1.961 +
   1.962 +protected:
   1.963 +  nsRefPtr<CacheFileHandle>     mHandle;
   1.964 +  int64_t                       mTruncatePos;
   1.965 +  int64_t                       mEOFPos;
   1.966 +  nsCOMPtr<CacheFileIOListener> mCallback;
   1.967 +  nsCOMPtr<nsIEventTarget>      mTarget;
   1.968 +  nsresult                      mRV;
   1.969 +};
   1.970 +
   1.971 +class RenameFileEvent : public nsRunnable {
   1.972 +public:
   1.973 +  RenameFileEvent(CacheFileHandle *aHandle, const nsACString &aNewName,
   1.974 +                  CacheFileIOListener *aCallback)
   1.975 +    : mHandle(aHandle)
   1.976 +    , mNewName(aNewName)
   1.977 +    , mCallback(aCallback)
   1.978 +    , mRV(NS_ERROR_FAILURE)
   1.979 +  {
   1.980 +    MOZ_COUNT_CTOR(RenameFileEvent);
   1.981 +    mTarget = static_cast<nsIEventTarget*>(NS_GetCurrentThread());
   1.982 +  }
   1.983 +
   1.984 +  ~RenameFileEvent()
   1.985 +  {
   1.986 +    MOZ_COUNT_DTOR(RenameFileEvent);
   1.987 +  }
   1.988 +
   1.989 +  NS_IMETHOD Run()
   1.990 +  {
   1.991 +    if (mTarget) {
   1.992 +      if (mHandle->IsClosed()) {
   1.993 +        mRV = NS_ERROR_NOT_INITIALIZED;
   1.994 +      } else {
   1.995 +        mRV = CacheFileIOManager::gInstance->RenameFileInternal(mHandle,
   1.996 +                                                                mNewName);
   1.997 +      }
   1.998 +
   1.999 +      nsCOMPtr<nsIEventTarget> target;
  1.1000 +      mTarget.swap(target);
  1.1001 +      target->Dispatch(this, nsIEventTarget::DISPATCH_NORMAL);
  1.1002 +    } else {
  1.1003 +      if (mCallback) {
  1.1004 +        mCallback->OnFileRenamed(mHandle, mRV);
  1.1005 +      }
  1.1006 +    }
  1.1007 +    return NS_OK;
  1.1008 +  }
  1.1009 +
  1.1010 +protected:
  1.1011 +  nsRefPtr<CacheFileHandle>     mHandle;
  1.1012 +  nsCString                     mNewName;
  1.1013 +  nsCOMPtr<CacheFileIOListener> mCallback;
  1.1014 +  nsCOMPtr<nsIEventTarget>      mTarget;
  1.1015 +  nsresult                      mRV;
  1.1016 +};
  1.1017 +
  1.1018 +class InitIndexEntryEvent : public nsRunnable {
  1.1019 +public:
  1.1020 +  InitIndexEntryEvent(CacheFileHandle *aHandle, uint32_t aAppId,
  1.1021 +                      bool aAnonymous, bool aInBrowser)
  1.1022 +    : mHandle(aHandle)
  1.1023 +    , mAppId(aAppId)
  1.1024 +    , mAnonymous(aAnonymous)
  1.1025 +    , mInBrowser(aInBrowser)
  1.1026 +  {
  1.1027 +    MOZ_COUNT_CTOR(InitIndexEntryEvent);
  1.1028 +  }
  1.1029 +
  1.1030 +  ~InitIndexEntryEvent()
  1.1031 +  {
  1.1032 +    MOZ_COUNT_DTOR(InitIndexEntryEvent);
  1.1033 +  }
  1.1034 +
  1.1035 +  NS_IMETHOD Run()
  1.1036 +  {
  1.1037 +    if (mHandle->IsClosed() || mHandle->IsDoomed()) {
  1.1038 +      return NS_OK;
  1.1039 +    }
  1.1040 +
  1.1041 +    CacheIndex::InitEntry(mHandle->Hash(), mAppId, mAnonymous, mInBrowser);
  1.1042 +
  1.1043 +    // We cannot set the filesize before we init the entry. If we're opening
  1.1044 +    // an existing entry file, frecency and expiration time will be set after
  1.1045 +    // parsing the entry file, but we must set the filesize here since nobody is
  1.1046 +    // going to set it if there is no write to the file.
  1.1047 +    uint32_t sizeInK = mHandle->FileSizeInK();
  1.1048 +    CacheIndex::UpdateEntry(mHandle->Hash(), nullptr, nullptr, &sizeInK);
  1.1049 +
  1.1050 +    return NS_OK;
  1.1051 +  }
  1.1052 +
  1.1053 +protected:
  1.1054 +  nsRefPtr<CacheFileHandle> mHandle;
  1.1055 +  uint32_t                  mAppId;
  1.1056 +  bool                      mAnonymous;
  1.1057 +  bool                      mInBrowser;
  1.1058 +};
  1.1059 +
  1.1060 +class UpdateIndexEntryEvent : public nsRunnable {
  1.1061 +public:
  1.1062 +  UpdateIndexEntryEvent(CacheFileHandle *aHandle, const uint32_t *aFrecency,
  1.1063 +                        const uint32_t *aExpirationTime)
  1.1064 +    : mHandle(aHandle)
  1.1065 +    , mHasFrecency(false)
  1.1066 +    , mHasExpirationTime(false)
  1.1067 +  {
  1.1068 +    MOZ_COUNT_CTOR(UpdateIndexEntryEvent);
  1.1069 +    if (aFrecency) {
  1.1070 +      mHasFrecency = true;
  1.1071 +      mFrecency = *aFrecency;
  1.1072 +    }
  1.1073 +    if (aExpirationTime) {
  1.1074 +      mHasExpirationTime = true;
  1.1075 +      mExpirationTime = *aExpirationTime;
  1.1076 +    }
  1.1077 +  }
  1.1078 +
  1.1079 +  ~UpdateIndexEntryEvent()
  1.1080 +  {
  1.1081 +    MOZ_COUNT_DTOR(UpdateIndexEntryEvent);
  1.1082 +  }
  1.1083 +
  1.1084 +  NS_IMETHOD Run()
  1.1085 +  {
  1.1086 +    if (mHandle->IsClosed() || mHandle->IsDoomed()) {
  1.1087 +      return NS_OK;
  1.1088 +    }
  1.1089 +
  1.1090 +    CacheIndex::UpdateEntry(mHandle->Hash(),
  1.1091 +                            mHasFrecency ? &mFrecency : nullptr,
  1.1092 +                            mHasExpirationTime ? &mExpirationTime : nullptr,
  1.1093 +                            nullptr);
  1.1094 +    return NS_OK;
  1.1095 +  }
  1.1096 +
  1.1097 +protected:
  1.1098 +  nsRefPtr<CacheFileHandle> mHandle;
  1.1099 +  bool                      mHasFrecency;
  1.1100 +  bool                      mHasExpirationTime;
  1.1101 +  uint32_t                  mFrecency;
  1.1102 +  uint32_t                  mExpirationTime;
  1.1103 +};
  1.1104 +
  1.1105 +class MetadataWriteScheduleEvent : public nsRunnable
  1.1106 +{
  1.1107 +public:
  1.1108 +  enum EMode {
  1.1109 +    SCHEDULE,
  1.1110 +    UNSCHEDULE,
  1.1111 +    SHUTDOWN
  1.1112 +  } mMode;
  1.1113 +
  1.1114 +  nsRefPtr<CacheFile> mFile;
  1.1115 +  nsRefPtr<CacheFileIOManager> mIOMan;
  1.1116 +
  1.1117 +  MetadataWriteScheduleEvent(CacheFileIOManager * aManager,
  1.1118 +                             CacheFile * aFile,
  1.1119 +                             EMode aMode)
  1.1120 +    : mMode(aMode)
  1.1121 +    , mFile(aFile)
  1.1122 +    , mIOMan(aManager)
  1.1123 +  { }
  1.1124 +
  1.1125 +  virtual ~MetadataWriteScheduleEvent() { }
  1.1126 +
  1.1127 +  NS_IMETHOD Run()
  1.1128 +  {
  1.1129 +    nsRefPtr<CacheFileIOManager> ioMan = CacheFileIOManager::gInstance;
  1.1130 +    if (!ioMan) {
  1.1131 +      NS_WARNING("CacheFileIOManager already gone in MetadataWriteScheduleEvent::Run()");
  1.1132 +      return NS_OK;
  1.1133 +    }
  1.1134 +
  1.1135 +    switch (mMode)
  1.1136 +    {
  1.1137 +    case SCHEDULE:
  1.1138 +      ioMan->ScheduleMetadataWriteInternal(mFile);
  1.1139 +      break;
  1.1140 +    case UNSCHEDULE:
  1.1141 +      ioMan->UnscheduleMetadataWriteInternal(mFile);
  1.1142 +      break;
  1.1143 +    case SHUTDOWN:
  1.1144 +      ioMan->ShutdownMetadataWriteSchedulingInternal();
  1.1145 +      break;
  1.1146 +    }
  1.1147 +    return NS_OK;
  1.1148 +  }
  1.1149 +};
  1.1150 +
  1.1151 +CacheFileIOManager * CacheFileIOManager::gInstance = nullptr;
  1.1152 +
  1.1153 +NS_IMPL_ISUPPORTS(CacheFileIOManager, nsITimerCallback)
  1.1154 +
  1.1155 +CacheFileIOManager::CacheFileIOManager()
  1.1156 +  : mShuttingDown(false)
  1.1157 +  , mTreeCreated(false)
  1.1158 +  , mOverLimitEvicting(false)
  1.1159 +  , mRemovingTrashDirs(false)
  1.1160 +{
  1.1161 +  LOG(("CacheFileIOManager::CacheFileIOManager [this=%p]", this));
  1.1162 +  MOZ_COUNT_CTOR(CacheFileIOManager);
  1.1163 +  MOZ_ASSERT(!gInstance, "multiple CacheFileIOManager instances!");
  1.1164 +}
  1.1165 +
  1.1166 +CacheFileIOManager::~CacheFileIOManager()
  1.1167 +{
  1.1168 +  LOG(("CacheFileIOManager::~CacheFileIOManager [this=%p]", this));
  1.1169 +  MOZ_COUNT_DTOR(CacheFileIOManager);
  1.1170 +}
  1.1171 +
  1.1172 +// static
  1.1173 +nsresult
  1.1174 +CacheFileIOManager::Init()
  1.1175 +{
  1.1176 +  LOG(("CacheFileIOManager::Init()"));
  1.1177 +
  1.1178 +  MOZ_ASSERT(NS_IsMainThread());
  1.1179 +
  1.1180 +  if (gInstance) {
  1.1181 +    return NS_ERROR_ALREADY_INITIALIZED;
  1.1182 +  }
  1.1183 +
  1.1184 +  nsRefPtr<CacheFileIOManager> ioMan = new CacheFileIOManager();
  1.1185 +
  1.1186 +  nsresult rv = ioMan->InitInternal();
  1.1187 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1188 +
  1.1189 +  ioMan.swap(gInstance);
  1.1190 +  return NS_OK;
  1.1191 +}
  1.1192 +
  1.1193 +nsresult
  1.1194 +CacheFileIOManager::InitInternal()
  1.1195 +{
  1.1196 +  nsresult rv;
  1.1197 +
  1.1198 +  mIOThread = new CacheIOThread();
  1.1199 +
  1.1200 +  rv = mIOThread->Init();
  1.1201 +  MOZ_ASSERT(NS_SUCCEEDED(rv), "Can't create background thread");
  1.1202 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1203 +
  1.1204 +  mStartTime = TimeStamp::NowLoRes();
  1.1205 +
  1.1206 +  return NS_OK;
  1.1207 +}
  1.1208 +
  1.1209 +// static
  1.1210 +nsresult
  1.1211 +CacheFileIOManager::Shutdown()
  1.1212 +{
  1.1213 +  LOG(("CacheFileIOManager::Shutdown() [gInstance=%p]", gInstance));
  1.1214 +
  1.1215 +  MOZ_ASSERT(NS_IsMainThread());
  1.1216 +
  1.1217 +  if (!gInstance) {
  1.1218 +    return NS_ERROR_NOT_INITIALIZED;
  1.1219 +  }
  1.1220 +
  1.1221 +  Telemetry::AutoTimer<Telemetry::NETWORK_DISK_CACHE_SHUTDOWN_V2> shutdownTimer;
  1.1222 +
  1.1223 +  CacheIndex::PreShutdown();
  1.1224 +
  1.1225 +  ShutdownMetadataWriteScheduling();
  1.1226 +
  1.1227 +  {
  1.1228 +    mozilla::Mutex lock("CacheFileIOManager::Shutdown() lock");
  1.1229 +    mozilla::CondVar condVar(lock, "CacheFileIOManager::Shutdown() condVar");
  1.1230 +
  1.1231 +    MutexAutoLock autoLock(lock);
  1.1232 +    nsRefPtr<ShutdownEvent> ev = new ShutdownEvent(&lock, &condVar);
  1.1233 +    DebugOnly<nsresult> rv;
  1.1234 +    rv = gInstance->mIOThread->Dispatch(ev, CacheIOThread::CLOSE);
  1.1235 +    MOZ_ASSERT(NS_SUCCEEDED(rv));
  1.1236 +    condVar.Wait();
  1.1237 +  }
  1.1238 +
  1.1239 +  MOZ_ASSERT(gInstance->mHandles.HandleCount() == 0);
  1.1240 +  MOZ_ASSERT(gInstance->mHandlesByLastUsed.Length() == 0);
  1.1241 +
  1.1242 +  if (gInstance->mIOThread) {
  1.1243 +    gInstance->mIOThread->Shutdown();
  1.1244 +  }
  1.1245 +
  1.1246 +  CacheIndex::Shutdown();
  1.1247 +
  1.1248 +  if (CacheObserver::ClearCacheOnShutdown()) {
  1.1249 +    gInstance->SyncRemoveAllCacheFiles();
  1.1250 +  }
  1.1251 +
  1.1252 +  nsRefPtr<CacheFileIOManager> ioMan;
  1.1253 +  ioMan.swap(gInstance);
  1.1254 +
  1.1255 +  return NS_OK;
  1.1256 +}
  1.1257 +
  1.1258 +nsresult
  1.1259 +CacheFileIOManager::ShutdownInternal()
  1.1260 +{
  1.1261 +  LOG(("CacheFileIOManager::ShutdownInternal() [this=%p]", this));
  1.1262 +
  1.1263 +  MOZ_ASSERT(mIOThread->IsCurrentThread());
  1.1264 +
  1.1265 +  // No new handles can be created after this flag is set
  1.1266 +  mShuttingDown = true;
  1.1267 +
  1.1268 +  // close all handles and delete all associated files
  1.1269 +  nsTArray<nsRefPtr<CacheFileHandle> > handles;
  1.1270 +  mHandles.GetAllHandles(&handles);
  1.1271 +  handles.AppendElements(mSpecialHandles);
  1.1272 +
  1.1273 +  for (uint32_t i=0 ; i<handles.Length() ; i++) {
  1.1274 +    CacheFileHandle *h = handles[i];
  1.1275 +    h->mClosed = true;
  1.1276 +
  1.1277 +    h->Log();
  1.1278 +
  1.1279 +    // Close file handle
  1.1280 +    if (h->mFD) {
  1.1281 +      ReleaseNSPRHandleInternal(h);
  1.1282 +    }
  1.1283 +
  1.1284 +    // Remove file if entry is doomed or invalid
  1.1285 +    if (h->mFileExists && (h->mIsDoomed || h->mInvalid)) {
  1.1286 +      LOG(("CacheFileIOManager::ShutdownInternal() - Removing file from disk"));
  1.1287 +      h->mFile->Remove(false);
  1.1288 +    }
  1.1289 +
  1.1290 +    if (!h->IsSpecialFile() && !h->mIsDoomed &&
  1.1291 +        (h->mInvalid || !h->mFileExists)) {
  1.1292 +      CacheIndex::RemoveEntry(h->Hash());
  1.1293 +    }
  1.1294 +
  1.1295 +    // Remove the handle from mHandles/mSpecialHandles
  1.1296 +    if (h->IsSpecialFile()) {
  1.1297 +      mSpecialHandles.RemoveElement(h);
  1.1298 +    } else {
  1.1299 +      mHandles.RemoveHandle(h);
  1.1300 +    }
  1.1301 +  }
  1.1302 +
  1.1303 +  // Assert the table is empty. When we are here, no new handles can be added
  1.1304 +  // and handles will no longer remove them self from this table and we don't 
  1.1305 +  // want to keep invalid handles here. Also, there is no lookup after this 
  1.1306 +  // point to happen.
  1.1307 +  MOZ_ASSERT(mHandles.HandleCount() == 0);
  1.1308 +
  1.1309 +  // Release trash directory enumerator
  1.1310 +  if (mTrashDirEnumerator) {
  1.1311 +    mTrashDirEnumerator->Close();
  1.1312 +    mTrashDirEnumerator = nullptr;
  1.1313 +  }
  1.1314 +
  1.1315 +  return NS_OK;
  1.1316 +}
  1.1317 +
  1.1318 +// static
  1.1319 +nsresult
  1.1320 +CacheFileIOManager::OnProfile()
  1.1321 +{
  1.1322 +  LOG(("CacheFileIOManager::OnProfile() [gInstance=%p]", gInstance));
  1.1323 +
  1.1324 +  nsRefPtr<CacheFileIOManager> ioMan = gInstance;
  1.1325 +  if (!ioMan) {
  1.1326 +    // CacheFileIOManager::Init() failed, probably could not create the IO
  1.1327 +    // thread, just go with it...
  1.1328 +    return NS_ERROR_NOT_INITIALIZED;
  1.1329 +  }
  1.1330 +
  1.1331 +  nsresult rv;
  1.1332 +
  1.1333 +  nsCOMPtr<nsIFile> directory;
  1.1334 +
  1.1335 +  CacheObserver::ParentDirOverride(getter_AddRefs(directory));
  1.1336 +
  1.1337 +#if defined(MOZ_WIDGET_ANDROID)
  1.1338 +  char* cachePath = getenv("CACHE_DIRECTORY");
  1.1339 +  if (!directory && cachePath && *cachePath) {
  1.1340 +    rv = NS_NewNativeLocalFile(nsDependentCString(cachePath),
  1.1341 +                               true, getter_AddRefs(directory));
  1.1342 +  }
  1.1343 +#endif
  1.1344 +
  1.1345 +  if (!directory) {
  1.1346 +    rv = NS_GetSpecialDirectory(NS_APP_CACHE_PARENT_DIR,
  1.1347 +                                getter_AddRefs(directory));
  1.1348 +  }
  1.1349 +
  1.1350 +  if (!directory) {
  1.1351 +    rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR,
  1.1352 +                                getter_AddRefs(directory));
  1.1353 +  }
  1.1354 +
  1.1355 +  if (directory) {
  1.1356 +    rv = directory->Append(NS_LITERAL_STRING("cache2"));
  1.1357 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1358 +  }
  1.1359 +
  1.1360 +  // All functions return a clone.
  1.1361 +  ioMan->mCacheDirectory.swap(directory);
  1.1362 +
  1.1363 +  if (ioMan->mCacheDirectory) {
  1.1364 +    CacheIndex::Init(ioMan->mCacheDirectory);
  1.1365 +  }
  1.1366 +
  1.1367 +  return NS_OK;
  1.1368 +}
  1.1369 +
  1.1370 +// static
  1.1371 +already_AddRefed<nsIEventTarget>
  1.1372 +CacheFileIOManager::IOTarget()
  1.1373 +{
  1.1374 +  nsCOMPtr<nsIEventTarget> target;
  1.1375 +  if (gInstance && gInstance->mIOThread) {
  1.1376 +    target = gInstance->mIOThread->Target();
  1.1377 +  }
  1.1378 +
  1.1379 +  return target.forget();
  1.1380 +}
  1.1381 +
  1.1382 +// static
  1.1383 +already_AddRefed<CacheIOThread>
  1.1384 +CacheFileIOManager::IOThread()
  1.1385 +{
  1.1386 +  nsRefPtr<CacheIOThread> thread;
  1.1387 +  if (gInstance) {
  1.1388 +    thread = gInstance->mIOThread;
  1.1389 +  }
  1.1390 +
  1.1391 +  return thread.forget();
  1.1392 +}
  1.1393 +
  1.1394 +// static
  1.1395 +bool
  1.1396 +CacheFileIOManager::IsOnIOThread()
  1.1397 +{
  1.1398 +  nsRefPtr<CacheFileIOManager> ioMan = gInstance;
  1.1399 +  if (ioMan && ioMan->mIOThread) {
  1.1400 +    return ioMan->mIOThread->IsCurrentThread();
  1.1401 +  }
  1.1402 +
  1.1403 +  return false;
  1.1404 +}
  1.1405 +
  1.1406 +// static
  1.1407 +bool
  1.1408 +CacheFileIOManager::IsOnIOThreadOrCeased()
  1.1409 +{
  1.1410 +  nsRefPtr<CacheFileIOManager> ioMan = gInstance;
  1.1411 +  if (ioMan && ioMan->mIOThread) {
  1.1412 +    return ioMan->mIOThread->IsCurrentThread();
  1.1413 +  }
  1.1414 +
  1.1415 +  // Ceased...
  1.1416 +  return true;
  1.1417 +}
  1.1418 +
  1.1419 +// static
  1.1420 +bool
  1.1421 +CacheFileIOManager::IsShutdown()
  1.1422 +{
  1.1423 +  if (!gInstance) {
  1.1424 +    return true;
  1.1425 +  }
  1.1426 +  return gInstance->mShuttingDown;
  1.1427 +}
  1.1428 +
  1.1429 +// static
  1.1430 +nsresult
  1.1431 +CacheFileIOManager::ScheduleMetadataWrite(CacheFile * aFile)
  1.1432 +{
  1.1433 +  nsRefPtr<CacheFileIOManager> ioMan = gInstance;
  1.1434 +  NS_ENSURE_TRUE(ioMan, NS_ERROR_NOT_INITIALIZED);
  1.1435 +
  1.1436 +  NS_ENSURE_TRUE(!ioMan->mShuttingDown, NS_ERROR_NOT_INITIALIZED);
  1.1437 +
  1.1438 +  nsRefPtr<MetadataWriteScheduleEvent> event = new MetadataWriteScheduleEvent(
  1.1439 +    ioMan, aFile, MetadataWriteScheduleEvent::SCHEDULE);
  1.1440 +  nsCOMPtr<nsIEventTarget> target = ioMan->IOTarget();
  1.1441 +  NS_ENSURE_TRUE(target, NS_ERROR_UNEXPECTED);
  1.1442 +  return target->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL);
  1.1443 +}
  1.1444 +
  1.1445 +nsresult
  1.1446 +CacheFileIOManager::ScheduleMetadataWriteInternal(CacheFile * aFile)
  1.1447 +{
  1.1448 +  MOZ_ASSERT(IsOnIOThreadOrCeased());
  1.1449 +
  1.1450 +  nsresult rv;
  1.1451 +
  1.1452 +  if (!mMetadataWritesTimer) {
  1.1453 +    mMetadataWritesTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
  1.1454 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1455 +
  1.1456 +    rv = mMetadataWritesTimer->InitWithCallback(
  1.1457 +      this, kMetadataWriteDelay, nsITimer::TYPE_ONE_SHOT);
  1.1458 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1459 +  }
  1.1460 +
  1.1461 +  if (mScheduledMetadataWrites.IndexOf(aFile) !=
  1.1462 +      mScheduledMetadataWrites.NoIndex) {
  1.1463 +    return NS_OK;
  1.1464 +  }
  1.1465 +
  1.1466 +  mScheduledMetadataWrites.AppendElement(aFile);
  1.1467 +
  1.1468 +  return NS_OK;
  1.1469 +}
  1.1470 +
  1.1471 +// static
  1.1472 +nsresult
  1.1473 +CacheFileIOManager::UnscheduleMetadataWrite(CacheFile * aFile)
  1.1474 +{
  1.1475 +  nsRefPtr<CacheFileIOManager> ioMan = gInstance;
  1.1476 +  NS_ENSURE_TRUE(ioMan, NS_ERROR_NOT_INITIALIZED);
  1.1477 +
  1.1478 +  NS_ENSURE_TRUE(!ioMan->mShuttingDown, NS_ERROR_NOT_INITIALIZED);
  1.1479 +
  1.1480 +  nsRefPtr<MetadataWriteScheduleEvent> event = new MetadataWriteScheduleEvent(
  1.1481 +    ioMan, aFile, MetadataWriteScheduleEvent::UNSCHEDULE);
  1.1482 +  nsCOMPtr<nsIEventTarget> target = ioMan->IOTarget();
  1.1483 +  NS_ENSURE_TRUE(target, NS_ERROR_UNEXPECTED);
  1.1484 +  return target->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL);
  1.1485 +}
  1.1486 +
  1.1487 +nsresult
  1.1488 +CacheFileIOManager::UnscheduleMetadataWriteInternal(CacheFile * aFile)
  1.1489 +{
  1.1490 +  MOZ_ASSERT(IsOnIOThreadOrCeased());
  1.1491 +
  1.1492 +  mScheduledMetadataWrites.RemoveElement(aFile);
  1.1493 +
  1.1494 +  if (mScheduledMetadataWrites.Length() == 0 &&
  1.1495 +      mMetadataWritesTimer) {
  1.1496 +    mMetadataWritesTimer->Cancel();
  1.1497 +    mMetadataWritesTimer = nullptr;
  1.1498 +  }
  1.1499 +
  1.1500 +  return NS_OK;
  1.1501 +}
  1.1502 +
  1.1503 +// static
  1.1504 +nsresult
  1.1505 +CacheFileIOManager::ShutdownMetadataWriteScheduling()
  1.1506 +{
  1.1507 +  nsRefPtr<CacheFileIOManager> ioMan = gInstance;
  1.1508 +  NS_ENSURE_TRUE(ioMan, NS_ERROR_NOT_INITIALIZED);
  1.1509 +
  1.1510 +  nsRefPtr<MetadataWriteScheduleEvent> event = new MetadataWriteScheduleEvent(
  1.1511 +    ioMan, nullptr, MetadataWriteScheduleEvent::SHUTDOWN);
  1.1512 +  nsCOMPtr<nsIEventTarget> target = ioMan->IOTarget();
  1.1513 +  NS_ENSURE_TRUE(target, NS_ERROR_UNEXPECTED);
  1.1514 +  return target->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL);
  1.1515 +}
  1.1516 +
  1.1517 +nsresult
  1.1518 +CacheFileIOManager::ShutdownMetadataWriteSchedulingInternal()
  1.1519 +{
  1.1520 +  MOZ_ASSERT(IsOnIOThreadOrCeased());
  1.1521 +
  1.1522 +  nsTArray<nsRefPtr<CacheFile> > files;
  1.1523 +  files.SwapElements(mScheduledMetadataWrites);
  1.1524 +  for (uint32_t i = 0; i < files.Length(); ++i) {
  1.1525 +    CacheFile * file = files[i];
  1.1526 +    file->WriteMetadataIfNeeded();
  1.1527 +  }
  1.1528 +
  1.1529 +  if (mMetadataWritesTimer) {
  1.1530 +    mMetadataWritesTimer->Cancel();
  1.1531 +    mMetadataWritesTimer = nullptr;
  1.1532 +  }
  1.1533 +
  1.1534 +  return NS_OK;
  1.1535 +}
  1.1536 +
  1.1537 +NS_IMETHODIMP
  1.1538 +CacheFileIOManager::Notify(nsITimer * aTimer)
  1.1539 +{
  1.1540 +  MOZ_ASSERT(IsOnIOThreadOrCeased());
  1.1541 +  MOZ_ASSERT(mMetadataWritesTimer == aTimer);
  1.1542 +
  1.1543 +  mMetadataWritesTimer = nullptr;
  1.1544 +
  1.1545 +  nsTArray<nsRefPtr<CacheFile> > files;
  1.1546 +  files.SwapElements(mScheduledMetadataWrites);
  1.1547 +  for (uint32_t i = 0; i < files.Length(); ++i) {
  1.1548 +    CacheFile * file = files[i];
  1.1549 +    file->WriteMetadataIfNeeded();
  1.1550 +  }
  1.1551 +
  1.1552 +  return NS_OK;
  1.1553 +}
  1.1554 +
  1.1555 +// static
  1.1556 +nsresult
  1.1557 +CacheFileIOManager::OpenFile(const nsACString &aKey,
  1.1558 +                             uint32_t aFlags, bool aResultOnAnyThread,
  1.1559 +                             CacheFileIOListener *aCallback)
  1.1560 +{
  1.1561 +  LOG(("CacheFileIOManager::OpenFile() [key=%s, flags=%d, listener=%p]",
  1.1562 +       PromiseFlatCString(aKey).get(), aFlags, aCallback));
  1.1563 +
  1.1564 +  nsresult rv;
  1.1565 +  nsRefPtr<CacheFileIOManager> ioMan = gInstance;
  1.1566 +
  1.1567 +  if (!ioMan) {
  1.1568 +    return NS_ERROR_NOT_INITIALIZED;
  1.1569 +  }
  1.1570 +
  1.1571 +  bool priority = aFlags & CacheFileIOManager::PRIORITY;
  1.1572 +  nsRefPtr<OpenFileEvent> ev = new OpenFileEvent(aKey, aFlags, aResultOnAnyThread, aCallback);
  1.1573 +  rv = ioMan->mIOThread->Dispatch(ev, priority
  1.1574 +    ? CacheIOThread::OPEN_PRIORITY
  1.1575 +    : CacheIOThread::OPEN);
  1.1576 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1577 +
  1.1578 +  return NS_OK;
  1.1579 +}
  1.1580 +
  1.1581 +nsresult
  1.1582 +CacheFileIOManager::OpenFileInternal(const SHA1Sum::Hash *aHash,
  1.1583 +                                     const nsACString &aKey,
  1.1584 +                                     uint32_t aFlags,
  1.1585 +                                     CacheFileHandle **_retval)
  1.1586 +{
  1.1587 +  LOG(("CacheFileIOManager::OpenFileInternal() [hash=%08x%08x%08x%08x%08x, "
  1.1588 +       "key=%s, flags=%d]", LOGSHA1(aHash), PromiseFlatCString(aKey).get(),
  1.1589 +       aFlags));
  1.1590 +
  1.1591 +  MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
  1.1592 +
  1.1593 +  nsresult rv;
  1.1594 +
  1.1595 +  if (mShuttingDown) {
  1.1596 +    return NS_ERROR_NOT_INITIALIZED;
  1.1597 +  }
  1.1598 +
  1.1599 +  if (!mTreeCreated) {
  1.1600 +    rv = CreateCacheTree();
  1.1601 +    if (NS_FAILED(rv)) return rv;
  1.1602 +  }
  1.1603 +
  1.1604 +  nsCOMPtr<nsIFile> file;
  1.1605 +  rv = GetFile(aHash, getter_AddRefs(file));
  1.1606 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1607 +
  1.1608 +  nsRefPtr<CacheFileHandle> handle;
  1.1609 +  mHandles.GetHandle(aHash, false, getter_AddRefs(handle));
  1.1610 +
  1.1611 +  if ((aFlags & (OPEN | CREATE | CREATE_NEW)) == CREATE_NEW) {
  1.1612 +    if (handle) {
  1.1613 +      rv = DoomFileInternal(handle);
  1.1614 +      NS_ENSURE_SUCCESS(rv, rv);
  1.1615 +      handle = nullptr;
  1.1616 +    }
  1.1617 +
  1.1618 +    rv = mHandles.NewHandle(aHash, aFlags & PRIORITY, getter_AddRefs(handle));
  1.1619 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1620 +
  1.1621 +    bool exists;
  1.1622 +    rv = file->Exists(&exists);
  1.1623 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1624 +
  1.1625 +    if (exists) {
  1.1626 +      CacheIndex::RemoveEntry(aHash);
  1.1627 +
  1.1628 +      LOG(("CacheFileIOManager::OpenFileInternal() - Removing old file from "
  1.1629 +           "disk"));
  1.1630 +      rv = file->Remove(false);
  1.1631 +      if (NS_FAILED(rv)) {
  1.1632 +        NS_WARNING("Cannot remove old entry from the disk");
  1.1633 +        LOG(("CacheFileIOManager::OpenFileInternal() - Removing old file failed"
  1.1634 +             ". [rv=0x%08x]", rv));
  1.1635 +      }
  1.1636 +    }
  1.1637 +
  1.1638 +    CacheIndex::AddEntry(aHash);
  1.1639 +    handle->mFile.swap(file);
  1.1640 +    handle->mFileSize = 0;
  1.1641 +  }
  1.1642 +
  1.1643 +  if (handle) {
  1.1644 +    handle.swap(*_retval);
  1.1645 +    return NS_OK;
  1.1646 +  }
  1.1647 +
  1.1648 +  bool exists;
  1.1649 +  rv = file->Exists(&exists);
  1.1650 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1651 +
  1.1652 +  if (exists && mContextEvictor) {
  1.1653 +    if (mContextEvictor->ContextsCount() == 0) {
  1.1654 +      mContextEvictor = nullptr;
  1.1655 +    } else {
  1.1656 +      bool wasEvicted = false;
  1.1657 +      mContextEvictor->WasEvicted(aKey, file, &wasEvicted);
  1.1658 +      if (wasEvicted) {
  1.1659 +        LOG(("CacheFileIOManager::OpenFileInternal() - Removing file since the "
  1.1660 +             "entry was evicted by EvictByContext()"));
  1.1661 +        exists = false;
  1.1662 +        file->Remove(false);
  1.1663 +        CacheIndex::RemoveEntry(aHash);
  1.1664 +      }
  1.1665 +    }
  1.1666 +  }
  1.1667 +
  1.1668 +  if (!exists && (aFlags & (OPEN | CREATE | CREATE_NEW)) == OPEN) {
  1.1669 +    return NS_ERROR_NOT_AVAILABLE;
  1.1670 +  }
  1.1671 +
  1.1672 +  rv = mHandles.NewHandle(aHash, aFlags & PRIORITY, getter_AddRefs(handle));
  1.1673 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1674 +
  1.1675 +  if (exists) {
  1.1676 +    rv = file->GetFileSize(&handle->mFileSize);
  1.1677 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1678 +
  1.1679 +    handle->mFileExists = true;
  1.1680 +
  1.1681 +    CacheIndex::EnsureEntryExists(aHash);
  1.1682 +  } else {
  1.1683 +    handle->mFileSize = 0;
  1.1684 +
  1.1685 +    CacheIndex::AddEntry(aHash);
  1.1686 +  }
  1.1687 +
  1.1688 +  handle->mFile.swap(file);
  1.1689 +  handle.swap(*_retval);
  1.1690 +  return NS_OK;
  1.1691 +}
  1.1692 +
  1.1693 +nsresult
  1.1694 +CacheFileIOManager::OpenSpecialFileInternal(const nsACString &aKey,
  1.1695 +                                            uint32_t aFlags,
  1.1696 +                                            CacheFileHandle **_retval)
  1.1697 +{
  1.1698 +  LOG(("CacheFileIOManager::OpenSpecialFileInternal() [key=%s, flags=%d]",
  1.1699 +       PromiseFlatCString(aKey).get(), aFlags));
  1.1700 +
  1.1701 +  MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
  1.1702 +
  1.1703 +  nsresult rv;
  1.1704 +
  1.1705 +  if (mShuttingDown) {
  1.1706 +    return NS_ERROR_NOT_INITIALIZED;
  1.1707 +  }
  1.1708 +
  1.1709 +  if (!mTreeCreated) {
  1.1710 +    rv = CreateCacheTree();
  1.1711 +    if (NS_FAILED(rv)) return rv;
  1.1712 +  }
  1.1713 +
  1.1714 +  nsCOMPtr<nsIFile> file;
  1.1715 +  rv = GetSpecialFile(aKey, getter_AddRefs(file));
  1.1716 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1717 +
  1.1718 +  nsRefPtr<CacheFileHandle> handle;
  1.1719 +  for (uint32_t i = 0 ; i < mSpecialHandles.Length() ; i++) {
  1.1720 +    if (!mSpecialHandles[i]->IsDoomed() && mSpecialHandles[i]->Key() == aKey) {
  1.1721 +      handle = mSpecialHandles[i];
  1.1722 +      break;
  1.1723 +    }
  1.1724 +  }
  1.1725 +
  1.1726 +  if ((aFlags & (OPEN | CREATE | CREATE_NEW)) == CREATE_NEW) {
  1.1727 +    if (handle) {
  1.1728 +      rv = DoomFileInternal(handle);
  1.1729 +      NS_ENSURE_SUCCESS(rv, rv);
  1.1730 +      handle = nullptr;
  1.1731 +    }
  1.1732 +
  1.1733 +    handle = new CacheFileHandle(aKey, aFlags & PRIORITY);
  1.1734 +    mSpecialHandles.AppendElement(handle);
  1.1735 +
  1.1736 +    bool exists;
  1.1737 +    rv = file->Exists(&exists);
  1.1738 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1739 +
  1.1740 +    if (exists) {
  1.1741 +      LOG(("CacheFileIOManager::OpenSpecialFileInternal() - Removing file from "
  1.1742 +           "disk"));
  1.1743 +      rv = file->Remove(false);
  1.1744 +      if (NS_FAILED(rv)) {
  1.1745 +        NS_WARNING("Cannot remove old entry from the disk");
  1.1746 +        LOG(("CacheFileIOManager::OpenSpecialFileInternal() - Removing file "
  1.1747 +             "failed. [rv=0x%08x]", rv));
  1.1748 +      }
  1.1749 +    }
  1.1750 +
  1.1751 +    handle->mFile.swap(file);
  1.1752 +    handle->mFileSize = 0;
  1.1753 +  }
  1.1754 +
  1.1755 +  if (handle) {
  1.1756 +    handle.swap(*_retval);
  1.1757 +    return NS_OK;
  1.1758 +  }
  1.1759 +
  1.1760 +  bool exists;
  1.1761 +  rv = file->Exists(&exists);
  1.1762 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1763 +
  1.1764 +  if (!exists && (aFlags & (OPEN | CREATE | CREATE_NEW)) == OPEN) {
  1.1765 +    return NS_ERROR_NOT_AVAILABLE;
  1.1766 +  }
  1.1767 +
  1.1768 +  handle = new CacheFileHandle(aKey, aFlags & PRIORITY);
  1.1769 +  mSpecialHandles.AppendElement(handle);
  1.1770 +
  1.1771 +  if (exists) {
  1.1772 +    rv = file->GetFileSize(&handle->mFileSize);
  1.1773 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1774 +
  1.1775 +    handle->mFileExists = true;
  1.1776 +  } else {
  1.1777 +    handle->mFileSize = 0;
  1.1778 +  }
  1.1779 +
  1.1780 +  handle->mFile.swap(file);
  1.1781 +  handle.swap(*_retval);
  1.1782 +  return NS_OK;
  1.1783 +}
  1.1784 +
  1.1785 +nsresult
  1.1786 +CacheFileIOManager::CloseHandleInternal(CacheFileHandle *aHandle)
  1.1787 +{
  1.1788 +  LOG(("CacheFileIOManager::CloseHandleInternal() [handle=%p]", aHandle));
  1.1789 +  aHandle->Log();
  1.1790 +
  1.1791 +  MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
  1.1792 +
  1.1793 +  // Close file handle
  1.1794 +  if (aHandle->mFD) {
  1.1795 +    ReleaseNSPRHandleInternal(aHandle);
  1.1796 +  }
  1.1797 +
  1.1798 +  // Delete the file if the entry was doomed or invalid
  1.1799 +  if (aHandle->mIsDoomed || aHandle->mInvalid) {
  1.1800 +    LOG(("CacheFileIOManager::CloseHandleInternal() - Removing file from "
  1.1801 +         "disk"));
  1.1802 +    aHandle->mFile->Remove(false);
  1.1803 +  }
  1.1804 +
  1.1805 +  if (!aHandle->IsSpecialFile() && !aHandle->mIsDoomed &&
  1.1806 +      (aHandle->mInvalid || !aHandle->mFileExists)) {
  1.1807 +    CacheIndex::RemoveEntry(aHandle->Hash());
  1.1808 +  }
  1.1809 +
  1.1810 +  // Don't remove handles after shutdown
  1.1811 +  if (!mShuttingDown) {
  1.1812 +    if (aHandle->IsSpecialFile()) {
  1.1813 +      mSpecialHandles.RemoveElement(aHandle);
  1.1814 +    } else {
  1.1815 +      mHandles.RemoveHandle(aHandle);
  1.1816 +    }
  1.1817 +  }
  1.1818 +
  1.1819 +  return NS_OK;
  1.1820 +}
  1.1821 +
  1.1822 +// static
  1.1823 +nsresult
  1.1824 +CacheFileIOManager::Read(CacheFileHandle *aHandle, int64_t aOffset,
  1.1825 +                         char *aBuf, int32_t aCount, bool aResultOnAnyThread,
  1.1826 +                         CacheFileIOListener *aCallback)
  1.1827 +{
  1.1828 +  LOG(("CacheFileIOManager::Read() [handle=%p, offset=%lld, count=%d, "
  1.1829 +       "listener=%p]", aHandle, aOffset, aCount, aCallback));
  1.1830 +
  1.1831 +  nsresult rv;
  1.1832 +  nsRefPtr<CacheFileIOManager> ioMan = gInstance;
  1.1833 +
  1.1834 +  if (aHandle->IsClosed() || !ioMan) {
  1.1835 +    return NS_ERROR_NOT_INITIALIZED;
  1.1836 +  }
  1.1837 +
  1.1838 +  nsRefPtr<ReadEvent> ev = new ReadEvent(aHandle, aOffset, aBuf, aCount,
  1.1839 +                                         aResultOnAnyThread, aCallback);
  1.1840 +  rv = ioMan->mIOThread->Dispatch(ev, aHandle->IsPriority()
  1.1841 +    ? CacheIOThread::READ_PRIORITY
  1.1842 +    : CacheIOThread::READ);
  1.1843 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1844 +
  1.1845 +  return NS_OK;
  1.1846 +}
  1.1847 +
  1.1848 +nsresult
  1.1849 +CacheFileIOManager::ReadInternal(CacheFileHandle *aHandle, int64_t aOffset,
  1.1850 +                                 char *aBuf, int32_t aCount)
  1.1851 +{
  1.1852 +  LOG(("CacheFileIOManager::ReadInternal() [handle=%p, offset=%lld, count=%d]",
  1.1853 +       aHandle, aOffset, aCount));
  1.1854 +
  1.1855 +  nsresult rv;
  1.1856 +
  1.1857 +  if (!aHandle->mFileExists) {
  1.1858 +    NS_WARNING("Trying to read from non-existent file");
  1.1859 +    return NS_ERROR_NOT_AVAILABLE;
  1.1860 +  }
  1.1861 +
  1.1862 +  if (!aHandle->mFD) {
  1.1863 +    rv = OpenNSPRHandle(aHandle);
  1.1864 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1865 +  } else {
  1.1866 +    NSPRHandleUsed(aHandle);
  1.1867 +  }
  1.1868 +
  1.1869 +  // Check again, OpenNSPRHandle could figure out the file was gone.
  1.1870 +  if (!aHandle->mFileExists) {
  1.1871 +    NS_WARNING("Trying to read from non-existent file");
  1.1872 +    return NS_ERROR_NOT_AVAILABLE;
  1.1873 +  }
  1.1874 +
  1.1875 +  int64_t offset = PR_Seek64(aHandle->mFD, aOffset, PR_SEEK_SET);
  1.1876 +  if (offset == -1) {
  1.1877 +    return NS_ERROR_FAILURE;
  1.1878 +  }
  1.1879 +
  1.1880 +  int32_t bytesRead = PR_Read(aHandle->mFD, aBuf, aCount);
  1.1881 +  if (bytesRead != aCount) {
  1.1882 +    return NS_ERROR_FAILURE;
  1.1883 +  }
  1.1884 +
  1.1885 +  return NS_OK;
  1.1886 +}
  1.1887 +
  1.1888 +// static
  1.1889 +nsresult
  1.1890 +CacheFileIOManager::Write(CacheFileHandle *aHandle, int64_t aOffset,
  1.1891 +                          const char *aBuf, int32_t aCount, bool aValidate,
  1.1892 +                          CacheFileIOListener *aCallback)
  1.1893 +{
  1.1894 +  LOG(("CacheFileIOManager::Write() [handle=%p, offset=%lld, count=%d, "
  1.1895 +       "validate=%d, listener=%p]", aHandle, aOffset, aCount, aValidate,
  1.1896 +       aCallback));
  1.1897 +
  1.1898 +  nsresult rv;
  1.1899 +  nsRefPtr<CacheFileIOManager> ioMan = gInstance;
  1.1900 +
  1.1901 +  if (aHandle->IsClosed() || !ioMan) {
  1.1902 +    return NS_ERROR_NOT_INITIALIZED;
  1.1903 +  }
  1.1904 +
  1.1905 +  nsRefPtr<WriteEvent> ev = new WriteEvent(aHandle, aOffset, aBuf, aCount,
  1.1906 +                                           aValidate, aCallback);
  1.1907 +  rv = ioMan->mIOThread->Dispatch(ev, CacheIOThread::WRITE);
  1.1908 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1909 +
  1.1910 +  return NS_OK;
  1.1911 +}
  1.1912 +
  1.1913 +nsresult
  1.1914 +CacheFileIOManager::WriteInternal(CacheFileHandle *aHandle, int64_t aOffset,
  1.1915 +                                  const char *aBuf, int32_t aCount,
  1.1916 +                                  bool aValidate)
  1.1917 +{
  1.1918 +  LOG(("CacheFileIOManager::WriteInternal() [handle=%p, offset=%lld, count=%d, "
  1.1919 +       "validate=%d]", aHandle, aOffset, aCount, aValidate));
  1.1920 +
  1.1921 +  nsresult rv;
  1.1922 +
  1.1923 +  if (!aHandle->mFileExists) {
  1.1924 +    rv = CreateFile(aHandle);
  1.1925 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1926 +  }
  1.1927 +
  1.1928 +  if (!aHandle->mFD) {
  1.1929 +    rv = OpenNSPRHandle(aHandle);
  1.1930 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1931 +  } else {
  1.1932 +    NSPRHandleUsed(aHandle);
  1.1933 +  }
  1.1934 +
  1.1935 +  // Check again, OpenNSPRHandle could figure out the file was gone.
  1.1936 +  if (!aHandle->mFileExists) {
  1.1937 +    return NS_ERROR_NOT_AVAILABLE;
  1.1938 +  }
  1.1939 +
  1.1940 +  // Write invalidates the entry by default
  1.1941 +  aHandle->mInvalid = true;
  1.1942 +
  1.1943 +  int64_t offset = PR_Seek64(aHandle->mFD, aOffset, PR_SEEK_SET);
  1.1944 +  if (offset == -1) {
  1.1945 +    return NS_ERROR_FAILURE;
  1.1946 +  }
  1.1947 +
  1.1948 +  int32_t bytesWritten = PR_Write(aHandle->mFD, aBuf, aCount);
  1.1949 +
  1.1950 +  if (bytesWritten != -1 && aHandle->mFileSize < aOffset+bytesWritten) {
  1.1951 +    aHandle->mFileSize = aOffset+bytesWritten;
  1.1952 +
  1.1953 +    if (!aHandle->IsDoomed() && !aHandle->IsSpecialFile()) {
  1.1954 +      uint32_t size = aHandle->FileSizeInK();
  1.1955 +      CacheIndex::UpdateEntry(aHandle->Hash(), nullptr, nullptr, &size);
  1.1956 +      EvictIfOverLimitInternal();
  1.1957 +    }
  1.1958 +  }
  1.1959 +
  1.1960 +  if (bytesWritten != aCount) {
  1.1961 +    return NS_ERROR_FAILURE;
  1.1962 +  }
  1.1963 +
  1.1964 +  // Write was successful and this write validates the entry (i.e. metadata)
  1.1965 +  if (aValidate) {
  1.1966 +    aHandle->mInvalid = false;
  1.1967 +  }
  1.1968 +
  1.1969 +  return NS_OK;
  1.1970 +}
  1.1971 +
  1.1972 +// static
  1.1973 +nsresult
  1.1974 +CacheFileIOManager::DoomFile(CacheFileHandle *aHandle,
  1.1975 +                             CacheFileIOListener *aCallback)
  1.1976 +{
  1.1977 +  LOG(("CacheFileIOManager::DoomFile() [handle=%p, listener=%p]",
  1.1978 +       aHandle, aCallback));
  1.1979 +
  1.1980 +  nsresult rv;
  1.1981 +  nsRefPtr<CacheFileIOManager> ioMan = gInstance;
  1.1982 +
  1.1983 +  if (aHandle->IsClosed() || !ioMan) {
  1.1984 +    return NS_ERROR_NOT_INITIALIZED;
  1.1985 +  }
  1.1986 +
  1.1987 +  nsRefPtr<DoomFileEvent> ev = new DoomFileEvent(aHandle, aCallback);
  1.1988 +  rv = ioMan->mIOThread->Dispatch(ev, aHandle->IsPriority()
  1.1989 +    ? CacheIOThread::OPEN_PRIORITY
  1.1990 +    : CacheIOThread::OPEN);
  1.1991 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1992 +
  1.1993 +  return NS_OK;
  1.1994 +}
  1.1995 +
  1.1996 +nsresult
  1.1997 +CacheFileIOManager::DoomFileInternal(CacheFileHandle *aHandle)
  1.1998 +{
  1.1999 +  LOG(("CacheFileIOManager::DoomFileInternal() [handle=%p]", aHandle));
  1.2000 +  aHandle->Log();
  1.2001 +
  1.2002 +  nsresult rv;
  1.2003 +
  1.2004 +  if (aHandle->IsDoomed()) {
  1.2005 +    return NS_OK;
  1.2006 +  }
  1.2007 +
  1.2008 +  if (aHandle->mFileExists) {
  1.2009 +    // we need to move the current file to the doomed directory
  1.2010 +    if (aHandle->mFD) {
  1.2011 +      ReleaseNSPRHandleInternal(aHandle);
  1.2012 +    }
  1.2013 +
  1.2014 +    // find unused filename
  1.2015 +    nsCOMPtr<nsIFile> file;
  1.2016 +    rv = GetDoomedFile(getter_AddRefs(file));
  1.2017 +    NS_ENSURE_SUCCESS(rv, rv);
  1.2018 +
  1.2019 +    nsCOMPtr<nsIFile> parentDir;
  1.2020 +    rv = file->GetParent(getter_AddRefs(parentDir));
  1.2021 +    NS_ENSURE_SUCCESS(rv, rv);
  1.2022 +
  1.2023 +    nsAutoCString leafName;
  1.2024 +    rv = file->GetNativeLeafName(leafName);
  1.2025 +    NS_ENSURE_SUCCESS(rv, rv);
  1.2026 +
  1.2027 +    rv = aHandle->mFile->MoveToNative(parentDir, leafName);
  1.2028 +    if (NS_ERROR_FILE_NOT_FOUND == rv || NS_ERROR_FILE_TARGET_DOES_NOT_EXIST == rv) {
  1.2029 +      LOG(("  file already removed under our hands"));
  1.2030 +      aHandle->mFileExists = false;
  1.2031 +      rv = NS_OK;
  1.2032 +    } else {
  1.2033 +      NS_ENSURE_SUCCESS(rv, rv);
  1.2034 +      aHandle->mFile.swap(file);
  1.2035 +    }
  1.2036 +  }
  1.2037 +
  1.2038 +  if (!aHandle->IsSpecialFile()) {
  1.2039 +    CacheIndex::RemoveEntry(aHandle->Hash());
  1.2040 +  }
  1.2041 +
  1.2042 +  aHandle->mIsDoomed = true;
  1.2043 +
  1.2044 +  if (!aHandle->IsSpecialFile()) {
  1.2045 +    nsRefPtr<CacheStorageService> storageService = CacheStorageService::Self();
  1.2046 +    if (storageService) {
  1.2047 +      nsAutoCString idExtension, url;
  1.2048 +      nsCOMPtr<nsILoadContextInfo> info =
  1.2049 +        CacheFileUtils::ParseKey(aHandle->Key(), &idExtension, &url);
  1.2050 +      MOZ_ASSERT(info);
  1.2051 +      if (info) {
  1.2052 +        storageService->CacheFileDoomed(info, idExtension, url);
  1.2053 +      }
  1.2054 +    }
  1.2055 +  }
  1.2056 +
  1.2057 +  return NS_OK;
  1.2058 +}
  1.2059 +
  1.2060 +// static
  1.2061 +nsresult
  1.2062 +CacheFileIOManager::DoomFileByKey(const nsACString &aKey,
  1.2063 +                                  CacheFileIOListener *aCallback)
  1.2064 +{
  1.2065 +  LOG(("CacheFileIOManager::DoomFileByKey() [key=%s, listener=%p]",
  1.2066 +       PromiseFlatCString(aKey).get(), aCallback));
  1.2067 +
  1.2068 +  nsresult rv;
  1.2069 +  nsRefPtr<CacheFileIOManager> ioMan = gInstance;
  1.2070 +
  1.2071 +  if (!ioMan) {
  1.2072 +    return NS_ERROR_NOT_INITIALIZED;
  1.2073 +  }
  1.2074 +
  1.2075 +  nsRefPtr<DoomFileByKeyEvent> ev = new DoomFileByKeyEvent(aKey, aCallback);
  1.2076 +  rv = ioMan->mIOThread->DispatchAfterPendingOpens(ev);
  1.2077 +  NS_ENSURE_SUCCESS(rv, rv);
  1.2078 +
  1.2079 +  return NS_OK;
  1.2080 +}
  1.2081 +
  1.2082 +nsresult
  1.2083 +CacheFileIOManager::DoomFileByKeyInternal(const SHA1Sum::Hash *aHash)
  1.2084 +{
  1.2085 +  LOG(("CacheFileIOManager::DoomFileByKeyInternal() [hash=%08x%08x%08x%08x%08x]"
  1.2086 +       , LOGSHA1(aHash)));
  1.2087 +
  1.2088 +  MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
  1.2089 +
  1.2090 +  nsresult rv;
  1.2091 +
  1.2092 +  if (mShuttingDown) {
  1.2093 +    return NS_ERROR_NOT_INITIALIZED;
  1.2094 +  }
  1.2095 +
  1.2096 +  if (!mCacheDirectory) {
  1.2097 +    return NS_ERROR_FILE_INVALID_PATH;
  1.2098 +  }
  1.2099 +
  1.2100 +  // Find active handle
  1.2101 +  nsRefPtr<CacheFileHandle> handle;
  1.2102 +  mHandles.GetHandle(aHash, true, getter_AddRefs(handle));
  1.2103 +
  1.2104 +  if (handle) {
  1.2105 +    handle->Log();
  1.2106 +
  1.2107 +    if (handle->IsDoomed()) {
  1.2108 +      return NS_OK;
  1.2109 +    }
  1.2110 +
  1.2111 +    return DoomFileInternal(handle);
  1.2112 +  }
  1.2113 +
  1.2114 +  // There is no handle for this file, delete the file if exists
  1.2115 +  nsCOMPtr<nsIFile> file;
  1.2116 +  rv = GetFile(aHash, getter_AddRefs(file));
  1.2117 +  NS_ENSURE_SUCCESS(rv, rv);
  1.2118 +
  1.2119 +  bool exists;
  1.2120 +  rv = file->Exists(&exists);
  1.2121 +  NS_ENSURE_SUCCESS(rv, rv);
  1.2122 +
  1.2123 +  if (!exists) {
  1.2124 +    return NS_ERROR_NOT_AVAILABLE;
  1.2125 +  }
  1.2126 +
  1.2127 +  LOG(("CacheFileIOManager::DoomFileByKeyInternal() - Removing file from "
  1.2128 +       "disk"));
  1.2129 +  rv = file->Remove(false);
  1.2130 +  if (NS_FAILED(rv)) {
  1.2131 +    NS_WARNING("Cannot remove old entry from the disk");
  1.2132 +    LOG(("CacheFileIOManager::DoomFileByKeyInternal() - Removing file failed. "
  1.2133 +         "[rv=0x%08x]", rv));
  1.2134 +  }
  1.2135 +
  1.2136 +  CacheIndex::RemoveEntry(aHash);
  1.2137 +
  1.2138 +  return NS_OK;
  1.2139 +}
  1.2140 +
  1.2141 +// static
  1.2142 +nsresult
  1.2143 +CacheFileIOManager::ReleaseNSPRHandle(CacheFileHandle *aHandle)
  1.2144 +{
  1.2145 +  LOG(("CacheFileIOManager::ReleaseNSPRHandle() [handle=%p]", aHandle));
  1.2146 +
  1.2147 +  nsresult rv;
  1.2148 +  nsRefPtr<CacheFileIOManager> ioMan = gInstance;
  1.2149 +
  1.2150 +  if (aHandle->IsClosed() || !ioMan) {
  1.2151 +    return NS_ERROR_NOT_INITIALIZED;
  1.2152 +  }
  1.2153 +
  1.2154 +  nsRefPtr<ReleaseNSPRHandleEvent> ev = new ReleaseNSPRHandleEvent(aHandle);
  1.2155 +  rv = ioMan->mIOThread->Dispatch(ev, CacheIOThread::CLOSE);
  1.2156 +  NS_ENSURE_SUCCESS(rv, rv);
  1.2157 +
  1.2158 +  return NS_OK;
  1.2159 +}
  1.2160 +
  1.2161 +nsresult
  1.2162 +CacheFileIOManager::ReleaseNSPRHandleInternal(CacheFileHandle *aHandle)
  1.2163 +{
  1.2164 +  LOG(("CacheFileIOManager::ReleaseNSPRHandleInternal() [handle=%p]", aHandle));
  1.2165 +
  1.2166 +  MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
  1.2167 +  MOZ_ASSERT(aHandle->mFD);
  1.2168 +
  1.2169 +  DebugOnly<bool> found;
  1.2170 +  found = mHandlesByLastUsed.RemoveElement(aHandle);
  1.2171 +  MOZ_ASSERT(found);
  1.2172 +
  1.2173 +  PR_Close(aHandle->mFD);
  1.2174 +  aHandle->mFD = nullptr;
  1.2175 +
  1.2176 +  return NS_OK;
  1.2177 +}
  1.2178 +
  1.2179 +// static
  1.2180 +nsresult
  1.2181 +CacheFileIOManager::TruncateSeekSetEOF(CacheFileHandle *aHandle,
  1.2182 +                                       int64_t aTruncatePos, int64_t aEOFPos,
  1.2183 +                                       CacheFileIOListener *aCallback)
  1.2184 +{
  1.2185 +  LOG(("CacheFileIOManager::TruncateSeekSetEOF() [handle=%p, truncatePos=%lld, "
  1.2186 +       "EOFPos=%lld, listener=%p]", aHandle, aTruncatePos, aEOFPos, aCallback));
  1.2187 +
  1.2188 +  nsresult rv;
  1.2189 +  nsRefPtr<CacheFileIOManager> ioMan = gInstance;
  1.2190 +
  1.2191 +  if (aHandle->IsClosed() || !ioMan) {
  1.2192 +    return NS_ERROR_NOT_INITIALIZED;
  1.2193 +  }
  1.2194 +
  1.2195 +  nsRefPtr<TruncateSeekSetEOFEvent> ev = new TruncateSeekSetEOFEvent(
  1.2196 +                                           aHandle, aTruncatePos, aEOFPos,
  1.2197 +                                           aCallback);
  1.2198 +  rv = ioMan->mIOThread->Dispatch(ev, CacheIOThread::WRITE);
  1.2199 +  NS_ENSURE_SUCCESS(rv, rv);
  1.2200 +
  1.2201 +  return NS_OK;
  1.2202 +}
  1.2203 +
  1.2204 +// static
  1.2205 +void CacheFileIOManager::GetCacheDirectory(nsIFile** result)
  1.2206 +{
  1.2207 +  *result = nullptr;
  1.2208 +
  1.2209 +  nsRefPtr<CacheFileIOManager> ioMan = gInstance;
  1.2210 +  if (!ioMan) {
  1.2211 +    return;
  1.2212 +  }
  1.2213 +
  1.2214 +  nsCOMPtr<nsIFile> file = ioMan->mCacheDirectory;
  1.2215 +  file.forget(result);
  1.2216 +}
  1.2217 +
  1.2218 +static nsresult
  1.2219 +TruncFile(PRFileDesc *aFD, uint32_t aEOF)
  1.2220 +{
  1.2221 +#if defined(XP_UNIX)
  1.2222 +  if (ftruncate(PR_FileDesc2NativeHandle(aFD), aEOF) != 0) {
  1.2223 +    NS_ERROR("ftruncate failed");
  1.2224 +    return NS_ERROR_FAILURE;
  1.2225 +  }
  1.2226 +#elif defined(XP_WIN)
  1.2227 +  int32_t cnt = PR_Seek(aFD, aEOF, PR_SEEK_SET);
  1.2228 +  if (cnt == -1) {
  1.2229 +    return NS_ERROR_FAILURE;
  1.2230 +  }
  1.2231 +  if (!SetEndOfFile((HANDLE) PR_FileDesc2NativeHandle(aFD))) {
  1.2232 +    NS_ERROR("SetEndOfFile failed");
  1.2233 +    return NS_ERROR_FAILURE;
  1.2234 +  }
  1.2235 +#else
  1.2236 +  MOZ_ASSERT(false, "Not implemented!");
  1.2237 +#endif
  1.2238 +
  1.2239 +  return NS_OK;
  1.2240 +}
  1.2241 +
  1.2242 +nsresult
  1.2243 +CacheFileIOManager::TruncateSeekSetEOFInternal(CacheFileHandle *aHandle,
  1.2244 +                                               int64_t aTruncatePos,
  1.2245 +                                               int64_t aEOFPos)
  1.2246 +{
  1.2247 +  LOG(("CacheFileIOManager::TruncateSeekSetEOFInternal() [handle=%p, "
  1.2248 +       "truncatePos=%lld, EOFPos=%lld]", aHandle, aTruncatePos, aEOFPos));
  1.2249 +
  1.2250 +  nsresult rv;
  1.2251 +
  1.2252 +  if (!aHandle->mFileExists) {
  1.2253 +    rv = CreateFile(aHandle);
  1.2254 +    NS_ENSURE_SUCCESS(rv, rv);
  1.2255 +  }
  1.2256 +
  1.2257 +  if (!aHandle->mFD) {
  1.2258 +    rv = OpenNSPRHandle(aHandle);
  1.2259 +    NS_ENSURE_SUCCESS(rv, rv);
  1.2260 +  } else {
  1.2261 +    NSPRHandleUsed(aHandle);
  1.2262 +  }
  1.2263 +
  1.2264 +  // Check again, OpenNSPRHandle could figure out the file was gone.
  1.2265 +  if (!aHandle->mFileExists) {
  1.2266 +    return NS_ERROR_NOT_AVAILABLE;
  1.2267 +  }
  1.2268 +
  1.2269 +  // This operation always invalidates the entry
  1.2270 +  aHandle->mInvalid = true;
  1.2271 +
  1.2272 +  rv = TruncFile(aHandle->mFD, static_cast<uint32_t>(aTruncatePos));
  1.2273 +  NS_ENSURE_SUCCESS(rv, rv);
  1.2274 +
  1.2275 +  rv = TruncFile(aHandle->mFD, static_cast<uint32_t>(aEOFPos));
  1.2276 +  NS_ENSURE_SUCCESS(rv, rv);
  1.2277 +
  1.2278 +  return NS_OK;
  1.2279 +}
  1.2280 +
  1.2281 +// static
  1.2282 +nsresult
  1.2283 +CacheFileIOManager::RenameFile(CacheFileHandle *aHandle,
  1.2284 +                               const nsACString &aNewName,
  1.2285 +                               CacheFileIOListener *aCallback)
  1.2286 +{
  1.2287 +  LOG(("CacheFileIOManager::RenameFile() [handle=%p, newName=%s, listener=%p]",
  1.2288 +       aHandle, PromiseFlatCString(aNewName).get(), aCallback));
  1.2289 +
  1.2290 +  nsresult rv;
  1.2291 +  nsRefPtr<CacheFileIOManager> ioMan = gInstance;
  1.2292 +
  1.2293 +  if (aHandle->IsClosed() || !ioMan) {
  1.2294 +    return NS_ERROR_NOT_INITIALIZED;
  1.2295 +  }
  1.2296 +
  1.2297 +  if (!aHandle->IsSpecialFile()) {
  1.2298 +    return NS_ERROR_UNEXPECTED;
  1.2299 +  }
  1.2300 +
  1.2301 +  nsRefPtr<RenameFileEvent> ev = new RenameFileEvent(aHandle, aNewName,
  1.2302 +                                                     aCallback);
  1.2303 +  rv = ioMan->mIOThread->Dispatch(ev, CacheIOThread::WRITE);
  1.2304 +  NS_ENSURE_SUCCESS(rv, rv);
  1.2305 +
  1.2306 +  return NS_OK;
  1.2307 +}
  1.2308 +
  1.2309 +nsresult
  1.2310 +CacheFileIOManager::RenameFileInternal(CacheFileHandle *aHandle,
  1.2311 +                                       const nsACString &aNewName)
  1.2312 +{
  1.2313 +  LOG(("CacheFileIOManager::RenameFileInternal() [handle=%p, newName=%s]",
  1.2314 +       aHandle, PromiseFlatCString(aNewName).get()));
  1.2315 +
  1.2316 +  nsresult rv;
  1.2317 +
  1.2318 +  MOZ_ASSERT(aHandle->IsSpecialFile());
  1.2319 +
  1.2320 +  if (aHandle->IsDoomed()) {
  1.2321 +    return NS_ERROR_NOT_AVAILABLE;
  1.2322 +  }
  1.2323 +
  1.2324 +  // Doom old handle if it exists and is not doomed
  1.2325 +  for (uint32_t i = 0 ; i < mSpecialHandles.Length() ; i++) {
  1.2326 +    if (!mSpecialHandles[i]->IsDoomed() &&
  1.2327 +        mSpecialHandles[i]->Key() == aNewName) {
  1.2328 +      MOZ_ASSERT(aHandle != mSpecialHandles[i]);
  1.2329 +      rv = DoomFileInternal(mSpecialHandles[i]);
  1.2330 +      NS_ENSURE_SUCCESS(rv, rv);
  1.2331 +      break;
  1.2332 +    }
  1.2333 +  }
  1.2334 +
  1.2335 +  nsCOMPtr<nsIFile> file;
  1.2336 +  rv = GetSpecialFile(aNewName, getter_AddRefs(file));
  1.2337 +  NS_ENSURE_SUCCESS(rv, rv);
  1.2338 +
  1.2339 +  bool exists;
  1.2340 +  rv = file->Exists(&exists);
  1.2341 +  NS_ENSURE_SUCCESS(rv, rv);
  1.2342 +
  1.2343 +  if (exists) {
  1.2344 +    LOG(("CacheFileIOManager::RenameFileInternal() - Removing old file from "
  1.2345 +         "disk"));
  1.2346 +    rv = file->Remove(false);
  1.2347 +    if (NS_FAILED(rv)) {
  1.2348 +      NS_WARNING("Cannot remove file from the disk");
  1.2349 +      LOG(("CacheFileIOManager::RenameFileInternal() - Removing old file failed"
  1.2350 +           ". [rv=0x%08x]", rv));
  1.2351 +    }
  1.2352 +  }
  1.2353 +
  1.2354 +  if (!aHandle->FileExists()) {
  1.2355 +    aHandle->mKey = aNewName;
  1.2356 +    return NS_OK;
  1.2357 +  }
  1.2358 +
  1.2359 +  if (aHandle->mFD) {
  1.2360 +    ReleaseNSPRHandleInternal(aHandle);
  1.2361 +  }
  1.2362 +
  1.2363 +  rv = aHandle->mFile->MoveToNative(nullptr, aNewName);
  1.2364 +  NS_ENSURE_SUCCESS(rv, rv);
  1.2365 +
  1.2366 +  aHandle->mKey = aNewName;
  1.2367 +  return NS_OK;
  1.2368 +}
  1.2369 +
  1.2370 +// static
  1.2371 +nsresult
  1.2372 +CacheFileIOManager::EvictIfOverLimit()
  1.2373 +{
  1.2374 +  LOG(("CacheFileIOManager::EvictIfOverLimit()"));
  1.2375 +
  1.2376 +  nsresult rv;
  1.2377 +  nsRefPtr<CacheFileIOManager> ioMan = gInstance;
  1.2378 +
  1.2379 +  if (!ioMan) {
  1.2380 +    return NS_ERROR_NOT_INITIALIZED;
  1.2381 +  }
  1.2382 +
  1.2383 +  nsCOMPtr<nsIRunnable> ev;
  1.2384 +  ev = NS_NewRunnableMethod(ioMan,
  1.2385 +                            &CacheFileIOManager::EvictIfOverLimitInternal);
  1.2386 +
  1.2387 +  rv = ioMan->mIOThread->Dispatch(ev, CacheIOThread::EVICT);
  1.2388 +  NS_ENSURE_SUCCESS(rv, rv);
  1.2389 +
  1.2390 +  return NS_OK;
  1.2391 +}
  1.2392 +
  1.2393 +nsresult
  1.2394 +CacheFileIOManager::EvictIfOverLimitInternal()
  1.2395 +{
  1.2396 +  LOG(("CacheFileIOManager::EvictIfOverLimitInternal()"));
  1.2397 +
  1.2398 +  nsresult rv;
  1.2399 +
  1.2400 +  MOZ_ASSERT(mIOThread->IsCurrentThread());
  1.2401 +
  1.2402 +  if (mShuttingDown) {
  1.2403 +    return NS_ERROR_NOT_INITIALIZED;
  1.2404 +  }
  1.2405 +
  1.2406 +  if (mOverLimitEvicting) {
  1.2407 +    LOG(("CacheFileIOManager::EvictIfOverLimitInternal() - Eviction already "
  1.2408 +         "running."));
  1.2409 +    return NS_OK;
  1.2410 +  }
  1.2411 +
  1.2412 +  UpdateSmartCacheSize();
  1.2413 +
  1.2414 +  uint32_t cacheUsage;
  1.2415 +  rv = CacheIndex::GetCacheSize(&cacheUsage);
  1.2416 +  NS_ENSURE_SUCCESS(rv, rv);
  1.2417 +
  1.2418 +  uint32_t cacheLimit = CacheObserver::DiskCacheCapacity() >> 10;
  1.2419 +  if (cacheUsage <= cacheLimit) {
  1.2420 +    LOG(("CacheFileIOManager::EvictIfOverLimitInternal() - Cache size under "
  1.2421 +         "limit. [cacheSize=%u, limit=%u]", cacheUsage, cacheLimit));
  1.2422 +    return NS_OK;
  1.2423 +  }
  1.2424 +
  1.2425 +  LOG(("CacheFileIOManager::EvictIfOverLimitInternal() - Cache size exceeded "
  1.2426 +       "limit. Starting overlimit eviction. [cacheSize=%u, limit=%u]",
  1.2427 +       cacheUsage, cacheLimit));
  1.2428 +
  1.2429 +  nsCOMPtr<nsIRunnable> ev;
  1.2430 +  ev = NS_NewRunnableMethod(this,
  1.2431 +                            &CacheFileIOManager::OverLimitEvictionInternal);
  1.2432 +
  1.2433 +  rv = mIOThread->Dispatch(ev, CacheIOThread::EVICT);
  1.2434 +  NS_ENSURE_SUCCESS(rv, rv);
  1.2435 +
  1.2436 +  mOverLimitEvicting = true;
  1.2437 +  return NS_OK;
  1.2438 +}
  1.2439 +
  1.2440 +nsresult
  1.2441 +CacheFileIOManager::OverLimitEvictionInternal()
  1.2442 +{
  1.2443 +  LOG(("CacheFileIOManager::OverLimitEvictionInternal()"));
  1.2444 +
  1.2445 +  nsresult rv;
  1.2446 +
  1.2447 +  MOZ_ASSERT(mIOThread->IsCurrentThread());
  1.2448 +
  1.2449 +  // mOverLimitEvicting is accessed only on IO thread, so we can set it to false
  1.2450 +  // here and set it to true again once we dispatch another event that will
  1.2451 +  // continue with the eviction. The reason why we do so is that we can fail
  1.2452 +  // early anywhere in this method and the variable will contain a correct
  1.2453 +  // value. Otherwise we would need to set it to false on every failing place.
  1.2454 +  mOverLimitEvicting = false;
  1.2455 +
  1.2456 +  if (mShuttingDown) {
  1.2457 +    return NS_ERROR_NOT_INITIALIZED;
  1.2458 +  }
  1.2459 +
  1.2460 +  UpdateSmartCacheSize();
  1.2461 +
  1.2462 +  while (true) {
  1.2463 +    uint32_t cacheUsage;
  1.2464 +    rv = CacheIndex::GetCacheSize(&cacheUsage);
  1.2465 +    NS_ENSURE_SUCCESS(rv, rv);
  1.2466 +
  1.2467 +    uint32_t cacheLimit = CacheObserver::DiskCacheCapacity() >> 10;
  1.2468 +    if (cacheUsage <= cacheLimit) {
  1.2469 +      LOG(("CacheFileIOManager::OverLimitEvictionInternal() - Cache size under "
  1.2470 +           "limit. [cacheSize=%u, limit=%u]", cacheUsage, cacheLimit));
  1.2471 +      return NS_OK;
  1.2472 +    }
  1.2473 +
  1.2474 +    LOG(("CacheFileIOManager::OverLimitEvictionInternal() - Cache size over "
  1.2475 +         "limit. [cacheSize=%u, limit=%u]", cacheUsage, cacheLimit));
  1.2476 +
  1.2477 +    if (CacheIOThread::YieldAndRerun()) {
  1.2478 +      LOG(("CacheFileIOManager::OverLimitEvictionInternal() - Breaking loop "
  1.2479 +           "for higher level events."));
  1.2480 +      mOverLimitEvicting = true;
  1.2481 +      return NS_OK;
  1.2482 +    }
  1.2483 +
  1.2484 +    SHA1Sum::Hash hash;
  1.2485 +    uint32_t cnt;
  1.2486 +    static uint32_t consecutiveFailures = 0;
  1.2487 +    rv = CacheIndex::GetEntryForEviction(&hash, &cnt);
  1.2488 +    NS_ENSURE_SUCCESS(rv, rv);
  1.2489 +
  1.2490 +    rv = DoomFileByKeyInternal(&hash);
  1.2491 +    if (NS_SUCCEEDED(rv)) {
  1.2492 +      consecutiveFailures = 0;
  1.2493 +    } else if (rv == NS_ERROR_NOT_AVAILABLE) {
  1.2494 +      LOG(("CacheFileIOManager::OverLimitEvictionInternal() - "
  1.2495 +           "DoomFileByKeyInternal() failed. [rv=0x%08x]", rv));
  1.2496 +      // TODO index is outdated, start update
  1.2497 +
  1.2498 +      // Make sure index won't return the same entry again
  1.2499 +      CacheIndex::RemoveEntry(&hash);
  1.2500 +      consecutiveFailures = 0;
  1.2501 +    } else {
  1.2502 +      // This shouldn't normally happen, but the eviction must not fail
  1.2503 +      // completely if we ever encounter this problem.
  1.2504 +      NS_WARNING("CacheFileIOManager::OverLimitEvictionInternal() - Unexpected "
  1.2505 +                 "failure of DoomFileByKeyInternal()");
  1.2506 +
  1.2507 +      LOG(("CacheFileIOManager::OverLimitEvictionInternal() - "
  1.2508 +           "DoomFileByKeyInternal() failed. [rv=0x%08x]", rv));
  1.2509 +
  1.2510 +      // Normally, CacheIndex::UpdateEntry() is called only to update newly
  1.2511 +      // created/opened entries which are always fresh and UpdateEntry() expects
  1.2512 +      // and checks this flag. The way we use UpdateEntry() here is a kind of
  1.2513 +      // hack and we must make sure the flag is set by calling
  1.2514 +      // EnsureEntryExists().
  1.2515 +      rv = CacheIndex::EnsureEntryExists(&hash);
  1.2516 +      NS_ENSURE_SUCCESS(rv, rv);
  1.2517 +
  1.2518 +      // Move the entry at the end of both lists to make sure we won't end up
  1.2519 +      // failing on one entry forever.
  1.2520 +      uint32_t frecency = 0;
  1.2521 +      uint32_t expTime = nsICacheEntry::NO_EXPIRATION_TIME;
  1.2522 +      rv = CacheIndex::UpdateEntry(&hash, &frecency, &expTime, nullptr);
  1.2523 +      NS_ENSURE_SUCCESS(rv, rv);
  1.2524 +
  1.2525 +      consecutiveFailures++;
  1.2526 +      if (consecutiveFailures >= cnt) {
  1.2527 +        // This doesn't necessarily mean that we've tried to doom every entry
  1.2528 +        // but we've reached a sane number of tries. It is likely that another
  1.2529 +        // eviction will start soon. And as said earlier, this normally doesn't
  1.2530 +        // happen at all.
  1.2531 +        return NS_OK;
  1.2532 +      }
  1.2533 +    }
  1.2534 +  }
  1.2535 +
  1.2536 +  NS_NOTREACHED("We should never get here");
  1.2537 +  return NS_OK;
  1.2538 +}
  1.2539 +
  1.2540 +// static
  1.2541 +nsresult
  1.2542 +CacheFileIOManager::EvictAll()
  1.2543 +{
  1.2544 +  LOG(("CacheFileIOManager::EvictAll()"));
  1.2545 +
  1.2546 +  nsresult rv;
  1.2547 +  nsRefPtr<CacheFileIOManager> ioMan = gInstance;
  1.2548 +
  1.2549 +  if (!ioMan) {
  1.2550 +    return NS_ERROR_NOT_INITIALIZED;
  1.2551 +  }
  1.2552 +
  1.2553 +  nsCOMPtr<nsIRunnable> ev;
  1.2554 +  ev = NS_NewRunnableMethod(ioMan, &CacheFileIOManager::EvictAllInternal);
  1.2555 +
  1.2556 +  rv = ioMan->mIOThread->DispatchAfterPendingOpens(ev);
  1.2557 +  if (NS_WARN_IF(NS_FAILED(rv))) {
  1.2558 +    return rv;
  1.2559 +  }
  1.2560 +
  1.2561 +  return NS_OK;
  1.2562 +}
  1.2563 +
  1.2564 +namespace {
  1.2565 +
  1.2566 +class EvictionNotifierRunnable : public nsRunnable
  1.2567 +{
  1.2568 +public:
  1.2569 +  NS_DECL_NSIRUNNABLE
  1.2570 +};
  1.2571 +
  1.2572 +NS_IMETHODIMP
  1.2573 +EvictionNotifierRunnable::Run()
  1.2574 +{
  1.2575 +  nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService();
  1.2576 +  if (obsSvc) {
  1.2577 +    obsSvc->NotifyObservers(nullptr, "cacheservice:empty-cache", nullptr);
  1.2578 +  }
  1.2579 +  return NS_OK;
  1.2580 +}
  1.2581 +
  1.2582 +} // anonymous namespace
  1.2583 +
  1.2584 +nsresult
  1.2585 +CacheFileIOManager::EvictAllInternal()
  1.2586 +{
  1.2587 +  LOG(("CacheFileIOManager::EvictAllInternal()"));
  1.2588 +
  1.2589 +  nsresult rv;
  1.2590 +
  1.2591 +  MOZ_ASSERT(mIOThread->IsCurrentThread());
  1.2592 +
  1.2593 +  nsRefPtr<EvictionNotifierRunnable> r = new EvictionNotifierRunnable();
  1.2594 +
  1.2595 +  if (!mCacheDirectory) {
  1.2596 +    // This is a kind of hack. Somebody called EvictAll() without a profile.
  1.2597 +    // This happens in xpcshell tests that use cache without profile. We need
  1.2598 +    // to notify observers in this case since the tests are waiting for it.
  1.2599 +    NS_DispatchToMainThread(r);
  1.2600 +    return NS_ERROR_FILE_INVALID_PATH;
  1.2601 +  }
  1.2602 +
  1.2603 +  if (mShuttingDown) {
  1.2604 +    return NS_ERROR_NOT_INITIALIZED;
  1.2605 +  }
  1.2606 +
  1.2607 +  if (!mTreeCreated) {
  1.2608 +    rv = CreateCacheTree();
  1.2609 +    if (NS_FAILED(rv)) {
  1.2610 +      return rv;
  1.2611 +    }
  1.2612 +  }
  1.2613 +
  1.2614 +  // Doom all active handles
  1.2615 +  nsTArray<nsRefPtr<CacheFileHandle> > handles;
  1.2616 +  mHandles.GetActiveHandles(&handles);
  1.2617 +
  1.2618 +  for (uint32_t i = 0; i < handles.Length(); ++i) {
  1.2619 +    rv = DoomFileInternal(handles[i]);
  1.2620 +    if (NS_WARN_IF(NS_FAILED(rv))) {
  1.2621 +      LOG(("CacheFileIOManager::EvictAllInternal() - Cannot doom handle "
  1.2622 +           "[handle=%p]", handles[i].get()));
  1.2623 +    }
  1.2624 +  }
  1.2625 +
  1.2626 +  nsCOMPtr<nsIFile> file;
  1.2627 +  rv = mCacheDirectory->Clone(getter_AddRefs(file));
  1.2628 +  if (NS_WARN_IF(NS_FAILED(rv))) {
  1.2629 +    return rv;
  1.2630 +  }
  1.2631 +
  1.2632 +  rv = file->AppendNative(NS_LITERAL_CSTRING(kEntriesDir));
  1.2633 +  if (NS_WARN_IF(NS_FAILED(rv))) {
  1.2634 +    return rv;
  1.2635 +  }
  1.2636 +
  1.2637 +  // Trash current entries directory
  1.2638 +  rv = TrashDirectory(file);
  1.2639 +  if (NS_WARN_IF(NS_FAILED(rv))) {
  1.2640 +    return rv;
  1.2641 +  }
  1.2642 +
  1.2643 +  // Files are now inaccessible in entries directory, notify observers.
  1.2644 +  NS_DispatchToMainThread(r);
  1.2645 +
  1.2646 +  // Create a new empty entries directory
  1.2647 +  rv = CheckAndCreateDir(mCacheDirectory, kEntriesDir, false);
  1.2648 +  if (NS_WARN_IF(NS_FAILED(rv))) {
  1.2649 +    return rv;
  1.2650 +  }
  1.2651 +
  1.2652 +  CacheIndex::RemoveAll();
  1.2653 +
  1.2654 +  return NS_OK;
  1.2655 +}
  1.2656 +
  1.2657 +// static
  1.2658 +nsresult
  1.2659 +CacheFileIOManager::EvictByContext(nsILoadContextInfo *aLoadContextInfo)
  1.2660 +{
  1.2661 +  LOG(("CacheFileIOManager::EvictByContext() [loadContextInfo=%p]",
  1.2662 +       aLoadContextInfo));
  1.2663 +
  1.2664 +  nsresult rv;
  1.2665 +  nsRefPtr<CacheFileIOManager> ioMan = gInstance;
  1.2666 +
  1.2667 +  if (!ioMan) {
  1.2668 +    return NS_ERROR_NOT_INITIALIZED;
  1.2669 +  }
  1.2670 +
  1.2671 +  nsCOMPtr<nsIRunnable> ev;
  1.2672 +  ev = NS_NewRunnableMethodWithArg<nsCOMPtr<nsILoadContextInfo> >
  1.2673 +         (ioMan, &CacheFileIOManager::EvictByContextInternal, aLoadContextInfo);
  1.2674 +
  1.2675 +  rv = ioMan->mIOThread->DispatchAfterPendingOpens(ev);
  1.2676 +  if (NS_WARN_IF(NS_FAILED(rv))) {
  1.2677 +    return rv;
  1.2678 +  }
  1.2679 +
  1.2680 +  return NS_OK;
  1.2681 +}
  1.2682 +
  1.2683 +nsresult
  1.2684 +CacheFileIOManager::EvictByContextInternal(nsILoadContextInfo *aLoadContextInfo)
  1.2685 +{
  1.2686 +  LOG(("CacheFileIOManager::EvictByContextInternal() [loadContextInfo=%p, "
  1.2687 +       "anonymous=%u, inBrowser=%u, appId=%u]", aLoadContextInfo,
  1.2688 +       aLoadContextInfo->IsAnonymous(), aLoadContextInfo->IsInBrowserElement(),
  1.2689 +       aLoadContextInfo->AppId()));
  1.2690 +
  1.2691 +  nsresult rv;
  1.2692 +
  1.2693 +  MOZ_ASSERT(mIOThread->IsCurrentThread());
  1.2694 +
  1.2695 +  MOZ_ASSERT(!aLoadContextInfo->IsPrivate());
  1.2696 +  if (aLoadContextInfo->IsPrivate()) {
  1.2697 +    return NS_ERROR_INVALID_ARG;
  1.2698 +  }
  1.2699 +
  1.2700 +  if (!mCacheDirectory) {
  1.2701 +    return NS_ERROR_FILE_INVALID_PATH;
  1.2702 +  }
  1.2703 +
  1.2704 +  if (mShuttingDown) {
  1.2705 +    return NS_ERROR_NOT_INITIALIZED;
  1.2706 +  }
  1.2707 +
  1.2708 +  if (!mTreeCreated) {
  1.2709 +    rv = CreateCacheTree();
  1.2710 +    if (NS_FAILED(rv)) {
  1.2711 +      return rv;
  1.2712 +    }
  1.2713 +  }
  1.2714 +
  1.2715 +  // Doom all active handles that matches the load context
  1.2716 +  nsTArray<nsRefPtr<CacheFileHandle> > handles;
  1.2717 +  mHandles.GetActiveHandles(&handles);
  1.2718 +
  1.2719 +  for (uint32_t i = 0; i < handles.Length(); ++i) {
  1.2720 +    bool equals;
  1.2721 +    rv = CacheFileUtils::KeyMatchesLoadContextInfo(handles[i]->Key(),
  1.2722 +                                                   aLoadContextInfo,
  1.2723 +                                                   &equals);
  1.2724 +    if (NS_FAILED(rv)) {
  1.2725 +      LOG(("CacheFileIOManager::EvictByContextInternal() - Cannot parse key in "
  1.2726 +           "handle! [handle=%p, key=%s]", handles[i].get(),
  1.2727 +           handles[i]->Key().get()));
  1.2728 +      MOZ_CRASH("Unexpected error!");
  1.2729 +    }
  1.2730 +
  1.2731 +    if (equals) {
  1.2732 +      rv = DoomFileInternal(handles[i]);
  1.2733 +      if (NS_WARN_IF(NS_FAILED(rv))) {
  1.2734 +        LOG(("CacheFileIOManager::EvictByContextInternal() - Cannot doom handle"
  1.2735 +             " [handle=%p]", handles[i].get()));
  1.2736 +      }
  1.2737 +    }
  1.2738 +  }
  1.2739 +
  1.2740 +  if (!mContextEvictor) {
  1.2741 +    mContextEvictor = new CacheFileContextEvictor();
  1.2742 +    mContextEvictor->Init(mCacheDirectory);
  1.2743 +  }
  1.2744 +
  1.2745 +  mContextEvictor->AddContext(aLoadContextInfo);
  1.2746 +
  1.2747 +  return NS_OK;
  1.2748 +}
  1.2749 +
  1.2750 +// static
  1.2751 +nsresult
  1.2752 +CacheFileIOManager::CacheIndexStateChanged()
  1.2753 +{
  1.2754 +  LOG(("CacheFileIOManager::CacheIndexStateChanged()"));
  1.2755 +
  1.2756 +  nsresult rv;
  1.2757 +
  1.2758 +  // CacheFileIOManager lives longer than CacheIndex so gInstance must be
  1.2759 +  // non-null here.
  1.2760 +  MOZ_ASSERT(gInstance);
  1.2761 +
  1.2762 +  // We have to re-distatch even if we are on IO thread to prevent reentering
  1.2763 +  // the lock in CacheIndex
  1.2764 +  nsCOMPtr<nsIRunnable> ev;
  1.2765 +  ev = NS_NewRunnableMethod(
  1.2766 +    gInstance, &CacheFileIOManager::CacheIndexStateChangedInternal);
  1.2767 +
  1.2768 +  nsCOMPtr<nsIEventTarget> ioTarget = IOTarget();
  1.2769 +  MOZ_ASSERT(ioTarget);
  1.2770 +
  1.2771 +  rv = ioTarget->Dispatch(ev, nsIEventTarget::DISPATCH_NORMAL);
  1.2772 +  if (NS_WARN_IF(NS_FAILED(rv))) {
  1.2773 +    return rv;
  1.2774 +  }
  1.2775 +
  1.2776 +  return NS_OK;
  1.2777 +}
  1.2778 +
  1.2779 +nsresult
  1.2780 +CacheFileIOManager::CacheIndexStateChangedInternal()
  1.2781 +{
  1.2782 +  if (mShuttingDown) {
  1.2783 +    // ignore notification during shutdown
  1.2784 +    return NS_OK;
  1.2785 +  }
  1.2786 +
  1.2787 +  if (!mContextEvictor) {
  1.2788 +    return NS_OK;
  1.2789 +  }
  1.2790 +
  1.2791 +  mContextEvictor->CacheIndexStateChanged();
  1.2792 +  return NS_OK;
  1.2793 +}
  1.2794 +
  1.2795 +nsresult
  1.2796 +CacheFileIOManager::TrashDirectory(nsIFile *aFile)
  1.2797 +{
  1.2798 +#ifdef PR_LOGGING
  1.2799 +  nsAutoCString path;
  1.2800 +  aFile->GetNativePath(path);
  1.2801 +#endif
  1.2802 +  LOG(("CacheFileIOManager::TrashDirectory() [file=%s]", path.get()));
  1.2803 +
  1.2804 +  nsresult rv;
  1.2805 +
  1.2806 +  MOZ_ASSERT(mIOThread->IsCurrentThread());
  1.2807 +  MOZ_ASSERT(mCacheDirectory);
  1.2808 +
  1.2809 +  // When the directory is empty, it is cheaper to remove it directly instead of
  1.2810 +  // using the trash mechanism.
  1.2811 +  bool isEmpty;
  1.2812 +  rv = IsEmptyDirectory(aFile, &isEmpty);
  1.2813 +  NS_ENSURE_SUCCESS(rv, rv);
  1.2814 +
  1.2815 +  if (isEmpty) {
  1.2816 +    rv = aFile->Remove(false);
  1.2817 +    LOG(("CacheFileIOManager::TrashDirectory() - Directory removed [rv=0x%08x]",
  1.2818 +         rv));
  1.2819 +    return rv;
  1.2820 +  }
  1.2821 +
  1.2822 +#ifdef DEBUG
  1.2823 +  nsCOMPtr<nsIFile> dirCheck;
  1.2824 +  rv = aFile->GetParent(getter_AddRefs(dirCheck));
  1.2825 +  NS_ENSURE_SUCCESS(rv, rv);
  1.2826 +
  1.2827 +  bool equals = false;
  1.2828 +  rv = dirCheck->Equals(mCacheDirectory, &equals);
  1.2829 +  NS_ENSURE_SUCCESS(rv, rv);
  1.2830 +
  1.2831 +  MOZ_ASSERT(equals);
  1.2832 +#endif
  1.2833 +
  1.2834 +  nsCOMPtr<nsIFile> dir, trash;
  1.2835 +  nsAutoCString leaf;
  1.2836 +
  1.2837 +  rv = aFile->Clone(getter_AddRefs(dir));
  1.2838 +  NS_ENSURE_SUCCESS(rv, rv);
  1.2839 +
  1.2840 +  rv = aFile->Clone(getter_AddRefs(trash));
  1.2841 +  NS_ENSURE_SUCCESS(rv, rv);
  1.2842 +
  1.2843 +  srand(static_cast<unsigned>(PR_Now()));
  1.2844 +  while (true) {
  1.2845 +    leaf = kTrashDir;
  1.2846 +    leaf.AppendInt(rand());
  1.2847 +    rv = trash->SetNativeLeafName(leaf);
  1.2848 +    NS_ENSURE_SUCCESS(rv, rv);
  1.2849 +
  1.2850 +    bool exists;
  1.2851 +    if (NS_SUCCEEDED(trash->Exists(&exists)) && !exists) {
  1.2852 +      break;
  1.2853 +    }
  1.2854 +  }
  1.2855 +
  1.2856 +  LOG(("CacheFileIOManager::TrashDirectory() - Renaming directory [leaf=%s]",
  1.2857 +       leaf.get()));
  1.2858 +
  1.2859 +  rv = dir->MoveToNative(nullptr, leaf);
  1.2860 +  NS_ENSURE_SUCCESS(rv, rv);
  1.2861 +
  1.2862 +  StartRemovingTrash();
  1.2863 +  return NS_OK;
  1.2864 +}
  1.2865 +
  1.2866 +// static
  1.2867 +void
  1.2868 +CacheFileIOManager::OnTrashTimer(nsITimer *aTimer, void *aClosure)
  1.2869 +{
  1.2870 +  LOG(("CacheFileIOManager::OnTrashTimer() [timer=%p, closure=%p]", aTimer,
  1.2871 +       aClosure));
  1.2872 +
  1.2873 +  nsRefPtr<CacheFileIOManager> ioMan = gInstance;
  1.2874 +
  1.2875 +  if (!ioMan) {
  1.2876 +    return;
  1.2877 +  }
  1.2878 +
  1.2879 +  ioMan->mTrashTimer = nullptr;
  1.2880 +  ioMan->StartRemovingTrash();
  1.2881 +}
  1.2882 +
  1.2883 +nsresult
  1.2884 +CacheFileIOManager::StartRemovingTrash()
  1.2885 +{
  1.2886 +  LOG(("CacheFileIOManager::StartRemovingTrash()"));
  1.2887 +
  1.2888 +  nsresult rv;
  1.2889 +
  1.2890 +  MOZ_ASSERT(mIOThread->IsCurrentThread());
  1.2891 +
  1.2892 +  if (mShuttingDown) {
  1.2893 +    return NS_ERROR_NOT_INITIALIZED;
  1.2894 +  }
  1.2895 +
  1.2896 +  if (!mCacheDirectory) {
  1.2897 +    return NS_ERROR_FILE_INVALID_PATH;
  1.2898 +  }
  1.2899 +
  1.2900 +  if (mTrashTimer) {
  1.2901 +    LOG(("CacheFileIOManager::StartRemovingTrash() - Trash timer exists."));
  1.2902 +    return NS_OK;
  1.2903 +  }
  1.2904 +
  1.2905 +  if (mRemovingTrashDirs) {
  1.2906 +    LOG(("CacheFileIOManager::StartRemovingTrash() - Trash removing in "
  1.2907 +         "progress."));
  1.2908 +    return NS_OK;
  1.2909 +  }
  1.2910 +
  1.2911 +  uint32_t elapsed = (TimeStamp::NowLoRes() - mStartTime).ToMilliseconds();
  1.2912 +  if (elapsed < kRemoveTrashStartDelay) {
  1.2913 +    nsCOMPtr<nsITimer> timer = do_CreateInstance("@mozilla.org/timer;1", &rv);
  1.2914 +    NS_ENSURE_SUCCESS(rv, rv);
  1.2915 +
  1.2916 +    nsCOMPtr<nsIEventTarget> ioTarget = IOTarget();
  1.2917 +    MOZ_ASSERT(ioTarget);
  1.2918 +
  1.2919 +    rv = timer->SetTarget(ioTarget);
  1.2920 +    NS_ENSURE_SUCCESS(rv, rv);
  1.2921 +
  1.2922 +    rv = timer->InitWithFuncCallback(CacheFileIOManager::OnTrashTimer, nullptr,
  1.2923 +                                     kRemoveTrashStartDelay - elapsed,
  1.2924 +                                     nsITimer::TYPE_ONE_SHOT);
  1.2925 +    NS_ENSURE_SUCCESS(rv, rv);
  1.2926 +
  1.2927 +    mTrashTimer.swap(timer);
  1.2928 +    return NS_OK;
  1.2929 +  }
  1.2930 +
  1.2931 +  nsCOMPtr<nsIRunnable> ev;
  1.2932 +  ev = NS_NewRunnableMethod(this,
  1.2933 +                            &CacheFileIOManager::RemoveTrashInternal);
  1.2934 +
  1.2935 +  rv = mIOThread->Dispatch(ev, CacheIOThread::EVICT);
  1.2936 +  NS_ENSURE_SUCCESS(rv, rv);
  1.2937 +
  1.2938 +  mRemovingTrashDirs = true;
  1.2939 +  return NS_OK;
  1.2940 +}
  1.2941 +
  1.2942 +nsresult
  1.2943 +CacheFileIOManager::RemoveTrashInternal()
  1.2944 +{
  1.2945 +  LOG(("CacheFileIOManager::RemoveTrashInternal()"));
  1.2946 +
  1.2947 +  nsresult rv;
  1.2948 +
  1.2949 +  MOZ_ASSERT(mIOThread->IsCurrentThread());
  1.2950 +
  1.2951 +  if (mShuttingDown) {
  1.2952 +    return NS_ERROR_NOT_INITIALIZED;
  1.2953 +  }
  1.2954 +
  1.2955 +  MOZ_ASSERT(!mTrashTimer);
  1.2956 +  MOZ_ASSERT(mRemovingTrashDirs);
  1.2957 +
  1.2958 +  if (!mTreeCreated) {
  1.2959 +    rv = CreateCacheTree();
  1.2960 +    if (NS_FAILED(rv)) {
  1.2961 +      return rv;
  1.2962 +    }
  1.2963 +  }
  1.2964 +
  1.2965 +  // mRemovingTrashDirs is accessed only on IO thread, so we can drop the flag
  1.2966 +  // here and set it again once we dispatch a continuation event. By doing so,
  1.2967 +  // we don't have to drop the flag on any possible early return.
  1.2968 +  mRemovingTrashDirs = false;
  1.2969 +
  1.2970 +  while (true) {
  1.2971 +    if (CacheIOThread::YieldAndRerun()) {
  1.2972 +      LOG(("CacheFileIOManager::RemoveTrashInternal() - Breaking loop for "
  1.2973 +           "higher level events."));
  1.2974 +      mRemovingTrashDirs = true;
  1.2975 +      return NS_OK;
  1.2976 +    }
  1.2977 +
  1.2978 +    // Find some trash directory
  1.2979 +    if (!mTrashDir) {
  1.2980 +      MOZ_ASSERT(!mTrashDirEnumerator);
  1.2981 +
  1.2982 +      rv = FindTrashDirToRemove();
  1.2983 +      if (rv == NS_ERROR_NOT_AVAILABLE) {
  1.2984 +        LOG(("CacheFileIOManager::RemoveTrashInternal() - No trash directory "
  1.2985 +             "found."));
  1.2986 +        return NS_OK;
  1.2987 +      }
  1.2988 +      NS_ENSURE_SUCCESS(rv, rv);
  1.2989 +
  1.2990 +      nsCOMPtr<nsISimpleEnumerator> enumerator;
  1.2991 +      rv = mTrashDir->GetDirectoryEntries(getter_AddRefs(enumerator));
  1.2992 +      if (NS_SUCCEEDED(rv)) {
  1.2993 +        mTrashDirEnumerator = do_QueryInterface(enumerator, &rv);
  1.2994 +        NS_ENSURE_SUCCESS(rv, rv);
  1.2995 +      }
  1.2996 +
  1.2997 +      continue; // check elapsed time
  1.2998 +    }
  1.2999 +
  1.3000 +    // We null out mTrashDirEnumerator once we remove all files in the
  1.3001 +    // directory, so remove the trash directory if we don't have enumerator.
  1.3002 +    if (!mTrashDirEnumerator) {
  1.3003 +      rv = mTrashDir->Remove(false);
  1.3004 +      if (NS_FAILED(rv)) {
  1.3005 +        // There is no reason why removing an empty directory should fail, but
  1.3006 +        // if it does, we should continue and try to remove all other trash
  1.3007 +        // directories.
  1.3008 +        nsAutoCString leafName;
  1.3009 +        mTrashDir->GetNativeLeafName(leafName);
  1.3010 +        mFailedTrashDirs.AppendElement(leafName);
  1.3011 +        LOG(("CacheFileIOManager::RemoveTrashInternal() - Cannot remove "
  1.3012 +             "trashdir. [name=%s]", leafName.get()));
  1.3013 +      }
  1.3014 +
  1.3015 +      mTrashDir = nullptr;
  1.3016 +      continue; // check elapsed time
  1.3017 +    }
  1.3018 +
  1.3019 +    nsCOMPtr<nsIFile> file;
  1.3020 +    rv = mTrashDirEnumerator->GetNextFile(getter_AddRefs(file));
  1.3021 +    if (!file) {
  1.3022 +      mTrashDirEnumerator->Close();
  1.3023 +      mTrashDirEnumerator = nullptr;
  1.3024 +      continue; // check elapsed time
  1.3025 +    } else {
  1.3026 +      bool isDir = false;
  1.3027 +      file->IsDirectory(&isDir);
  1.3028 +      if (isDir) {
  1.3029 +        NS_WARNING("Found a directory in a trash directory! It will be removed "
  1.3030 +                   "recursively, but this can block IO thread for a while!");
  1.3031 +#ifdef PR_LOGGING
  1.3032 +        nsAutoCString path;
  1.3033 +        file->GetNativePath(path);
  1.3034 +#endif
  1.3035 +        LOG(("CacheFileIOManager::RemoveTrashInternal() - Found a directory in a trash "
  1.3036 +            "directory! It will be removed recursively, but this can block IO "
  1.3037 +            "thread for a while! [file=%s]", path.get()));
  1.3038 +      }
  1.3039 +      file->Remove(isDir);
  1.3040 +    }
  1.3041 +  }
  1.3042 +
  1.3043 +  NS_NOTREACHED("We should never get here");
  1.3044 +  return NS_OK;
  1.3045 +}
  1.3046 +
  1.3047 +nsresult
  1.3048 +CacheFileIOManager::FindTrashDirToRemove()
  1.3049 +{
  1.3050 +  LOG(("CacheFileIOManager::FindTrashDirToRemove()"));
  1.3051 +
  1.3052 +  nsresult rv;
  1.3053 +
  1.3054 +  // We call this method on the main thread during shutdown when user wants to
  1.3055 +  // remove all cache files.
  1.3056 +  MOZ_ASSERT(mIOThread->IsCurrentThread() || mShuttingDown);
  1.3057 +
  1.3058 +  nsCOMPtr<nsISimpleEnumerator> iter;
  1.3059 +  rv = mCacheDirectory->GetDirectoryEntries(getter_AddRefs(iter));
  1.3060 +  NS_ENSURE_SUCCESS(rv, rv);
  1.3061 +
  1.3062 +  bool more;
  1.3063 +  nsCOMPtr<nsISupports> elem;
  1.3064 +
  1.3065 +  while (NS_SUCCEEDED(iter->HasMoreElements(&more)) && more) {
  1.3066 +    rv = iter->GetNext(getter_AddRefs(elem));
  1.3067 +    if (NS_FAILED(rv)) {
  1.3068 +      continue;
  1.3069 +    }
  1.3070 +
  1.3071 +    nsCOMPtr<nsIFile> file = do_QueryInterface(elem);
  1.3072 +    if (!file) {
  1.3073 +      continue;
  1.3074 +    }
  1.3075 +
  1.3076 +    bool isDir = false;
  1.3077 +    file->IsDirectory(&isDir);
  1.3078 +    if (!isDir) {
  1.3079 +      continue;
  1.3080 +    }
  1.3081 +
  1.3082 +    nsAutoCString leafName;
  1.3083 +    rv = file->GetNativeLeafName(leafName);
  1.3084 +    if (NS_FAILED(rv)) {
  1.3085 +      continue;
  1.3086 +    }
  1.3087 +
  1.3088 +    if (leafName.Length() < strlen(kTrashDir)) {
  1.3089 +      continue;
  1.3090 +    }
  1.3091 +
  1.3092 +    if (!StringBeginsWith(leafName, NS_LITERAL_CSTRING(kTrashDir))) {
  1.3093 +      continue;
  1.3094 +    }
  1.3095 +
  1.3096 +    if (mFailedTrashDirs.Contains(leafName)) {
  1.3097 +      continue;
  1.3098 +    }
  1.3099 +
  1.3100 +    LOG(("CacheFileIOManager::FindTrashDirToRemove() - Returning directory %s",
  1.3101 +         leafName.get()));
  1.3102 +
  1.3103 +    mTrashDir = file;
  1.3104 +    return NS_OK;
  1.3105 +  }
  1.3106 +
  1.3107 +  // When we're here we've tried to delete all trash directories. Clear
  1.3108 +  // mFailedTrashDirs so we will try to delete them again when we start removing
  1.3109 +  // trash directories next time.
  1.3110 +  mFailedTrashDirs.Clear();
  1.3111 +  return NS_ERROR_NOT_AVAILABLE;
  1.3112 +}
  1.3113 +
  1.3114 +// static
  1.3115 +nsresult
  1.3116 +CacheFileIOManager::InitIndexEntry(CacheFileHandle *aHandle,
  1.3117 +                                   uint32_t         aAppId,
  1.3118 +                                   bool             aAnonymous,
  1.3119 +                                   bool             aInBrowser)
  1.3120 +{
  1.3121 +  LOG(("CacheFileIOManager::InitIndexEntry() [handle=%p, appId=%u, anonymous=%d"
  1.3122 +       ", inBrowser=%d]", aHandle, aAppId, aAnonymous, aInBrowser));
  1.3123 +
  1.3124 +  nsresult rv;
  1.3125 +  nsRefPtr<CacheFileIOManager> ioMan = gInstance;
  1.3126 +
  1.3127 +  if (aHandle->IsClosed() || !ioMan) {
  1.3128 +    return NS_ERROR_NOT_INITIALIZED;
  1.3129 +  }
  1.3130 +
  1.3131 +  if (aHandle->IsSpecialFile()) {
  1.3132 +    return NS_ERROR_UNEXPECTED;
  1.3133 +  }
  1.3134 +
  1.3135 +  nsRefPtr<InitIndexEntryEvent> ev =
  1.3136 +    new InitIndexEntryEvent(aHandle, aAppId, aAnonymous, aInBrowser);
  1.3137 +  rv = ioMan->mIOThread->Dispatch(ev, CacheIOThread::WRITE);
  1.3138 +  NS_ENSURE_SUCCESS(rv, rv);
  1.3139 +
  1.3140 +  return NS_OK;
  1.3141 +}
  1.3142 +
  1.3143 +// static
  1.3144 +nsresult
  1.3145 +CacheFileIOManager::UpdateIndexEntry(CacheFileHandle *aHandle,
  1.3146 +                                     const uint32_t  *aFrecency,
  1.3147 +                                     const uint32_t  *aExpirationTime)
  1.3148 +{
  1.3149 +  LOG(("CacheFileIOManager::UpdateIndexEntry() [handle=%p, frecency=%s, "
  1.3150 +       "expirationTime=%s]", aHandle,
  1.3151 +       aFrecency ? nsPrintfCString("%u", *aFrecency).get() : "",
  1.3152 +       aExpirationTime ? nsPrintfCString("%u", *aExpirationTime).get() : ""));
  1.3153 +
  1.3154 +  nsresult rv;
  1.3155 +  nsRefPtr<CacheFileIOManager> ioMan = gInstance;
  1.3156 +
  1.3157 +  if (aHandle->IsClosed() || !ioMan) {
  1.3158 +    return NS_ERROR_NOT_INITIALIZED;
  1.3159 +  }
  1.3160 +
  1.3161 +  if (aHandle->IsSpecialFile()) {
  1.3162 +    return NS_ERROR_UNEXPECTED;
  1.3163 +  }
  1.3164 +
  1.3165 +  nsRefPtr<UpdateIndexEntryEvent> ev =
  1.3166 +    new UpdateIndexEntryEvent(aHandle, aFrecency, aExpirationTime);
  1.3167 +  rv = ioMan->mIOThread->Dispatch(ev, CacheIOThread::WRITE);
  1.3168 +  NS_ENSURE_SUCCESS(rv, rv);
  1.3169 +
  1.3170 +  return NS_OK;
  1.3171 +}
  1.3172 +
  1.3173 +nsresult
  1.3174 +CacheFileIOManager::CreateFile(CacheFileHandle *aHandle)
  1.3175 +{
  1.3176 +  MOZ_ASSERT(!aHandle->mFD);
  1.3177 +  MOZ_ASSERT(aHandle->mFile);
  1.3178 +
  1.3179 +  nsresult rv;
  1.3180 +
  1.3181 +  if (aHandle->IsDoomed()) {
  1.3182 +    nsCOMPtr<nsIFile> file;
  1.3183 +
  1.3184 +    rv = GetDoomedFile(getter_AddRefs(file));
  1.3185 +    NS_ENSURE_SUCCESS(rv, rv);
  1.3186 +
  1.3187 +    aHandle->mFile.swap(file);
  1.3188 +  } else {
  1.3189 +    bool exists;
  1.3190 +    if (NS_SUCCEEDED(aHandle->mFile->Exists(&exists)) && exists) {
  1.3191 +      NS_WARNING("Found a file that should not exist!");
  1.3192 +    }
  1.3193 +  }
  1.3194 +
  1.3195 +  rv = OpenNSPRHandle(aHandle, true);
  1.3196 +  NS_ENSURE_SUCCESS(rv, rv);
  1.3197 +
  1.3198 +  aHandle->mFileSize = 0;
  1.3199 +  return NS_OK;
  1.3200 +}
  1.3201 +
  1.3202 +// static
  1.3203 +void
  1.3204 +CacheFileIOManager::HashToStr(const SHA1Sum::Hash *aHash, nsACString &_retval)
  1.3205 +{
  1.3206 +  _retval.Assign("");
  1.3207 +  const char hexChars[] = {'0', '1', '2', '3', '4', '5', '6', '7',
  1.3208 +                           '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
  1.3209 +  for (uint32_t i=0 ; i<sizeof(SHA1Sum::Hash) ; i++) {
  1.3210 +    _retval.Append(hexChars[(*aHash)[i] >> 4]);
  1.3211 +    _retval.Append(hexChars[(*aHash)[i] & 0xF]);
  1.3212 +  }
  1.3213 +}
  1.3214 +
  1.3215 +// static
  1.3216 +nsresult
  1.3217 +CacheFileIOManager::StrToHash(const nsACString &aHash, SHA1Sum::Hash *_retval)
  1.3218 +{
  1.3219 +  if (aHash.Length() != 2*sizeof(SHA1Sum::Hash)) {
  1.3220 +    return NS_ERROR_INVALID_ARG;
  1.3221 +  }
  1.3222 +
  1.3223 +  for (uint32_t i=0 ; i<aHash.Length() ; i++) {
  1.3224 +    uint8_t value;
  1.3225 +
  1.3226 +    if (aHash[i] >= '0' && aHash[i] <= '9') {
  1.3227 +      value = aHash[i] - '0';
  1.3228 +    } else if (aHash[i] >= 'A' && aHash[i] <= 'F') {
  1.3229 +      value = aHash[i] - 'A' + 10;
  1.3230 +    } else if (aHash[i] >= 'a' && aHash[i] <= 'f') {
  1.3231 +      value = aHash[i] - 'a' + 10;
  1.3232 +    } else {
  1.3233 +      return NS_ERROR_INVALID_ARG;
  1.3234 +    }
  1.3235 +
  1.3236 +    if (i%2 == 0) {
  1.3237 +      (reinterpret_cast<uint8_t *>(_retval))[i/2] = value << 4;
  1.3238 +    } else {
  1.3239 +      (reinterpret_cast<uint8_t *>(_retval))[i/2] += value;
  1.3240 +    }
  1.3241 +  }
  1.3242 +
  1.3243 +  return NS_OK;
  1.3244 +}
  1.3245 +
  1.3246 +nsresult
  1.3247 +CacheFileIOManager::GetFile(const SHA1Sum::Hash *aHash, nsIFile **_retval)
  1.3248 +{
  1.3249 +  nsresult rv;
  1.3250 +  nsCOMPtr<nsIFile> file;
  1.3251 +  rv = mCacheDirectory->Clone(getter_AddRefs(file));
  1.3252 +  NS_ENSURE_SUCCESS(rv, rv);
  1.3253 +
  1.3254 +  rv = file->AppendNative(NS_LITERAL_CSTRING(kEntriesDir));
  1.3255 +  NS_ENSURE_SUCCESS(rv, rv);
  1.3256 +
  1.3257 +  nsAutoCString leafName;
  1.3258 +  HashToStr(aHash, leafName);
  1.3259 +
  1.3260 +  rv = file->AppendNative(leafName);
  1.3261 +  NS_ENSURE_SUCCESS(rv, rv);
  1.3262 +
  1.3263 +  file.swap(*_retval);
  1.3264 +  return NS_OK;
  1.3265 +}
  1.3266 +
  1.3267 +nsresult
  1.3268 +CacheFileIOManager::GetSpecialFile(const nsACString &aKey, nsIFile **_retval)
  1.3269 +{
  1.3270 +  nsresult rv;
  1.3271 +  nsCOMPtr<nsIFile> file;
  1.3272 +  rv = mCacheDirectory->Clone(getter_AddRefs(file));
  1.3273 +  NS_ENSURE_SUCCESS(rv, rv);
  1.3274 +
  1.3275 +  rv = file->AppendNative(aKey);
  1.3276 +  NS_ENSURE_SUCCESS(rv, rv);
  1.3277 +
  1.3278 +  file.swap(*_retval);
  1.3279 +  return NS_OK;
  1.3280 +}
  1.3281 +
  1.3282 +nsresult
  1.3283 +CacheFileIOManager::GetDoomedFile(nsIFile **_retval)
  1.3284 +{
  1.3285 +  nsresult rv;
  1.3286 +  nsCOMPtr<nsIFile> file;
  1.3287 +  rv = mCacheDirectory->Clone(getter_AddRefs(file));
  1.3288 +  NS_ENSURE_SUCCESS(rv, rv);
  1.3289 +
  1.3290 +  rv = file->AppendNative(NS_LITERAL_CSTRING(kDoomedDir));
  1.3291 +  NS_ENSURE_SUCCESS(rv, rv);
  1.3292 +
  1.3293 +  rv = file->AppendNative(NS_LITERAL_CSTRING("dummyleaf"));
  1.3294 +  NS_ENSURE_SUCCESS(rv, rv);
  1.3295 +
  1.3296 +  srand(static_cast<unsigned>(PR_Now()));
  1.3297 +  nsAutoCString leafName;
  1.3298 +  uint32_t iter=0;
  1.3299 +  while (true) {
  1.3300 +    iter++;
  1.3301 +    leafName.AppendInt(rand());
  1.3302 +    rv = file->SetNativeLeafName(leafName);
  1.3303 +    NS_ENSURE_SUCCESS(rv, rv);
  1.3304 +
  1.3305 +    bool exists;
  1.3306 +    if (NS_SUCCEEDED(file->Exists(&exists)) && !exists) {
  1.3307 +      break;
  1.3308 +    }
  1.3309 +
  1.3310 +    leafName.Truncate();
  1.3311 +  }
  1.3312 +
  1.3313 +//  Telemetry::Accumulate(Telemetry::DISK_CACHE_GETDOOMEDFILE_ITERATIONS, iter);
  1.3314 +
  1.3315 +  file.swap(*_retval);
  1.3316 +  return NS_OK;
  1.3317 +}
  1.3318 +
  1.3319 +nsresult
  1.3320 +CacheFileIOManager::IsEmptyDirectory(nsIFile *aFile, bool *_retval)
  1.3321 +{
  1.3322 +  MOZ_ASSERT(mIOThread->IsCurrentThread());
  1.3323 +
  1.3324 +  nsresult rv;
  1.3325 +
  1.3326 +  nsCOMPtr<nsISimpleEnumerator> enumerator;
  1.3327 +  rv = aFile->GetDirectoryEntries(getter_AddRefs(enumerator));
  1.3328 +  NS_ENSURE_SUCCESS(rv, rv);
  1.3329 +
  1.3330 +  bool hasMoreElements = false;
  1.3331 +  rv = enumerator->HasMoreElements(&hasMoreElements);
  1.3332 +  NS_ENSURE_SUCCESS(rv, rv);
  1.3333 +
  1.3334 +  *_retval = !hasMoreElements;
  1.3335 +  return NS_OK;
  1.3336 +}
  1.3337 +
  1.3338 +nsresult
  1.3339 +CacheFileIOManager::CheckAndCreateDir(nsIFile *aFile, const char *aDir,
  1.3340 +                                      bool aEnsureEmptyDir)
  1.3341 +{
  1.3342 +  nsresult rv;
  1.3343 +
  1.3344 +  nsCOMPtr<nsIFile> file;
  1.3345 +  if (!aDir) {
  1.3346 +    file = aFile;
  1.3347 +  } else {
  1.3348 +    nsAutoCString dir(aDir);
  1.3349 +    rv = aFile->Clone(getter_AddRefs(file));
  1.3350 +    NS_ENSURE_SUCCESS(rv, rv);
  1.3351 +    rv = file->AppendNative(dir);
  1.3352 +    NS_ENSURE_SUCCESS(rv, rv);
  1.3353 +  }
  1.3354 +
  1.3355 +  bool exists = false;
  1.3356 +  rv = file->Exists(&exists);
  1.3357 +  if (NS_SUCCEEDED(rv) && exists) {
  1.3358 +    bool isDirectory = false;
  1.3359 +    rv = file->IsDirectory(&isDirectory);
  1.3360 +    if (NS_FAILED(rv) || !isDirectory) {
  1.3361 +      // Try to remove the file
  1.3362 +      rv = file->Remove(false);
  1.3363 +      if (NS_SUCCEEDED(rv)) {
  1.3364 +        exists = false;
  1.3365 +      }
  1.3366 +    }
  1.3367 +    NS_ENSURE_SUCCESS(rv, rv);
  1.3368 +  }
  1.3369 +
  1.3370 +  if (aEnsureEmptyDir && NS_SUCCEEDED(rv) && exists) {
  1.3371 +    bool isEmpty;
  1.3372 +    rv = IsEmptyDirectory(file, &isEmpty);
  1.3373 +    NS_ENSURE_SUCCESS(rv, rv);
  1.3374 +
  1.3375 +    if (!isEmpty) {
  1.3376 +      rv = TrashDirectory(file);
  1.3377 +      NS_ENSURE_SUCCESS(rv, rv);
  1.3378 +
  1.3379 +      exists = false;
  1.3380 +    }
  1.3381 +  }
  1.3382 +
  1.3383 +  if (NS_SUCCEEDED(rv) && !exists) {
  1.3384 +    rv = file->Create(nsIFile::DIRECTORY_TYPE, 0700);
  1.3385 +  }
  1.3386 +  if (NS_FAILED(rv)) {
  1.3387 +    NS_WARNING("Cannot create directory");
  1.3388 +    return NS_ERROR_FAILURE;
  1.3389 +  }
  1.3390 +
  1.3391 +  return NS_OK;
  1.3392 +}
  1.3393 +
  1.3394 +nsresult
  1.3395 +CacheFileIOManager::CreateCacheTree()
  1.3396 +{
  1.3397 +  MOZ_ASSERT(mIOThread->IsCurrentThread());
  1.3398 +  MOZ_ASSERT(!mTreeCreated);
  1.3399 +
  1.3400 +  if (!mCacheDirectory) {
  1.3401 +    return NS_ERROR_FILE_INVALID_PATH;
  1.3402 +  }
  1.3403 +
  1.3404 +  nsresult rv;
  1.3405 +
  1.3406 +  // ensure parent directory exists
  1.3407 +  nsCOMPtr<nsIFile> parentDir;
  1.3408 +  rv = mCacheDirectory->GetParent(getter_AddRefs(parentDir));
  1.3409 +  NS_ENSURE_SUCCESS(rv, rv);
  1.3410 +  rv = CheckAndCreateDir(parentDir, nullptr, false);
  1.3411 +  NS_ENSURE_SUCCESS(rv, rv);
  1.3412 +
  1.3413 +  // ensure cache directory exists
  1.3414 +  rv = CheckAndCreateDir(mCacheDirectory, nullptr, false);
  1.3415 +  NS_ENSURE_SUCCESS(rv, rv);
  1.3416 +
  1.3417 +  // ensure entries directory exists
  1.3418 +  rv = CheckAndCreateDir(mCacheDirectory, kEntriesDir, false);
  1.3419 +  NS_ENSURE_SUCCESS(rv, rv);
  1.3420 +
  1.3421 +  // ensure doomed directory exists
  1.3422 +  rv = CheckAndCreateDir(mCacheDirectory, kDoomedDir, true);
  1.3423 +  NS_ENSURE_SUCCESS(rv, rv);
  1.3424 +
  1.3425 +  mTreeCreated = true;
  1.3426 +
  1.3427 +  if (!mContextEvictor) {
  1.3428 +    nsRefPtr<CacheFileContextEvictor> contextEvictor;
  1.3429 +    contextEvictor = new CacheFileContextEvictor();
  1.3430 +
  1.3431 +    // Init() method will try to load unfinished contexts from the disk. Store
  1.3432 +    // the evictor as a member only when there is some unfinished job.
  1.3433 +    contextEvictor->Init(mCacheDirectory);
  1.3434 +    if (contextEvictor->ContextsCount()) {
  1.3435 +      contextEvictor.swap(mContextEvictor);
  1.3436 +    }
  1.3437 +  }
  1.3438 +
  1.3439 +  StartRemovingTrash();
  1.3440 +
  1.3441 +  return NS_OK;
  1.3442 +}
  1.3443 +
  1.3444 +nsresult
  1.3445 +CacheFileIOManager::OpenNSPRHandle(CacheFileHandle *aHandle, bool aCreate)
  1.3446 +{
  1.3447 +  MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
  1.3448 +  MOZ_ASSERT(!aHandle->mFD);
  1.3449 +  MOZ_ASSERT(mHandlesByLastUsed.IndexOf(aHandle) == mHandlesByLastUsed.NoIndex);
  1.3450 +  MOZ_ASSERT(mHandlesByLastUsed.Length() <= kOpenHandlesLimit);
  1.3451 +  MOZ_ASSERT((aCreate && !aHandle->mFileExists) ||
  1.3452 +             (!aCreate && aHandle->mFileExists));
  1.3453 +
  1.3454 +  nsresult rv;
  1.3455 +
  1.3456 +  if (mHandlesByLastUsed.Length() == kOpenHandlesLimit) {
  1.3457 +    // close handle that hasn't been used for the longest time
  1.3458 +    rv = ReleaseNSPRHandleInternal(mHandlesByLastUsed[0]);
  1.3459 +    NS_ENSURE_SUCCESS(rv, rv);
  1.3460 +  }
  1.3461 +
  1.3462 +  if (aCreate) {
  1.3463 +    rv = aHandle->mFile->OpenNSPRFileDesc(
  1.3464 +           PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE, 0600, &aHandle->mFD);
  1.3465 +    NS_ENSURE_SUCCESS(rv, rv);
  1.3466 +
  1.3467 +    aHandle->mFileExists = true;
  1.3468 +  } else {
  1.3469 +    rv = aHandle->mFile->OpenNSPRFileDesc(PR_RDWR, 0600, &aHandle->mFD);
  1.3470 +    if (NS_ERROR_FILE_NOT_FOUND == rv) {
  1.3471 +      LOG(("  file doesn't exists"));
  1.3472 +      aHandle->mFileExists = false;
  1.3473 +      return DoomFileInternal(aHandle);
  1.3474 +    }
  1.3475 +    NS_ENSURE_SUCCESS(rv, rv);
  1.3476 +  }
  1.3477 +
  1.3478 +  mHandlesByLastUsed.AppendElement(aHandle);
  1.3479 +  return NS_OK;
  1.3480 +}
  1.3481 +
  1.3482 +void
  1.3483 +CacheFileIOManager::NSPRHandleUsed(CacheFileHandle *aHandle)
  1.3484 +{
  1.3485 +  MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
  1.3486 +  MOZ_ASSERT(aHandle->mFD);
  1.3487 +
  1.3488 +  DebugOnly<bool> found;
  1.3489 +  found = mHandlesByLastUsed.RemoveElement(aHandle);
  1.3490 +  MOZ_ASSERT(found);
  1.3491 +
  1.3492 +  mHandlesByLastUsed.AppendElement(aHandle);
  1.3493 +}
  1.3494 +
  1.3495 +nsresult
  1.3496 +CacheFileIOManager::SyncRemoveDir(nsIFile *aFile, const char *aDir)
  1.3497 +{
  1.3498 +  nsresult rv;
  1.3499 +  nsCOMPtr<nsIFile> file;
  1.3500 +
  1.3501 +  if (!aDir) {
  1.3502 +    file = aFile;
  1.3503 +  } else {
  1.3504 +    rv = aFile->Clone(getter_AddRefs(file));
  1.3505 +    if (NS_WARN_IF(NS_FAILED(rv))) {
  1.3506 +      return rv;
  1.3507 +    }
  1.3508 +
  1.3509 +    rv = file->AppendNative(nsDependentCString(aDir));
  1.3510 +    if (NS_WARN_IF(NS_FAILED(rv))) {
  1.3511 +      return rv;
  1.3512 +    }
  1.3513 +  }
  1.3514 +
  1.3515 +#ifdef PR_LOGGING
  1.3516 +  nsAutoCString path;
  1.3517 +  file->GetNativePath(path);
  1.3518 +#endif
  1.3519 +
  1.3520 +  LOG(("CacheFileIOManager::SyncRemoveDir() - Removing directory %s",
  1.3521 +       path.get()));
  1.3522 +
  1.3523 +  rv = file->Remove(true);
  1.3524 +  if (NS_WARN_IF(NS_FAILED(rv))) {
  1.3525 +    LOG(("CacheFileIOManager::SyncRemoveDir() - Removing failed! [rv=0x%08x]",
  1.3526 +         rv));
  1.3527 +  }
  1.3528 +
  1.3529 +  return rv;
  1.3530 +}
  1.3531 +
  1.3532 +void
  1.3533 +CacheFileIOManager::SyncRemoveAllCacheFiles()
  1.3534 +{
  1.3535 +  LOG(("CacheFileIOManager::SyncRemoveAllCacheFiles()"));
  1.3536 +
  1.3537 +  nsresult rv;
  1.3538 +
  1.3539 +  SyncRemoveDir(mCacheDirectory, kEntriesDir);
  1.3540 +  SyncRemoveDir(mCacheDirectory, kDoomedDir);
  1.3541 +
  1.3542 +  // Clear any intermediate state of trash dir enumeration.
  1.3543 +  mFailedTrashDirs.Clear();
  1.3544 +  mTrashDir = nullptr;
  1.3545 +
  1.3546 +  while (true) {
  1.3547 +    // FindTrashDirToRemove() fills mTrashDir if there is any trash directory.
  1.3548 +    rv = FindTrashDirToRemove();
  1.3549 +    if (rv == NS_ERROR_NOT_AVAILABLE) {
  1.3550 +      LOG(("CacheFileIOManager::SyncRemoveAllCacheFiles() - No trash directory "
  1.3551 +           "found."));
  1.3552 +      break;
  1.3553 +    }
  1.3554 +    if (NS_WARN_IF(NS_FAILED(rv))) {
  1.3555 +      LOG(("CacheFileIOManager::SyncRemoveAllCacheFiles() - "
  1.3556 +           "FindTrashDirToRemove() returned an unexpected error. [rv=0x%08x]",
  1.3557 +           rv));
  1.3558 +      break;
  1.3559 +    }
  1.3560 +
  1.3561 +    rv = SyncRemoveDir(mTrashDir, nullptr);
  1.3562 +    if (NS_FAILED(rv)) {
  1.3563 +      nsAutoCString leafName;
  1.3564 +      mTrashDir->GetNativeLeafName(leafName);
  1.3565 +      mFailedTrashDirs.AppendElement(leafName);
  1.3566 +    }
  1.3567 +  }
  1.3568 +}
  1.3569 +
  1.3570 +// Returns default ("smart") size (in KB) of cache, given available disk space
  1.3571 +// (also in KB)
  1.3572 +static uint32_t
  1.3573 +SmartCacheSize(const uint32_t availKB)
  1.3574 +{
  1.3575 +  uint32_t maxSize = kMaxCacheSizeKB;
  1.3576 +
  1.3577 +  if (availKB > 100 * 1024 * 1024) {
  1.3578 +    return maxSize;  // skip computing if we're over 100 GB
  1.3579 +  }
  1.3580 +
  1.3581 +  // Grow/shrink in 10 MB units, deliberately, so that in the common case we
  1.3582 +  // don't shrink cache and evict items every time we startup (it's important
  1.3583 +  // that we don't slow down startup benchmarks).
  1.3584 +  uint32_t sz10MBs = 0;
  1.3585 +  uint32_t avail10MBs = availKB / (1024*10);
  1.3586 +
  1.3587 +  // .5% of space above 25 GB
  1.3588 +  if (avail10MBs > 2500) {
  1.3589 +    sz10MBs += static_cast<uint32_t>((avail10MBs - 2500)*.005);
  1.3590 +    avail10MBs = 2500;
  1.3591 +  }
  1.3592 +  // 1% of space between 7GB -> 25 GB
  1.3593 +  if (avail10MBs > 700) {
  1.3594 +    sz10MBs += static_cast<uint32_t>((avail10MBs - 700)*.01);
  1.3595 +    avail10MBs = 700;
  1.3596 +  }
  1.3597 +  // 5% of space between 500 MB -> 7 GB
  1.3598 +  if (avail10MBs > 50) {
  1.3599 +    sz10MBs += static_cast<uint32_t>((avail10MBs - 50)*.05);
  1.3600 +    avail10MBs = 50;
  1.3601 +  }
  1.3602 +
  1.3603 +#ifdef ANDROID
  1.3604 +  // On Android, smaller/older devices may have very little storage and
  1.3605 +  // device owners may be sensitive to storage footprint: Use a smaller
  1.3606 +  // percentage of available space and a smaller minimum.
  1.3607 +
  1.3608 +  // 20% of space up to 500 MB (10 MB min)
  1.3609 +  sz10MBs += std::max<uint32_t>(1, static_cast<uint32_t>(avail10MBs * .2));
  1.3610 +#else
  1.3611 +  // 40% of space up to 500 MB (50 MB min)
  1.3612 +  sz10MBs += std::max<uint32_t>(5, static_cast<uint32_t>(avail10MBs * .4));
  1.3613 +#endif
  1.3614 +
  1.3615 +  return std::min<uint32_t>(maxSize, sz10MBs * 10 * 1024);
  1.3616 +}
  1.3617 +
  1.3618 +nsresult
  1.3619 +CacheFileIOManager::UpdateSmartCacheSize()
  1.3620 +{
  1.3621 +  MOZ_ASSERT(mIOThread->IsCurrentThread());
  1.3622 +
  1.3623 +  nsresult rv;
  1.3624 +
  1.3625 +  if (!CacheObserver::UseNewCache()) {
  1.3626 +    return NS_ERROR_NOT_AVAILABLE;
  1.3627 +  }
  1.3628 +
  1.3629 +  if (!CacheObserver::SmartCacheSizeEnabled()) {
  1.3630 +    return NS_ERROR_NOT_AVAILABLE;
  1.3631 +  }
  1.3632 +
  1.3633 +  // Wait at least kSmartSizeUpdateInterval before recomputing smart size.
  1.3634 +  static const TimeDuration kUpdateLimit =
  1.3635 +    TimeDuration::FromMilliseconds(kSmartSizeUpdateInterval);
  1.3636 +  if (!mLastSmartSizeTime.IsNull() &&
  1.3637 +      (TimeStamp::NowLoRes() - mLastSmartSizeTime) < kUpdateLimit) {
  1.3638 +    return NS_OK;
  1.3639 +  }
  1.3640 +
  1.3641 +  // Do not compute smart size when cache size is not reliable.
  1.3642 +  bool isUpToDate = false;
  1.3643 +  CacheIndex::IsUpToDate(&isUpToDate);
  1.3644 +  if (!isUpToDate) {
  1.3645 +    return NS_ERROR_NOT_AVAILABLE;
  1.3646 +  }
  1.3647 +
  1.3648 +  uint32_t cacheUsage;
  1.3649 +  rv = CacheIndex::GetCacheSize(&cacheUsage);
  1.3650 +  if (NS_WARN_IF(NS_FAILED(rv))) {
  1.3651 +    LOG(("CacheFileIOManager::UpdateSmartCacheSize() - Cannot get cacheUsage! "
  1.3652 +         "[rv=0x%08x]", rv));
  1.3653 +    return rv;
  1.3654 +  }
  1.3655 +
  1.3656 +  int64_t avail;
  1.3657 +  rv = mCacheDirectory->GetDiskSpaceAvailable(&avail);
  1.3658 +  if (NS_WARN_IF(NS_FAILED(rv))) {
  1.3659 +    // Do not change smart size.
  1.3660 +    LOG(("CacheFileIOManager::UpdateSmartCacheSize() - GetDiskSpaceAvailable() "
  1.3661 +         "failed! [rv=0x%08x]", rv));
  1.3662 +    return rv;
  1.3663 +  }
  1.3664 +
  1.3665 +  mLastSmartSizeTime = TimeStamp::NowLoRes();
  1.3666 +
  1.3667 +  uint32_t smartSize = SmartCacheSize(static_cast<uint32_t>(avail / 1024) +
  1.3668 +                                      cacheUsage);
  1.3669 +
  1.3670 +  if (smartSize == (CacheObserver::DiskCacheCapacity() >> 10)) {
  1.3671 +    // Smart size has not changed.
  1.3672 +    return NS_OK;
  1.3673 +  }
  1.3674 +
  1.3675 +  CacheObserver::SetDiskCacheCapacity(smartSize << 10);
  1.3676 +
  1.3677 +  return NS_OK;
  1.3678 +}
  1.3679 +
  1.3680 +// Memory reporting
  1.3681 +
  1.3682 +namespace { // anon
  1.3683 +
  1.3684 +// A helper class that dispatches and waits for an event that gets result of
  1.3685 +// CacheFileIOManager->mHandles.SizeOfExcludingThis() on the I/O thread
  1.3686 +// to safely get handles memory report.
  1.3687 +// We must do this, since the handle list is only accessed and managed w/o
  1.3688 +// locking on the I/O thread.  That is by design.
  1.3689 +class SizeOfHandlesRunnable : public nsRunnable
  1.3690 +{
  1.3691 +public:
  1.3692 +  SizeOfHandlesRunnable(mozilla::MallocSizeOf mallocSizeOf,
  1.3693 +                        CacheFileHandles const &handles,
  1.3694 +                        nsTArray<CacheFileHandle *> const &specialHandles)
  1.3695 +    : mMonitor("SizeOfHandlesRunnable.mMonitor")
  1.3696 +    , mMallocSizeOf(mallocSizeOf)
  1.3697 +    , mHandles(handles)
  1.3698 +    , mSpecialHandles(specialHandles)
  1.3699 +  {
  1.3700 +  }
  1.3701 +
  1.3702 +  size_t Get(CacheIOThread* thread)
  1.3703 +  {
  1.3704 +    nsCOMPtr<nsIEventTarget> target = thread->Target();
  1.3705 +    if (!target) {
  1.3706 +      NS_ERROR("If we have the I/O thread we also must have the I/O target");
  1.3707 +      return 0;
  1.3708 +    }
  1.3709 +
  1.3710 +    mozilla::MonitorAutoLock mon(mMonitor);
  1.3711 +    nsresult rv = target->Dispatch(this, nsIEventTarget::DISPATCH_NORMAL);
  1.3712 +    if (NS_FAILED(rv)) {
  1.3713 +      NS_ERROR("Dispatch failed, cannot do memory report of CacheFileHandles");
  1.3714 +      return 0;
  1.3715 +    }
  1.3716 +
  1.3717 +    mon.Wait();
  1.3718 +    return mSize;
  1.3719 +  }
  1.3720 +
  1.3721 +  NS_IMETHOD Run()
  1.3722 +  {
  1.3723 +    mozilla::MonitorAutoLock mon(mMonitor);
  1.3724 +    // Excluding this since the object itself is a member of CacheFileIOManager
  1.3725 +    // reported in CacheFileIOManager::SizeOfIncludingThis as part of |this|.
  1.3726 +    mSize = mHandles.SizeOfExcludingThis(mMallocSizeOf);
  1.3727 +    for (uint32_t i = 0; i < mSpecialHandles.Length(); ++i) {
  1.3728 +      mSize += mSpecialHandles[i]->SizeOfIncludingThis(mMallocSizeOf);
  1.3729 +    }
  1.3730 +
  1.3731 +    mon.Notify();
  1.3732 +    return NS_OK;
  1.3733 +  }
  1.3734 +
  1.3735 +private:
  1.3736 +  mozilla::Monitor mMonitor;
  1.3737 +  mozilla::MallocSizeOf mMallocSizeOf;
  1.3738 +  CacheFileHandles const &mHandles;
  1.3739 +  nsTArray<CacheFileHandle *> const &mSpecialHandles;
  1.3740 +  size_t mSize;
  1.3741 +};
  1.3742 +
  1.3743 +} // anon
  1.3744 +
  1.3745 +size_t
  1.3746 +CacheFileIOManager::SizeOfExcludingThisInternal(mozilla::MallocSizeOf mallocSizeOf) const
  1.3747 +{
  1.3748 +  size_t n = 0;
  1.3749 +  nsCOMPtr<nsISizeOf> sizeOf;
  1.3750 +
  1.3751 +  if (mIOThread) {
  1.3752 +    n += mIOThread->SizeOfIncludingThis(mallocSizeOf);
  1.3753 +
  1.3754 +    // mHandles and mSpecialHandles must be accessed only on the I/O thread,
  1.3755 +    // must sync dispatch.
  1.3756 +    nsRefPtr<SizeOfHandlesRunnable> sizeOfHandlesRunnable =
  1.3757 +      new SizeOfHandlesRunnable(mallocSizeOf, mHandles, mSpecialHandles);
  1.3758 +    n += sizeOfHandlesRunnable->Get(mIOThread);
  1.3759 +  }
  1.3760 +
  1.3761 +  // mHandlesByLastUsed just refers handles reported by mHandles.
  1.3762 +
  1.3763 +  sizeOf = do_QueryInterface(mCacheDirectory);
  1.3764 +  if (sizeOf)
  1.3765 +    n += sizeOf->SizeOfIncludingThis(mallocSizeOf);
  1.3766 +
  1.3767 +  sizeOf = do_QueryInterface(mMetadataWritesTimer);
  1.3768 +  if (sizeOf)
  1.3769 +    n += sizeOf->SizeOfIncludingThis(mallocSizeOf);
  1.3770 +
  1.3771 +  sizeOf = do_QueryInterface(mTrashTimer);
  1.3772 +  if (sizeOf)
  1.3773 +    n += sizeOf->SizeOfIncludingThis(mallocSizeOf);
  1.3774 +
  1.3775 +  sizeOf = do_QueryInterface(mTrashDir);
  1.3776 +  if (sizeOf)
  1.3777 +    n += sizeOf->SizeOfIncludingThis(mallocSizeOf);
  1.3778 +
  1.3779 +  for (uint32_t i = 0; i < mFailedTrashDirs.Length(); ++i) {
  1.3780 +    n += mFailedTrashDirs[i].SizeOfExcludingThisIfUnshared(mallocSizeOf);
  1.3781 +  }
  1.3782 +
  1.3783 +  return n;
  1.3784 +}
  1.3785 +
  1.3786 +// static
  1.3787 +size_t
  1.3788 +CacheFileIOManager::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf)
  1.3789 +{
  1.3790 +  if (!gInstance)
  1.3791 +    return 0;
  1.3792 +
  1.3793 +  return gInstance->SizeOfExcludingThisInternal(mallocSizeOf);
  1.3794 +}
  1.3795 +
  1.3796 +// static
  1.3797 +size_t
  1.3798 +CacheFileIOManager::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf)
  1.3799 +{
  1.3800 +  return mallocSizeOf(gInstance) + SizeOfExcludingThis(mallocSizeOf);
  1.3801 +}
  1.3802 +
  1.3803 +} // net
  1.3804 +} // mozilla

mercurial