netwerk/cache2/CacheFileIOManager.cpp

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

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

Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 #include "CacheLog.h"
michael@0 6 #include "CacheFileIOManager.h"
michael@0 7
michael@0 8 #include "../cache/nsCacheUtils.h"
michael@0 9 #include "CacheHashUtils.h"
michael@0 10 #include "CacheStorageService.h"
michael@0 11 #include "CacheIndex.h"
michael@0 12 #include "CacheFileUtils.h"
michael@0 13 #include "nsThreadUtils.h"
michael@0 14 #include "CacheFile.h"
michael@0 15 #include "CacheObserver.h"
michael@0 16 #include "nsIFile.h"
michael@0 17 #include "CacheFileContextEvictor.h"
michael@0 18 #include "nsITimer.h"
michael@0 19 #include "nsISimpleEnumerator.h"
michael@0 20 #include "nsIDirectoryEnumerator.h"
michael@0 21 #include "nsIObserverService.h"
michael@0 22 #include "nsISizeOf.h"
michael@0 23 #include "mozilla/Telemetry.h"
michael@0 24 #include "mozilla/DebugOnly.h"
michael@0 25 #include "mozilla/Services.h"
michael@0 26 #include "nsDirectoryServiceUtils.h"
michael@0 27 #include "nsAppDirectoryServiceDefs.h"
michael@0 28 #include "private/pprio.h"
michael@0 29 #include "mozilla/VisualEventTracer.h"
michael@0 30 #include "mozilla/Preferences.h"
michael@0 31
michael@0 32 // include files for ftruncate (or equivalent)
michael@0 33 #if defined(XP_UNIX)
michael@0 34 #include <unistd.h>
michael@0 35 #elif defined(XP_WIN)
michael@0 36 #include <windows.h>
michael@0 37 #undef CreateFile
michael@0 38 #undef CREATE_NEW
michael@0 39 #else
michael@0 40 // XXX add necessary include file for ftruncate (or equivalent)
michael@0 41 #endif
michael@0 42
michael@0 43
michael@0 44 namespace mozilla {
michael@0 45 namespace net {
michael@0 46
michael@0 47 #define kOpenHandlesLimit 64
michael@0 48 #define kMetadataWriteDelay 5000
michael@0 49 #define kRemoveTrashStartDelay 60000 // in milliseconds
michael@0 50 #define kSmartSizeUpdateInterval 60000 // in milliseconds
michael@0 51
michael@0 52 #ifdef ANDROID
michael@0 53 const uint32_t kMaxCacheSizeKB = 200*1024; // 200 MB
michael@0 54 #else
michael@0 55 const uint32_t kMaxCacheSizeKB = 350*1024; // 350 MB
michael@0 56 #endif
michael@0 57
michael@0 58 bool
michael@0 59 CacheFileHandle::DispatchRelease()
michael@0 60 {
michael@0 61 if (CacheFileIOManager::IsOnIOThreadOrCeased()) {
michael@0 62 return false;
michael@0 63 }
michael@0 64
michael@0 65 nsCOMPtr<nsIEventTarget> ioTarget = CacheFileIOManager::IOTarget();
michael@0 66 if (!ioTarget) {
michael@0 67 return false;
michael@0 68 }
michael@0 69
michael@0 70 nsRefPtr<nsRunnableMethod<CacheFileHandle, MozExternalRefCountType, false> > event =
michael@0 71 NS_NewNonOwningRunnableMethod(this, &CacheFileHandle::Release);
michael@0 72 nsresult rv = ioTarget->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL);
michael@0 73 if (NS_FAILED(rv)) {
michael@0 74 return false;
michael@0 75 }
michael@0 76
michael@0 77 return true;
michael@0 78 }
michael@0 79
michael@0 80 NS_IMPL_ADDREF(CacheFileHandle)
michael@0 81 NS_IMETHODIMP_(MozExternalRefCountType)
michael@0 82 CacheFileHandle::Release()
michael@0 83 {
michael@0 84 nsrefcnt count = mRefCnt - 1;
michael@0 85 if (DispatchRelease()) {
michael@0 86 // Redispatched to the IO thread.
michael@0 87 return count;
michael@0 88 }
michael@0 89
michael@0 90 MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
michael@0 91
michael@0 92 LOG(("CacheFileHandle::Release() [this=%p, refcnt=%d]", this, mRefCnt.get()));
michael@0 93 NS_PRECONDITION(0 != mRefCnt, "dup release");
michael@0 94 count = --mRefCnt;
michael@0 95 NS_LOG_RELEASE(this, count, "CacheFileHandle");
michael@0 96
michael@0 97 if (0 == count) {
michael@0 98 mRefCnt = 1;
michael@0 99 delete (this);
michael@0 100 return 0;
michael@0 101 }
michael@0 102
michael@0 103 return count;
michael@0 104 }
michael@0 105
michael@0 106 NS_INTERFACE_MAP_BEGIN(CacheFileHandle)
michael@0 107 NS_INTERFACE_MAP_ENTRY(nsISupports)
michael@0 108 NS_INTERFACE_MAP_END_THREADSAFE
michael@0 109
michael@0 110 CacheFileHandle::CacheFileHandle(const SHA1Sum::Hash *aHash, bool aPriority)
michael@0 111 : mHash(aHash)
michael@0 112 , mIsDoomed(false)
michael@0 113 , mPriority(aPriority)
michael@0 114 , mClosed(false)
michael@0 115 , mInvalid(false)
michael@0 116 , mFileExists(false)
michael@0 117 , mFileSize(-1)
michael@0 118 , mFD(nullptr)
michael@0 119 {
michael@0 120 LOG(("CacheFileHandle::CacheFileHandle() [this=%p, hash=%08x%08x%08x%08x%08x]"
michael@0 121 , this, LOGSHA1(aHash)));
michael@0 122 }
michael@0 123
michael@0 124 CacheFileHandle::CacheFileHandle(const nsACString &aKey, bool aPriority)
michael@0 125 : mHash(nullptr)
michael@0 126 , mIsDoomed(false)
michael@0 127 , mPriority(aPriority)
michael@0 128 , mClosed(false)
michael@0 129 , mInvalid(false)
michael@0 130 , mFileExists(false)
michael@0 131 , mFileSize(-1)
michael@0 132 , mFD(nullptr)
michael@0 133 , mKey(aKey)
michael@0 134 {
michael@0 135 LOG(("CacheFileHandle::CacheFileHandle() [this=%p, key=%s]", this,
michael@0 136 PromiseFlatCString(aKey).get()));
michael@0 137 }
michael@0 138
michael@0 139 CacheFileHandle::~CacheFileHandle()
michael@0 140 {
michael@0 141 LOG(("CacheFileHandle::~CacheFileHandle() [this=%p]", this));
michael@0 142
michael@0 143 MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
michael@0 144
michael@0 145 nsRefPtr<CacheFileIOManager> ioMan = CacheFileIOManager::gInstance;
michael@0 146 if (ioMan) {
michael@0 147 ioMan->CloseHandleInternal(this);
michael@0 148 }
michael@0 149 }
michael@0 150
michael@0 151 void
michael@0 152 CacheFileHandle::Log()
michael@0 153 {
michael@0 154 nsAutoCString leafName;
michael@0 155 if (mFile) {
michael@0 156 mFile->GetNativeLeafName(leafName);
michael@0 157 }
michael@0 158
michael@0 159 if (!mHash) {
michael@0 160 // special file
michael@0 161 LOG(("CacheFileHandle::Log() [this=%p, hash=nullptr, isDoomed=%d, "
michael@0 162 "priority=%d, closed=%d, invalid=%d, "
michael@0 163 "fileExists=%d, fileSize=%lld, leafName=%s, key=%s]",
michael@0 164 this, mIsDoomed, mPriority, mClosed, mInvalid,
michael@0 165 mFileExists, mFileSize, leafName.get(), mKey.get()));
michael@0 166 } else {
michael@0 167 LOG(("CacheFileHandle::Log() [this=%p, hash=%08x%08x%08x%08x%08x, "
michael@0 168 "isDoomed=%d, priority=%d, closed=%d, invalid=%d, "
michael@0 169 "fileExists=%d, fileSize=%lld, leafName=%s, key=%s]",
michael@0 170 this, LOGSHA1(mHash), mIsDoomed, mPriority, mClosed,
michael@0 171 mInvalid, mFileExists, mFileSize, leafName.get(), mKey.get()));
michael@0 172 }
michael@0 173 }
michael@0 174
michael@0 175 uint32_t
michael@0 176 CacheFileHandle::FileSizeInK() const
michael@0 177 {
michael@0 178 MOZ_ASSERT(mFileSize != -1);
michael@0 179 uint64_t size64 = mFileSize;
michael@0 180
michael@0 181 size64 += 0x3FF;
michael@0 182 size64 >>= 10;
michael@0 183
michael@0 184 uint32_t size;
michael@0 185 if (size64 >> 32) {
michael@0 186 NS_WARNING("CacheFileHandle::FileSizeInK() - FileSize is too large, "
michael@0 187 "truncating to PR_UINT32_MAX");
michael@0 188 size = PR_UINT32_MAX;
michael@0 189 } else {
michael@0 190 size = static_cast<uint32_t>(size64);
michael@0 191 }
michael@0 192
michael@0 193 return size;
michael@0 194 }
michael@0 195
michael@0 196 // Memory reporting
michael@0 197
michael@0 198 size_t
michael@0 199 CacheFileHandle::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
michael@0 200 {
michael@0 201 size_t n = 0;
michael@0 202 nsCOMPtr<nsISizeOf> sizeOf;
michael@0 203
michael@0 204 sizeOf = do_QueryInterface(mFile);
michael@0 205 if (sizeOf) {
michael@0 206 n += sizeOf->SizeOfIncludingThis(mallocSizeOf);
michael@0 207 }
michael@0 208
michael@0 209 n += mallocSizeOf(mFD);
michael@0 210 n += mKey.SizeOfExcludingThisIfUnshared(mallocSizeOf);
michael@0 211 return n;
michael@0 212 }
michael@0 213
michael@0 214 size_t
michael@0 215 CacheFileHandle::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
michael@0 216 {
michael@0 217 return mallocSizeOf(this) + SizeOfExcludingThis(mallocSizeOf);
michael@0 218 }
michael@0 219
michael@0 220 /******************************************************************************
michael@0 221 * CacheFileHandles::HandleHashKey
michael@0 222 *****************************************************************************/
michael@0 223
michael@0 224 void
michael@0 225 CacheFileHandles::HandleHashKey::AddHandle(CacheFileHandle* aHandle)
michael@0 226 {
michael@0 227 MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
michael@0 228
michael@0 229 mHandles.InsertElementAt(0, aHandle);
michael@0 230 }
michael@0 231
michael@0 232 void
michael@0 233 CacheFileHandles::HandleHashKey::RemoveHandle(CacheFileHandle* aHandle)
michael@0 234 {
michael@0 235 MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
michael@0 236
michael@0 237 DebugOnly<bool> found;
michael@0 238 found = mHandles.RemoveElement(aHandle);
michael@0 239 MOZ_ASSERT(found);
michael@0 240 }
michael@0 241
michael@0 242 already_AddRefed<CacheFileHandle>
michael@0 243 CacheFileHandles::HandleHashKey::GetNewestHandle()
michael@0 244 {
michael@0 245 MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
michael@0 246
michael@0 247 nsRefPtr<CacheFileHandle> handle;
michael@0 248 if (mHandles.Length()) {
michael@0 249 handle = mHandles[0];
michael@0 250 }
michael@0 251
michael@0 252 return handle.forget();
michael@0 253 }
michael@0 254
michael@0 255 void
michael@0 256 CacheFileHandles::HandleHashKey::GetHandles(nsTArray<nsRefPtr<CacheFileHandle> > &aResult)
michael@0 257 {
michael@0 258 MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
michael@0 259
michael@0 260 for (uint32_t i = 0; i < mHandles.Length(); ++i) {
michael@0 261 CacheFileHandle* handle = mHandles[i];
michael@0 262 aResult.AppendElement(handle);
michael@0 263 }
michael@0 264 }
michael@0 265
michael@0 266 #ifdef DEBUG
michael@0 267
michael@0 268 void
michael@0 269 CacheFileHandles::HandleHashKey::AssertHandlesState()
michael@0 270 {
michael@0 271 for (uint32_t i = 0; i < mHandles.Length(); ++i) {
michael@0 272 CacheFileHandle* handle = mHandles[i];
michael@0 273 MOZ_ASSERT(handle->IsDoomed());
michael@0 274 }
michael@0 275 }
michael@0 276
michael@0 277 #endif
michael@0 278
michael@0 279 size_t
michael@0 280 CacheFileHandles::HandleHashKey::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
michael@0 281 {
michael@0 282 MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
michael@0 283
michael@0 284 size_t n = 0;
michael@0 285 n += mallocSizeOf(mHash);
michael@0 286 for (uint32_t i = 0; i < mHandles.Length(); ++i) {
michael@0 287 n += mHandles[i]->SizeOfIncludingThis(mallocSizeOf);
michael@0 288 }
michael@0 289
michael@0 290 return n;
michael@0 291 }
michael@0 292
michael@0 293 /******************************************************************************
michael@0 294 * CacheFileHandles
michael@0 295 *****************************************************************************/
michael@0 296
michael@0 297 CacheFileHandles::CacheFileHandles()
michael@0 298 {
michael@0 299 LOG(("CacheFileHandles::CacheFileHandles() [this=%p]", this));
michael@0 300 MOZ_COUNT_CTOR(CacheFileHandles);
michael@0 301 }
michael@0 302
michael@0 303 CacheFileHandles::~CacheFileHandles()
michael@0 304 {
michael@0 305 LOG(("CacheFileHandles::~CacheFileHandles() [this=%p]", this));
michael@0 306 MOZ_COUNT_DTOR(CacheFileHandles);
michael@0 307 }
michael@0 308
michael@0 309 nsresult
michael@0 310 CacheFileHandles::GetHandle(const SHA1Sum::Hash *aHash,
michael@0 311 bool aReturnDoomed,
michael@0 312 CacheFileHandle **_retval)
michael@0 313 {
michael@0 314 MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
michael@0 315 MOZ_ASSERT(aHash);
michael@0 316
michael@0 317 #ifdef DEBUG_HANDLES
michael@0 318 LOG(("CacheFileHandles::GetHandle() [hash=%08x%08x%08x%08x%08x]",
michael@0 319 LOGSHA1(aHash)));
michael@0 320 #endif
michael@0 321
michael@0 322 // find hash entry for key
michael@0 323 HandleHashKey *entry = mTable.GetEntry(*aHash);
michael@0 324 if (!entry) {
michael@0 325 LOG(("CacheFileHandles::GetHandle() hash=%08x%08x%08x%08x%08x "
michael@0 326 "no handle entries found", LOGSHA1(aHash)));
michael@0 327 return NS_ERROR_NOT_AVAILABLE;
michael@0 328 }
michael@0 329
michael@0 330 #ifdef DEBUG_HANDLES
michael@0 331 Log(entry);
michael@0 332 #endif
michael@0 333
michael@0 334 // Check if the entry is doomed
michael@0 335 nsRefPtr<CacheFileHandle> handle = entry->GetNewestHandle();
michael@0 336 if (!handle) {
michael@0 337 LOG(("CacheFileHandles::GetHandle() hash=%08x%08x%08x%08x%08x "
michael@0 338 "no handle found %p, entry %p", LOGSHA1(aHash), handle.get(), entry));
michael@0 339 return NS_ERROR_NOT_AVAILABLE;
michael@0 340 }
michael@0 341
michael@0 342 if (handle->IsDoomed()) {
michael@0 343 LOG(("CacheFileHandles::GetHandle() hash=%08x%08x%08x%08x%08x "
michael@0 344 "found doomed handle %p, entry %p", LOGSHA1(aHash), handle.get(), entry));
michael@0 345
michael@0 346 // If the consumer doesn't want doomed handles, exit with NOT_AVAIL.
michael@0 347 if (!aReturnDoomed) {
michael@0 348 return NS_ERROR_NOT_AVAILABLE;
michael@0 349 }
michael@0 350 } else {
michael@0 351 LOG(("CacheFileHandles::GetHandle() hash=%08x%08x%08x%08x%08x "
michael@0 352 "found handle %p, entry %p", LOGSHA1(aHash), handle.get(), entry));
michael@0 353 }
michael@0 354
michael@0 355 handle.forget(_retval);
michael@0 356 return NS_OK;
michael@0 357 }
michael@0 358
michael@0 359
michael@0 360 nsresult
michael@0 361 CacheFileHandles::NewHandle(const SHA1Sum::Hash *aHash,
michael@0 362 bool aPriority,
michael@0 363 CacheFileHandle **_retval)
michael@0 364 {
michael@0 365 MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
michael@0 366 MOZ_ASSERT(aHash);
michael@0 367
michael@0 368 #ifdef DEBUG_HANDLES
michael@0 369 LOG(("CacheFileHandles::NewHandle() [hash=%08x%08x%08x%08x%08x]", LOGSHA1(aHash)));
michael@0 370 #endif
michael@0 371
michael@0 372 // find hash entry for key
michael@0 373 HandleHashKey *entry = mTable.PutEntry(*aHash);
michael@0 374
michael@0 375 #ifdef DEBUG_HANDLES
michael@0 376 Log(entry);
michael@0 377 #endif
michael@0 378
michael@0 379 #ifdef DEBUG
michael@0 380 entry->AssertHandlesState();
michael@0 381 #endif
michael@0 382
michael@0 383 nsRefPtr<CacheFileHandle> handle = new CacheFileHandle(entry->Hash(), aPriority);
michael@0 384 entry->AddHandle(handle);
michael@0 385
michael@0 386 LOG(("CacheFileHandles::NewHandle() hash=%08x%08x%08x%08x%08x "
michael@0 387 "created new handle %p, entry=%p", LOGSHA1(aHash), handle.get(), entry));
michael@0 388
michael@0 389 handle.forget(_retval);
michael@0 390 return NS_OK;
michael@0 391 }
michael@0 392
michael@0 393 void
michael@0 394 CacheFileHandles::RemoveHandle(CacheFileHandle *aHandle)
michael@0 395 {
michael@0 396 MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
michael@0 397 MOZ_ASSERT(aHandle);
michael@0 398
michael@0 399 if (!aHandle) {
michael@0 400 return;
michael@0 401 }
michael@0 402
michael@0 403 #ifdef DEBUG_HANDLES
michael@0 404 LOG(("CacheFileHandles::RemoveHandle() [handle=%p, hash=%08x%08x%08x%08x%08x]"
michael@0 405 , aHandle, LOGSHA1(aHandle->Hash())));
michael@0 406 #endif
michael@0 407
michael@0 408 // find hash entry for key
michael@0 409 HandleHashKey *entry = mTable.GetEntry(*aHandle->Hash());
michael@0 410 if (!entry) {
michael@0 411 MOZ_ASSERT(CacheFileIOManager::IsShutdown(),
michael@0 412 "Should find entry when removing a handle before shutdown");
michael@0 413
michael@0 414 LOG(("CacheFileHandles::RemoveHandle() hash=%08x%08x%08x%08x%08x "
michael@0 415 "no entries found", LOGSHA1(aHandle->Hash())));
michael@0 416 return;
michael@0 417 }
michael@0 418
michael@0 419 #ifdef DEBUG_HANDLES
michael@0 420 Log(entry);
michael@0 421 #endif
michael@0 422
michael@0 423 LOG(("CacheFileHandles::RemoveHandle() hash=%08x%08x%08x%08x%08x "
michael@0 424 "removing handle %p", LOGSHA1(entry->Hash()), aHandle));
michael@0 425 entry->RemoveHandle(aHandle);
michael@0 426
michael@0 427 if (entry->IsEmpty()) {
michael@0 428 LOG(("CacheFileHandles::RemoveHandle() hash=%08x%08x%08x%08x%08x "
michael@0 429 "list is empty, removing entry %p", LOGSHA1(entry->Hash()), entry));
michael@0 430 mTable.RemoveEntry(*entry->Hash());
michael@0 431 }
michael@0 432 }
michael@0 433
michael@0 434 static PLDHashOperator
michael@0 435 GetAllHandlesEnum(CacheFileHandles::HandleHashKey* aEntry, void *aClosure)
michael@0 436 {
michael@0 437 nsTArray<nsRefPtr<CacheFileHandle> > *array =
michael@0 438 static_cast<nsTArray<nsRefPtr<CacheFileHandle> > *>(aClosure);
michael@0 439
michael@0 440 aEntry->GetHandles(*array);
michael@0 441 return PL_DHASH_NEXT;
michael@0 442 }
michael@0 443
michael@0 444 void
michael@0 445 CacheFileHandles::GetAllHandles(nsTArray<nsRefPtr<CacheFileHandle> > *_retval)
michael@0 446 {
michael@0 447 MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
michael@0 448 mTable.EnumerateEntries(&GetAllHandlesEnum, _retval);
michael@0 449 }
michael@0 450
michael@0 451 static PLDHashOperator
michael@0 452 GetActiveHandlesEnum(CacheFileHandles::HandleHashKey* aEntry, void *aClosure)
michael@0 453 {
michael@0 454 nsTArray<nsRefPtr<CacheFileHandle> > *array =
michael@0 455 static_cast<nsTArray<nsRefPtr<CacheFileHandle> > *>(aClosure);
michael@0 456
michael@0 457 nsRefPtr<CacheFileHandle> handle = aEntry->GetNewestHandle();
michael@0 458 MOZ_ASSERT(handle);
michael@0 459
michael@0 460 if (!handle->IsDoomed()) {
michael@0 461 array->AppendElement(handle);
michael@0 462 }
michael@0 463
michael@0 464 return PL_DHASH_NEXT;
michael@0 465 }
michael@0 466
michael@0 467 void
michael@0 468 CacheFileHandles::GetActiveHandles(
michael@0 469 nsTArray<nsRefPtr<CacheFileHandle> > *_retval)
michael@0 470 {
michael@0 471 MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
michael@0 472 mTable.EnumerateEntries(&GetActiveHandlesEnum, _retval);
michael@0 473 }
michael@0 474
michael@0 475 void
michael@0 476 CacheFileHandles::ClearAll()
michael@0 477 {
michael@0 478 MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
michael@0 479 mTable.Clear();
michael@0 480 }
michael@0 481
michael@0 482 uint32_t
michael@0 483 CacheFileHandles::HandleCount()
michael@0 484 {
michael@0 485 return mTable.Count();
michael@0 486 }
michael@0 487
michael@0 488 #ifdef DEBUG_HANDLES
michael@0 489 void
michael@0 490 CacheFileHandles::Log(CacheFileHandlesEntry *entry)
michael@0 491 {
michael@0 492 LOG(("CacheFileHandles::Log() BEGIN [entry=%p]", entry));
michael@0 493
michael@0 494 nsTArray<nsRefPtr<CacheFileHandle> > array;
michael@0 495 aEntry->GetHandles(array);
michael@0 496
michael@0 497 for (uint32_t i = 0; i < array.Length(); ++i) {
michael@0 498 CacheFileHandle *handle = array[i];
michael@0 499 handle->Log();
michael@0 500 }
michael@0 501
michael@0 502 LOG(("CacheFileHandles::Log() END [entry=%p]", entry));
michael@0 503 }
michael@0 504 #endif
michael@0 505
michael@0 506 // Memory reporting
michael@0 507
michael@0 508 namespace { // anon
michael@0 509
michael@0 510 size_t
michael@0 511 CollectHandlesMemory(CacheFileHandles::HandleHashKey* key,
michael@0 512 mozilla::MallocSizeOf mallocSizeOf,
michael@0 513 void *arg)
michael@0 514 {
michael@0 515 return key->SizeOfExcludingThis(mallocSizeOf);
michael@0 516 }
michael@0 517
michael@0 518 } // anon
michael@0 519
michael@0 520 size_t
michael@0 521 CacheFileHandles::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
michael@0 522 {
michael@0 523 MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
michael@0 524
michael@0 525 return mTable.SizeOfExcludingThis(&CollectHandlesMemory, mallocSizeOf);
michael@0 526 }
michael@0 527
michael@0 528 // Events
michael@0 529
michael@0 530 class ShutdownEvent : public nsRunnable {
michael@0 531 public:
michael@0 532 ShutdownEvent(mozilla::Mutex *aLock, mozilla::CondVar *aCondVar)
michael@0 533 : mLock(aLock)
michael@0 534 , mCondVar(aCondVar)
michael@0 535 {
michael@0 536 MOZ_COUNT_CTOR(ShutdownEvent);
michael@0 537 }
michael@0 538
michael@0 539 ~ShutdownEvent()
michael@0 540 {
michael@0 541 MOZ_COUNT_DTOR(ShutdownEvent);
michael@0 542 }
michael@0 543
michael@0 544 NS_IMETHOD Run()
michael@0 545 {
michael@0 546 MutexAutoLock lock(*mLock);
michael@0 547
michael@0 548 CacheFileIOManager::gInstance->ShutdownInternal();
michael@0 549
michael@0 550 mCondVar->Notify();
michael@0 551 return NS_OK;
michael@0 552 }
michael@0 553
michael@0 554 protected:
michael@0 555 mozilla::Mutex *mLock;
michael@0 556 mozilla::CondVar *mCondVar;
michael@0 557 };
michael@0 558
michael@0 559 class OpenFileEvent : public nsRunnable {
michael@0 560 public:
michael@0 561 OpenFileEvent(const nsACString &aKey,
michael@0 562 uint32_t aFlags, bool aResultOnAnyThread,
michael@0 563 CacheFileIOListener *aCallback)
michael@0 564 : mFlags(aFlags)
michael@0 565 , mResultOnAnyThread(aResultOnAnyThread)
michael@0 566 , mCallback(aCallback)
michael@0 567 , mRV(NS_ERROR_FAILURE)
michael@0 568 , mKey(aKey)
michael@0 569 {
michael@0 570 MOZ_COUNT_CTOR(OpenFileEvent);
michael@0 571
michael@0 572 if (!aResultOnAnyThread) {
michael@0 573 mTarget = static_cast<nsIEventTarget*>(NS_GetCurrentThread());
michael@0 574 MOZ_ASSERT(mTarget);
michael@0 575 }
michael@0 576
michael@0 577 mIOMan = CacheFileIOManager::gInstance;
michael@0 578
michael@0 579 MOZ_EVENT_TRACER_NAME_OBJECT(static_cast<nsIRunnable*>(this), aKey.BeginReading());
michael@0 580 MOZ_EVENT_TRACER_WAIT(static_cast<nsIRunnable*>(this), "net::cache::open-background");
michael@0 581 }
michael@0 582
michael@0 583 ~OpenFileEvent()
michael@0 584 {
michael@0 585 MOZ_COUNT_DTOR(OpenFileEvent);
michael@0 586 }
michael@0 587
michael@0 588 NS_IMETHOD Run()
michael@0 589 {
michael@0 590 if (mResultOnAnyThread || mTarget) {
michael@0 591 mRV = NS_OK;
michael@0 592
michael@0 593 if (!(mFlags & CacheFileIOManager::SPECIAL_FILE)) {
michael@0 594 SHA1Sum sum;
michael@0 595 sum.update(mKey.BeginReading(), mKey.Length());
michael@0 596 sum.finish(mHash);
michael@0 597 }
michael@0 598
michael@0 599 MOZ_EVENT_TRACER_EXEC(static_cast<nsIRunnable*>(this),
michael@0 600 "net::cache::open-background");
michael@0 601 if (NS_SUCCEEDED(mRV)) {
michael@0 602 if (!mIOMan) {
michael@0 603 mRV = NS_ERROR_NOT_INITIALIZED;
michael@0 604 } else {
michael@0 605 if (mFlags & CacheFileIOManager::SPECIAL_FILE) {
michael@0 606 mRV = mIOMan->OpenSpecialFileInternal(mKey, mFlags,
michael@0 607 getter_AddRefs(mHandle));
michael@0 608 } else {
michael@0 609 mRV = mIOMan->OpenFileInternal(&mHash, mKey, mFlags,
michael@0 610 getter_AddRefs(mHandle));
michael@0 611 }
michael@0 612 mIOMan = nullptr;
michael@0 613 if (mHandle) {
michael@0 614 MOZ_EVENT_TRACER_NAME_OBJECT(mHandle.get(), mKey.get());
michael@0 615 if (mHandle->Key().IsEmpty()) {
michael@0 616 mHandle->Key() = mKey;
michael@0 617 }
michael@0 618 }
michael@0 619 }
michael@0 620 }
michael@0 621 MOZ_EVENT_TRACER_DONE(static_cast<nsIRunnable*>(this), "net::cache::open-background");
michael@0 622
michael@0 623 MOZ_EVENT_TRACER_WAIT(static_cast<nsIRunnable*>(this), "net::cache::open-result");
michael@0 624
michael@0 625 if (mTarget) {
michael@0 626 nsCOMPtr<nsIEventTarget> target;
michael@0 627 mTarget.swap(target);
michael@0 628 return target->Dispatch(this, nsIEventTarget::DISPATCH_NORMAL);
michael@0 629 }
michael@0 630 }
michael@0 631
michael@0 632 if (!mTarget) {
michael@0 633 MOZ_EVENT_TRACER_EXEC(static_cast<nsIRunnable*>(this), "net::cache::open-result");
michael@0 634 mCallback->OnFileOpened(mHandle, mRV);
michael@0 635 MOZ_EVENT_TRACER_DONE(static_cast<nsIRunnable*>(this), "net::cache::open-result");
michael@0 636 }
michael@0 637
michael@0 638 return NS_OK;
michael@0 639 }
michael@0 640
michael@0 641 protected:
michael@0 642 SHA1Sum::Hash mHash;
michael@0 643 uint32_t mFlags;
michael@0 644 bool mResultOnAnyThread;
michael@0 645 nsCOMPtr<CacheFileIOListener> mCallback;
michael@0 646 nsCOMPtr<nsIEventTarget> mTarget;
michael@0 647 nsRefPtr<CacheFileIOManager> mIOMan;
michael@0 648 nsRefPtr<CacheFileHandle> mHandle;
michael@0 649 nsresult mRV;
michael@0 650 nsCString mKey;
michael@0 651 };
michael@0 652
michael@0 653 class ReadEvent : public nsRunnable {
michael@0 654 public:
michael@0 655 ReadEvent(CacheFileHandle *aHandle, int64_t aOffset, char *aBuf,
michael@0 656 int32_t aCount, bool aResultOnAnyThread, CacheFileIOListener *aCallback)
michael@0 657 : mHandle(aHandle)
michael@0 658 , mOffset(aOffset)
michael@0 659 , mBuf(aBuf)
michael@0 660 , mCount(aCount)
michael@0 661 , mResultOnAnyThread(aResultOnAnyThread)
michael@0 662 , mCallback(aCallback)
michael@0 663 , mRV(NS_ERROR_FAILURE)
michael@0 664 {
michael@0 665 MOZ_COUNT_CTOR(ReadEvent);
michael@0 666
michael@0 667 if (!aResultOnAnyThread) {
michael@0 668 mTarget = static_cast<nsIEventTarget*>(NS_GetCurrentThread());
michael@0 669 }
michael@0 670
michael@0 671 MOZ_EVENT_TRACER_NAME_OBJECT(static_cast<nsIRunnable*>(this), aHandle->Key().get());
michael@0 672 MOZ_EVENT_TRACER_WAIT(static_cast<nsIRunnable*>(this), "net::cache::read-background");
michael@0 673 }
michael@0 674
michael@0 675 ~ReadEvent()
michael@0 676 {
michael@0 677 MOZ_COUNT_DTOR(ReadEvent);
michael@0 678 }
michael@0 679
michael@0 680 NS_IMETHOD Run()
michael@0 681 {
michael@0 682 if (mResultOnAnyThread || mTarget) {
michael@0 683 MOZ_EVENT_TRACER_EXEC(static_cast<nsIRunnable*>(this), "net::cache::read-background");
michael@0 684 if (mHandle->IsClosed()) {
michael@0 685 mRV = NS_ERROR_NOT_INITIALIZED;
michael@0 686 } else {
michael@0 687 mRV = CacheFileIOManager::gInstance->ReadInternal(
michael@0 688 mHandle, mOffset, mBuf, mCount);
michael@0 689 }
michael@0 690 MOZ_EVENT_TRACER_DONE(static_cast<nsIRunnable*>(this), "net::cache::read-background");
michael@0 691
michael@0 692 MOZ_EVENT_TRACER_WAIT(static_cast<nsIRunnable*>(this), "net::cache::read-result");
michael@0 693
michael@0 694 if (mTarget) {
michael@0 695 nsCOMPtr<nsIEventTarget> target;
michael@0 696 mTarget.swap(target);
michael@0 697 return target->Dispatch(this, nsIEventTarget::DISPATCH_NORMAL);
michael@0 698 }
michael@0 699 }
michael@0 700
michael@0 701 if (!mTarget && mCallback) {
michael@0 702 MOZ_EVENT_TRACER_EXEC(static_cast<nsIRunnable*>(this), "net::cache::read-result");
michael@0 703 mCallback->OnDataRead(mHandle, mBuf, mRV);
michael@0 704 MOZ_EVENT_TRACER_DONE(static_cast<nsIRunnable*>(this), "net::cache::read-result");
michael@0 705 }
michael@0 706
michael@0 707 return NS_OK;
michael@0 708 }
michael@0 709
michael@0 710 protected:
michael@0 711 nsRefPtr<CacheFileHandle> mHandle;
michael@0 712 int64_t mOffset;
michael@0 713 char *mBuf;
michael@0 714 int32_t mCount;
michael@0 715 bool mResultOnAnyThread;
michael@0 716 nsCOMPtr<CacheFileIOListener> mCallback;
michael@0 717 nsCOMPtr<nsIEventTarget> mTarget;
michael@0 718 nsresult mRV;
michael@0 719 };
michael@0 720
michael@0 721 class WriteEvent : public nsRunnable {
michael@0 722 public:
michael@0 723 WriteEvent(CacheFileHandle *aHandle, int64_t aOffset, const char *aBuf,
michael@0 724 int32_t aCount, bool aValidate, CacheFileIOListener *aCallback)
michael@0 725 : mHandle(aHandle)
michael@0 726 , mOffset(aOffset)
michael@0 727 , mBuf(aBuf)
michael@0 728 , mCount(aCount)
michael@0 729 , mValidate(aValidate)
michael@0 730 , mCallback(aCallback)
michael@0 731 , mRV(NS_ERROR_FAILURE)
michael@0 732 {
michael@0 733 MOZ_COUNT_CTOR(WriteEvent);
michael@0 734 mTarget = static_cast<nsIEventTarget*>(NS_GetCurrentThread());
michael@0 735
michael@0 736 MOZ_EVENT_TRACER_NAME_OBJECT(static_cast<nsIRunnable*>(this), aHandle->Key().get());
michael@0 737 MOZ_EVENT_TRACER_WAIT(static_cast<nsIRunnable*>(this), "net::cache::write-background");
michael@0 738 }
michael@0 739
michael@0 740 ~WriteEvent()
michael@0 741 {
michael@0 742 MOZ_COUNT_DTOR(WriteEvent);
michael@0 743
michael@0 744 if (!mCallback && mBuf) {
michael@0 745 free(const_cast<char *>(mBuf));
michael@0 746 }
michael@0 747 }
michael@0 748
michael@0 749 NS_IMETHOD Run()
michael@0 750 {
michael@0 751 if (mTarget) {
michael@0 752 MOZ_EVENT_TRACER_EXEC(static_cast<nsIRunnable*>(this), "net::cache::write-background");
michael@0 753 if (mHandle->IsClosed()) {
michael@0 754 mRV = NS_ERROR_NOT_INITIALIZED;
michael@0 755 } else {
michael@0 756 mRV = CacheFileIOManager::gInstance->WriteInternal(
michael@0 757 mHandle, mOffset, mBuf, mCount, mValidate);
michael@0 758 }
michael@0 759 MOZ_EVENT_TRACER_DONE(static_cast<nsIRunnable*>(this), "net::cache::write-background");
michael@0 760
michael@0 761 MOZ_EVENT_TRACER_WAIT(static_cast<nsIRunnable*>(this), "net::cache::write-result");
michael@0 762 nsCOMPtr<nsIEventTarget> target;
michael@0 763 mTarget.swap(target);
michael@0 764 target->Dispatch(this, nsIEventTarget::DISPATCH_NORMAL);
michael@0 765 } else {
michael@0 766 MOZ_EVENT_TRACER_EXEC(static_cast<nsIRunnable*>(this), "net::cache::write-result");
michael@0 767 if (mCallback) {
michael@0 768 mCallback->OnDataWritten(mHandle, mBuf, mRV);
michael@0 769 } else {
michael@0 770 free(const_cast<char *>(mBuf));
michael@0 771 mBuf = nullptr;
michael@0 772 }
michael@0 773 MOZ_EVENT_TRACER_DONE(static_cast<nsIRunnable*>(this), "net::cache::write-result");
michael@0 774 }
michael@0 775 return NS_OK;
michael@0 776 }
michael@0 777
michael@0 778 protected:
michael@0 779 nsRefPtr<CacheFileHandle> mHandle;
michael@0 780 int64_t mOffset;
michael@0 781 const char *mBuf;
michael@0 782 int32_t mCount;
michael@0 783 bool mValidate;
michael@0 784 nsCOMPtr<CacheFileIOListener> mCallback;
michael@0 785 nsCOMPtr<nsIEventTarget> mTarget;
michael@0 786 nsresult mRV;
michael@0 787 };
michael@0 788
michael@0 789 class DoomFileEvent : public nsRunnable {
michael@0 790 public:
michael@0 791 DoomFileEvent(CacheFileHandle *aHandle,
michael@0 792 CacheFileIOListener *aCallback)
michael@0 793 : mCallback(aCallback)
michael@0 794 , mHandle(aHandle)
michael@0 795 , mRV(NS_ERROR_FAILURE)
michael@0 796 {
michael@0 797 MOZ_COUNT_CTOR(DoomFileEvent);
michael@0 798 mTarget = static_cast<nsIEventTarget*>(NS_GetCurrentThread());
michael@0 799
michael@0 800 MOZ_EVENT_TRACER_NAME_OBJECT(static_cast<nsIRunnable*>(this), aHandle->Key().get());
michael@0 801 MOZ_EVENT_TRACER_WAIT(static_cast<nsIRunnable*>(this), "net::cache::doom-background");
michael@0 802 }
michael@0 803
michael@0 804 ~DoomFileEvent()
michael@0 805 {
michael@0 806 MOZ_COUNT_DTOR(DoomFileEvent);
michael@0 807 }
michael@0 808
michael@0 809 NS_IMETHOD Run()
michael@0 810 {
michael@0 811 if (mTarget) {
michael@0 812 MOZ_EVENT_TRACER_EXEC(static_cast<nsIRunnable*>(this), "net::cache::doom-background");
michael@0 813 if (mHandle->IsClosed()) {
michael@0 814 mRV = NS_ERROR_NOT_INITIALIZED;
michael@0 815 } else {
michael@0 816 mRV = CacheFileIOManager::gInstance->DoomFileInternal(mHandle);
michael@0 817 }
michael@0 818 MOZ_EVENT_TRACER_DONE(static_cast<nsIRunnable*>(this), "net::cache::doom-background");
michael@0 819
michael@0 820 MOZ_EVENT_TRACER_WAIT(static_cast<nsIRunnable*>(this), "net::cache::doom-result");
michael@0 821 nsCOMPtr<nsIEventTarget> target;
michael@0 822 mTarget.swap(target);
michael@0 823 target->Dispatch(this, nsIEventTarget::DISPATCH_NORMAL);
michael@0 824 } else {
michael@0 825 MOZ_EVENT_TRACER_EXEC(static_cast<nsIRunnable*>(this), "net::cache::doom-result");
michael@0 826 if (mCallback) {
michael@0 827 mCallback->OnFileDoomed(mHandle, mRV);
michael@0 828 }
michael@0 829 MOZ_EVENT_TRACER_DONE(static_cast<nsIRunnable*>(this), "net::cache::doom-result");
michael@0 830 }
michael@0 831 return NS_OK;
michael@0 832 }
michael@0 833
michael@0 834 protected:
michael@0 835 nsCOMPtr<CacheFileIOListener> mCallback;
michael@0 836 nsCOMPtr<nsIEventTarget> mTarget;
michael@0 837 nsRefPtr<CacheFileHandle> mHandle;
michael@0 838 nsresult mRV;
michael@0 839 };
michael@0 840
michael@0 841 class DoomFileByKeyEvent : public nsRunnable {
michael@0 842 public:
michael@0 843 DoomFileByKeyEvent(const nsACString &aKey,
michael@0 844 CacheFileIOListener *aCallback)
michael@0 845 : mCallback(aCallback)
michael@0 846 , mRV(NS_ERROR_FAILURE)
michael@0 847 {
michael@0 848 MOZ_COUNT_CTOR(DoomFileByKeyEvent);
michael@0 849
michael@0 850 SHA1Sum sum;
michael@0 851 sum.update(aKey.BeginReading(), aKey.Length());
michael@0 852 sum.finish(mHash);
michael@0 853
michael@0 854 mTarget = static_cast<nsIEventTarget*>(NS_GetCurrentThread());
michael@0 855 mIOMan = CacheFileIOManager::gInstance;
michael@0 856 MOZ_ASSERT(mTarget);
michael@0 857 }
michael@0 858
michael@0 859 ~DoomFileByKeyEvent()
michael@0 860 {
michael@0 861 MOZ_COUNT_DTOR(DoomFileByKeyEvent);
michael@0 862 }
michael@0 863
michael@0 864 NS_IMETHOD Run()
michael@0 865 {
michael@0 866 if (mTarget) {
michael@0 867 if (!mIOMan) {
michael@0 868 mRV = NS_ERROR_NOT_INITIALIZED;
michael@0 869 } else {
michael@0 870 mRV = mIOMan->DoomFileByKeyInternal(&mHash);
michael@0 871 mIOMan = nullptr;
michael@0 872 }
michael@0 873
michael@0 874 nsCOMPtr<nsIEventTarget> target;
michael@0 875 mTarget.swap(target);
michael@0 876 target->Dispatch(this, nsIEventTarget::DISPATCH_NORMAL);
michael@0 877 } else {
michael@0 878 if (mCallback) {
michael@0 879 mCallback->OnFileDoomed(nullptr, mRV);
michael@0 880 }
michael@0 881 }
michael@0 882 return NS_OK;
michael@0 883 }
michael@0 884
michael@0 885 protected:
michael@0 886 SHA1Sum::Hash mHash;
michael@0 887 nsCOMPtr<CacheFileIOListener> mCallback;
michael@0 888 nsCOMPtr<nsIEventTarget> mTarget;
michael@0 889 nsRefPtr<CacheFileIOManager> mIOMan;
michael@0 890 nsresult mRV;
michael@0 891 };
michael@0 892
michael@0 893 class ReleaseNSPRHandleEvent : public nsRunnable {
michael@0 894 public:
michael@0 895 ReleaseNSPRHandleEvent(CacheFileHandle *aHandle)
michael@0 896 : mHandle(aHandle)
michael@0 897 {
michael@0 898 MOZ_COUNT_CTOR(ReleaseNSPRHandleEvent);
michael@0 899 }
michael@0 900
michael@0 901 ~ReleaseNSPRHandleEvent()
michael@0 902 {
michael@0 903 MOZ_COUNT_DTOR(ReleaseNSPRHandleEvent);
michael@0 904 }
michael@0 905
michael@0 906 NS_IMETHOD Run()
michael@0 907 {
michael@0 908 if (mHandle->mFD && !mHandle->IsClosed()) {
michael@0 909 CacheFileIOManager::gInstance->ReleaseNSPRHandleInternal(mHandle);
michael@0 910 }
michael@0 911
michael@0 912 return NS_OK;
michael@0 913 }
michael@0 914
michael@0 915 protected:
michael@0 916 nsRefPtr<CacheFileHandle> mHandle;
michael@0 917 };
michael@0 918
michael@0 919 class TruncateSeekSetEOFEvent : public nsRunnable {
michael@0 920 public:
michael@0 921 TruncateSeekSetEOFEvent(CacheFileHandle *aHandle, int64_t aTruncatePos,
michael@0 922 int64_t aEOFPos, CacheFileIOListener *aCallback)
michael@0 923 : mHandle(aHandle)
michael@0 924 , mTruncatePos(aTruncatePos)
michael@0 925 , mEOFPos(aEOFPos)
michael@0 926 , mCallback(aCallback)
michael@0 927 , mRV(NS_ERROR_FAILURE)
michael@0 928 {
michael@0 929 MOZ_COUNT_CTOR(TruncateSeekSetEOFEvent);
michael@0 930 mTarget = static_cast<nsIEventTarget*>(NS_GetCurrentThread());
michael@0 931 }
michael@0 932
michael@0 933 ~TruncateSeekSetEOFEvent()
michael@0 934 {
michael@0 935 MOZ_COUNT_DTOR(TruncateSeekSetEOFEvent);
michael@0 936 }
michael@0 937
michael@0 938 NS_IMETHOD Run()
michael@0 939 {
michael@0 940 if (mTarget) {
michael@0 941 if (mHandle->IsClosed()) {
michael@0 942 mRV = NS_ERROR_NOT_INITIALIZED;
michael@0 943 } else {
michael@0 944 mRV = CacheFileIOManager::gInstance->TruncateSeekSetEOFInternal(
michael@0 945 mHandle, mTruncatePos, mEOFPos);
michael@0 946 }
michael@0 947
michael@0 948 nsCOMPtr<nsIEventTarget> target;
michael@0 949 mTarget.swap(target);
michael@0 950 target->Dispatch(this, nsIEventTarget::DISPATCH_NORMAL);
michael@0 951 } else {
michael@0 952 if (mCallback) {
michael@0 953 mCallback->OnEOFSet(mHandle, mRV);
michael@0 954 }
michael@0 955 }
michael@0 956 return NS_OK;
michael@0 957 }
michael@0 958
michael@0 959 protected:
michael@0 960 nsRefPtr<CacheFileHandle> mHandle;
michael@0 961 int64_t mTruncatePos;
michael@0 962 int64_t mEOFPos;
michael@0 963 nsCOMPtr<CacheFileIOListener> mCallback;
michael@0 964 nsCOMPtr<nsIEventTarget> mTarget;
michael@0 965 nsresult mRV;
michael@0 966 };
michael@0 967
michael@0 968 class RenameFileEvent : public nsRunnable {
michael@0 969 public:
michael@0 970 RenameFileEvent(CacheFileHandle *aHandle, const nsACString &aNewName,
michael@0 971 CacheFileIOListener *aCallback)
michael@0 972 : mHandle(aHandle)
michael@0 973 , mNewName(aNewName)
michael@0 974 , mCallback(aCallback)
michael@0 975 , mRV(NS_ERROR_FAILURE)
michael@0 976 {
michael@0 977 MOZ_COUNT_CTOR(RenameFileEvent);
michael@0 978 mTarget = static_cast<nsIEventTarget*>(NS_GetCurrentThread());
michael@0 979 }
michael@0 980
michael@0 981 ~RenameFileEvent()
michael@0 982 {
michael@0 983 MOZ_COUNT_DTOR(RenameFileEvent);
michael@0 984 }
michael@0 985
michael@0 986 NS_IMETHOD Run()
michael@0 987 {
michael@0 988 if (mTarget) {
michael@0 989 if (mHandle->IsClosed()) {
michael@0 990 mRV = NS_ERROR_NOT_INITIALIZED;
michael@0 991 } else {
michael@0 992 mRV = CacheFileIOManager::gInstance->RenameFileInternal(mHandle,
michael@0 993 mNewName);
michael@0 994 }
michael@0 995
michael@0 996 nsCOMPtr<nsIEventTarget> target;
michael@0 997 mTarget.swap(target);
michael@0 998 target->Dispatch(this, nsIEventTarget::DISPATCH_NORMAL);
michael@0 999 } else {
michael@0 1000 if (mCallback) {
michael@0 1001 mCallback->OnFileRenamed(mHandle, mRV);
michael@0 1002 }
michael@0 1003 }
michael@0 1004 return NS_OK;
michael@0 1005 }
michael@0 1006
michael@0 1007 protected:
michael@0 1008 nsRefPtr<CacheFileHandle> mHandle;
michael@0 1009 nsCString mNewName;
michael@0 1010 nsCOMPtr<CacheFileIOListener> mCallback;
michael@0 1011 nsCOMPtr<nsIEventTarget> mTarget;
michael@0 1012 nsresult mRV;
michael@0 1013 };
michael@0 1014
michael@0 1015 class InitIndexEntryEvent : public nsRunnable {
michael@0 1016 public:
michael@0 1017 InitIndexEntryEvent(CacheFileHandle *aHandle, uint32_t aAppId,
michael@0 1018 bool aAnonymous, bool aInBrowser)
michael@0 1019 : mHandle(aHandle)
michael@0 1020 , mAppId(aAppId)
michael@0 1021 , mAnonymous(aAnonymous)
michael@0 1022 , mInBrowser(aInBrowser)
michael@0 1023 {
michael@0 1024 MOZ_COUNT_CTOR(InitIndexEntryEvent);
michael@0 1025 }
michael@0 1026
michael@0 1027 ~InitIndexEntryEvent()
michael@0 1028 {
michael@0 1029 MOZ_COUNT_DTOR(InitIndexEntryEvent);
michael@0 1030 }
michael@0 1031
michael@0 1032 NS_IMETHOD Run()
michael@0 1033 {
michael@0 1034 if (mHandle->IsClosed() || mHandle->IsDoomed()) {
michael@0 1035 return NS_OK;
michael@0 1036 }
michael@0 1037
michael@0 1038 CacheIndex::InitEntry(mHandle->Hash(), mAppId, mAnonymous, mInBrowser);
michael@0 1039
michael@0 1040 // We cannot set the filesize before we init the entry. If we're opening
michael@0 1041 // an existing entry file, frecency and expiration time will be set after
michael@0 1042 // parsing the entry file, but we must set the filesize here since nobody is
michael@0 1043 // going to set it if there is no write to the file.
michael@0 1044 uint32_t sizeInK = mHandle->FileSizeInK();
michael@0 1045 CacheIndex::UpdateEntry(mHandle->Hash(), nullptr, nullptr, &sizeInK);
michael@0 1046
michael@0 1047 return NS_OK;
michael@0 1048 }
michael@0 1049
michael@0 1050 protected:
michael@0 1051 nsRefPtr<CacheFileHandle> mHandle;
michael@0 1052 uint32_t mAppId;
michael@0 1053 bool mAnonymous;
michael@0 1054 bool mInBrowser;
michael@0 1055 };
michael@0 1056
michael@0 1057 class UpdateIndexEntryEvent : public nsRunnable {
michael@0 1058 public:
michael@0 1059 UpdateIndexEntryEvent(CacheFileHandle *aHandle, const uint32_t *aFrecency,
michael@0 1060 const uint32_t *aExpirationTime)
michael@0 1061 : mHandle(aHandle)
michael@0 1062 , mHasFrecency(false)
michael@0 1063 , mHasExpirationTime(false)
michael@0 1064 {
michael@0 1065 MOZ_COUNT_CTOR(UpdateIndexEntryEvent);
michael@0 1066 if (aFrecency) {
michael@0 1067 mHasFrecency = true;
michael@0 1068 mFrecency = *aFrecency;
michael@0 1069 }
michael@0 1070 if (aExpirationTime) {
michael@0 1071 mHasExpirationTime = true;
michael@0 1072 mExpirationTime = *aExpirationTime;
michael@0 1073 }
michael@0 1074 }
michael@0 1075
michael@0 1076 ~UpdateIndexEntryEvent()
michael@0 1077 {
michael@0 1078 MOZ_COUNT_DTOR(UpdateIndexEntryEvent);
michael@0 1079 }
michael@0 1080
michael@0 1081 NS_IMETHOD Run()
michael@0 1082 {
michael@0 1083 if (mHandle->IsClosed() || mHandle->IsDoomed()) {
michael@0 1084 return NS_OK;
michael@0 1085 }
michael@0 1086
michael@0 1087 CacheIndex::UpdateEntry(mHandle->Hash(),
michael@0 1088 mHasFrecency ? &mFrecency : nullptr,
michael@0 1089 mHasExpirationTime ? &mExpirationTime : nullptr,
michael@0 1090 nullptr);
michael@0 1091 return NS_OK;
michael@0 1092 }
michael@0 1093
michael@0 1094 protected:
michael@0 1095 nsRefPtr<CacheFileHandle> mHandle;
michael@0 1096 bool mHasFrecency;
michael@0 1097 bool mHasExpirationTime;
michael@0 1098 uint32_t mFrecency;
michael@0 1099 uint32_t mExpirationTime;
michael@0 1100 };
michael@0 1101
michael@0 1102 class MetadataWriteScheduleEvent : public nsRunnable
michael@0 1103 {
michael@0 1104 public:
michael@0 1105 enum EMode {
michael@0 1106 SCHEDULE,
michael@0 1107 UNSCHEDULE,
michael@0 1108 SHUTDOWN
michael@0 1109 } mMode;
michael@0 1110
michael@0 1111 nsRefPtr<CacheFile> mFile;
michael@0 1112 nsRefPtr<CacheFileIOManager> mIOMan;
michael@0 1113
michael@0 1114 MetadataWriteScheduleEvent(CacheFileIOManager * aManager,
michael@0 1115 CacheFile * aFile,
michael@0 1116 EMode aMode)
michael@0 1117 : mMode(aMode)
michael@0 1118 , mFile(aFile)
michael@0 1119 , mIOMan(aManager)
michael@0 1120 { }
michael@0 1121
michael@0 1122 virtual ~MetadataWriteScheduleEvent() { }
michael@0 1123
michael@0 1124 NS_IMETHOD Run()
michael@0 1125 {
michael@0 1126 nsRefPtr<CacheFileIOManager> ioMan = CacheFileIOManager::gInstance;
michael@0 1127 if (!ioMan) {
michael@0 1128 NS_WARNING("CacheFileIOManager already gone in MetadataWriteScheduleEvent::Run()");
michael@0 1129 return NS_OK;
michael@0 1130 }
michael@0 1131
michael@0 1132 switch (mMode)
michael@0 1133 {
michael@0 1134 case SCHEDULE:
michael@0 1135 ioMan->ScheduleMetadataWriteInternal(mFile);
michael@0 1136 break;
michael@0 1137 case UNSCHEDULE:
michael@0 1138 ioMan->UnscheduleMetadataWriteInternal(mFile);
michael@0 1139 break;
michael@0 1140 case SHUTDOWN:
michael@0 1141 ioMan->ShutdownMetadataWriteSchedulingInternal();
michael@0 1142 break;
michael@0 1143 }
michael@0 1144 return NS_OK;
michael@0 1145 }
michael@0 1146 };
michael@0 1147
michael@0 1148 CacheFileIOManager * CacheFileIOManager::gInstance = nullptr;
michael@0 1149
michael@0 1150 NS_IMPL_ISUPPORTS(CacheFileIOManager, nsITimerCallback)
michael@0 1151
michael@0 1152 CacheFileIOManager::CacheFileIOManager()
michael@0 1153 : mShuttingDown(false)
michael@0 1154 , mTreeCreated(false)
michael@0 1155 , mOverLimitEvicting(false)
michael@0 1156 , mRemovingTrashDirs(false)
michael@0 1157 {
michael@0 1158 LOG(("CacheFileIOManager::CacheFileIOManager [this=%p]", this));
michael@0 1159 MOZ_COUNT_CTOR(CacheFileIOManager);
michael@0 1160 MOZ_ASSERT(!gInstance, "multiple CacheFileIOManager instances!");
michael@0 1161 }
michael@0 1162
michael@0 1163 CacheFileIOManager::~CacheFileIOManager()
michael@0 1164 {
michael@0 1165 LOG(("CacheFileIOManager::~CacheFileIOManager [this=%p]", this));
michael@0 1166 MOZ_COUNT_DTOR(CacheFileIOManager);
michael@0 1167 }
michael@0 1168
michael@0 1169 // static
michael@0 1170 nsresult
michael@0 1171 CacheFileIOManager::Init()
michael@0 1172 {
michael@0 1173 LOG(("CacheFileIOManager::Init()"));
michael@0 1174
michael@0 1175 MOZ_ASSERT(NS_IsMainThread());
michael@0 1176
michael@0 1177 if (gInstance) {
michael@0 1178 return NS_ERROR_ALREADY_INITIALIZED;
michael@0 1179 }
michael@0 1180
michael@0 1181 nsRefPtr<CacheFileIOManager> ioMan = new CacheFileIOManager();
michael@0 1182
michael@0 1183 nsresult rv = ioMan->InitInternal();
michael@0 1184 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1185
michael@0 1186 ioMan.swap(gInstance);
michael@0 1187 return NS_OK;
michael@0 1188 }
michael@0 1189
michael@0 1190 nsresult
michael@0 1191 CacheFileIOManager::InitInternal()
michael@0 1192 {
michael@0 1193 nsresult rv;
michael@0 1194
michael@0 1195 mIOThread = new CacheIOThread();
michael@0 1196
michael@0 1197 rv = mIOThread->Init();
michael@0 1198 MOZ_ASSERT(NS_SUCCEEDED(rv), "Can't create background thread");
michael@0 1199 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1200
michael@0 1201 mStartTime = TimeStamp::NowLoRes();
michael@0 1202
michael@0 1203 return NS_OK;
michael@0 1204 }
michael@0 1205
michael@0 1206 // static
michael@0 1207 nsresult
michael@0 1208 CacheFileIOManager::Shutdown()
michael@0 1209 {
michael@0 1210 LOG(("CacheFileIOManager::Shutdown() [gInstance=%p]", gInstance));
michael@0 1211
michael@0 1212 MOZ_ASSERT(NS_IsMainThread());
michael@0 1213
michael@0 1214 if (!gInstance) {
michael@0 1215 return NS_ERROR_NOT_INITIALIZED;
michael@0 1216 }
michael@0 1217
michael@0 1218 Telemetry::AutoTimer<Telemetry::NETWORK_DISK_CACHE_SHUTDOWN_V2> shutdownTimer;
michael@0 1219
michael@0 1220 CacheIndex::PreShutdown();
michael@0 1221
michael@0 1222 ShutdownMetadataWriteScheduling();
michael@0 1223
michael@0 1224 {
michael@0 1225 mozilla::Mutex lock("CacheFileIOManager::Shutdown() lock");
michael@0 1226 mozilla::CondVar condVar(lock, "CacheFileIOManager::Shutdown() condVar");
michael@0 1227
michael@0 1228 MutexAutoLock autoLock(lock);
michael@0 1229 nsRefPtr<ShutdownEvent> ev = new ShutdownEvent(&lock, &condVar);
michael@0 1230 DebugOnly<nsresult> rv;
michael@0 1231 rv = gInstance->mIOThread->Dispatch(ev, CacheIOThread::CLOSE);
michael@0 1232 MOZ_ASSERT(NS_SUCCEEDED(rv));
michael@0 1233 condVar.Wait();
michael@0 1234 }
michael@0 1235
michael@0 1236 MOZ_ASSERT(gInstance->mHandles.HandleCount() == 0);
michael@0 1237 MOZ_ASSERT(gInstance->mHandlesByLastUsed.Length() == 0);
michael@0 1238
michael@0 1239 if (gInstance->mIOThread) {
michael@0 1240 gInstance->mIOThread->Shutdown();
michael@0 1241 }
michael@0 1242
michael@0 1243 CacheIndex::Shutdown();
michael@0 1244
michael@0 1245 if (CacheObserver::ClearCacheOnShutdown()) {
michael@0 1246 gInstance->SyncRemoveAllCacheFiles();
michael@0 1247 }
michael@0 1248
michael@0 1249 nsRefPtr<CacheFileIOManager> ioMan;
michael@0 1250 ioMan.swap(gInstance);
michael@0 1251
michael@0 1252 return NS_OK;
michael@0 1253 }
michael@0 1254
michael@0 1255 nsresult
michael@0 1256 CacheFileIOManager::ShutdownInternal()
michael@0 1257 {
michael@0 1258 LOG(("CacheFileIOManager::ShutdownInternal() [this=%p]", this));
michael@0 1259
michael@0 1260 MOZ_ASSERT(mIOThread->IsCurrentThread());
michael@0 1261
michael@0 1262 // No new handles can be created after this flag is set
michael@0 1263 mShuttingDown = true;
michael@0 1264
michael@0 1265 // close all handles and delete all associated files
michael@0 1266 nsTArray<nsRefPtr<CacheFileHandle> > handles;
michael@0 1267 mHandles.GetAllHandles(&handles);
michael@0 1268 handles.AppendElements(mSpecialHandles);
michael@0 1269
michael@0 1270 for (uint32_t i=0 ; i<handles.Length() ; i++) {
michael@0 1271 CacheFileHandle *h = handles[i];
michael@0 1272 h->mClosed = true;
michael@0 1273
michael@0 1274 h->Log();
michael@0 1275
michael@0 1276 // Close file handle
michael@0 1277 if (h->mFD) {
michael@0 1278 ReleaseNSPRHandleInternal(h);
michael@0 1279 }
michael@0 1280
michael@0 1281 // Remove file if entry is doomed or invalid
michael@0 1282 if (h->mFileExists && (h->mIsDoomed || h->mInvalid)) {
michael@0 1283 LOG(("CacheFileIOManager::ShutdownInternal() - Removing file from disk"));
michael@0 1284 h->mFile->Remove(false);
michael@0 1285 }
michael@0 1286
michael@0 1287 if (!h->IsSpecialFile() && !h->mIsDoomed &&
michael@0 1288 (h->mInvalid || !h->mFileExists)) {
michael@0 1289 CacheIndex::RemoveEntry(h->Hash());
michael@0 1290 }
michael@0 1291
michael@0 1292 // Remove the handle from mHandles/mSpecialHandles
michael@0 1293 if (h->IsSpecialFile()) {
michael@0 1294 mSpecialHandles.RemoveElement(h);
michael@0 1295 } else {
michael@0 1296 mHandles.RemoveHandle(h);
michael@0 1297 }
michael@0 1298 }
michael@0 1299
michael@0 1300 // Assert the table is empty. When we are here, no new handles can be added
michael@0 1301 // and handles will no longer remove them self from this table and we don't
michael@0 1302 // want to keep invalid handles here. Also, there is no lookup after this
michael@0 1303 // point to happen.
michael@0 1304 MOZ_ASSERT(mHandles.HandleCount() == 0);
michael@0 1305
michael@0 1306 // Release trash directory enumerator
michael@0 1307 if (mTrashDirEnumerator) {
michael@0 1308 mTrashDirEnumerator->Close();
michael@0 1309 mTrashDirEnumerator = nullptr;
michael@0 1310 }
michael@0 1311
michael@0 1312 return NS_OK;
michael@0 1313 }
michael@0 1314
michael@0 1315 // static
michael@0 1316 nsresult
michael@0 1317 CacheFileIOManager::OnProfile()
michael@0 1318 {
michael@0 1319 LOG(("CacheFileIOManager::OnProfile() [gInstance=%p]", gInstance));
michael@0 1320
michael@0 1321 nsRefPtr<CacheFileIOManager> ioMan = gInstance;
michael@0 1322 if (!ioMan) {
michael@0 1323 // CacheFileIOManager::Init() failed, probably could not create the IO
michael@0 1324 // thread, just go with it...
michael@0 1325 return NS_ERROR_NOT_INITIALIZED;
michael@0 1326 }
michael@0 1327
michael@0 1328 nsresult rv;
michael@0 1329
michael@0 1330 nsCOMPtr<nsIFile> directory;
michael@0 1331
michael@0 1332 CacheObserver::ParentDirOverride(getter_AddRefs(directory));
michael@0 1333
michael@0 1334 #if defined(MOZ_WIDGET_ANDROID)
michael@0 1335 char* cachePath = getenv("CACHE_DIRECTORY");
michael@0 1336 if (!directory && cachePath && *cachePath) {
michael@0 1337 rv = NS_NewNativeLocalFile(nsDependentCString(cachePath),
michael@0 1338 true, getter_AddRefs(directory));
michael@0 1339 }
michael@0 1340 #endif
michael@0 1341
michael@0 1342 if (!directory) {
michael@0 1343 rv = NS_GetSpecialDirectory(NS_APP_CACHE_PARENT_DIR,
michael@0 1344 getter_AddRefs(directory));
michael@0 1345 }
michael@0 1346
michael@0 1347 if (!directory) {
michael@0 1348 rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR,
michael@0 1349 getter_AddRefs(directory));
michael@0 1350 }
michael@0 1351
michael@0 1352 if (directory) {
michael@0 1353 rv = directory->Append(NS_LITERAL_STRING("cache2"));
michael@0 1354 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1355 }
michael@0 1356
michael@0 1357 // All functions return a clone.
michael@0 1358 ioMan->mCacheDirectory.swap(directory);
michael@0 1359
michael@0 1360 if (ioMan->mCacheDirectory) {
michael@0 1361 CacheIndex::Init(ioMan->mCacheDirectory);
michael@0 1362 }
michael@0 1363
michael@0 1364 return NS_OK;
michael@0 1365 }
michael@0 1366
michael@0 1367 // static
michael@0 1368 already_AddRefed<nsIEventTarget>
michael@0 1369 CacheFileIOManager::IOTarget()
michael@0 1370 {
michael@0 1371 nsCOMPtr<nsIEventTarget> target;
michael@0 1372 if (gInstance && gInstance->mIOThread) {
michael@0 1373 target = gInstance->mIOThread->Target();
michael@0 1374 }
michael@0 1375
michael@0 1376 return target.forget();
michael@0 1377 }
michael@0 1378
michael@0 1379 // static
michael@0 1380 already_AddRefed<CacheIOThread>
michael@0 1381 CacheFileIOManager::IOThread()
michael@0 1382 {
michael@0 1383 nsRefPtr<CacheIOThread> thread;
michael@0 1384 if (gInstance) {
michael@0 1385 thread = gInstance->mIOThread;
michael@0 1386 }
michael@0 1387
michael@0 1388 return thread.forget();
michael@0 1389 }
michael@0 1390
michael@0 1391 // static
michael@0 1392 bool
michael@0 1393 CacheFileIOManager::IsOnIOThread()
michael@0 1394 {
michael@0 1395 nsRefPtr<CacheFileIOManager> ioMan = gInstance;
michael@0 1396 if (ioMan && ioMan->mIOThread) {
michael@0 1397 return ioMan->mIOThread->IsCurrentThread();
michael@0 1398 }
michael@0 1399
michael@0 1400 return false;
michael@0 1401 }
michael@0 1402
michael@0 1403 // static
michael@0 1404 bool
michael@0 1405 CacheFileIOManager::IsOnIOThreadOrCeased()
michael@0 1406 {
michael@0 1407 nsRefPtr<CacheFileIOManager> ioMan = gInstance;
michael@0 1408 if (ioMan && ioMan->mIOThread) {
michael@0 1409 return ioMan->mIOThread->IsCurrentThread();
michael@0 1410 }
michael@0 1411
michael@0 1412 // Ceased...
michael@0 1413 return true;
michael@0 1414 }
michael@0 1415
michael@0 1416 // static
michael@0 1417 bool
michael@0 1418 CacheFileIOManager::IsShutdown()
michael@0 1419 {
michael@0 1420 if (!gInstance) {
michael@0 1421 return true;
michael@0 1422 }
michael@0 1423 return gInstance->mShuttingDown;
michael@0 1424 }
michael@0 1425
michael@0 1426 // static
michael@0 1427 nsresult
michael@0 1428 CacheFileIOManager::ScheduleMetadataWrite(CacheFile * aFile)
michael@0 1429 {
michael@0 1430 nsRefPtr<CacheFileIOManager> ioMan = gInstance;
michael@0 1431 NS_ENSURE_TRUE(ioMan, NS_ERROR_NOT_INITIALIZED);
michael@0 1432
michael@0 1433 NS_ENSURE_TRUE(!ioMan->mShuttingDown, NS_ERROR_NOT_INITIALIZED);
michael@0 1434
michael@0 1435 nsRefPtr<MetadataWriteScheduleEvent> event = new MetadataWriteScheduleEvent(
michael@0 1436 ioMan, aFile, MetadataWriteScheduleEvent::SCHEDULE);
michael@0 1437 nsCOMPtr<nsIEventTarget> target = ioMan->IOTarget();
michael@0 1438 NS_ENSURE_TRUE(target, NS_ERROR_UNEXPECTED);
michael@0 1439 return target->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL);
michael@0 1440 }
michael@0 1441
michael@0 1442 nsresult
michael@0 1443 CacheFileIOManager::ScheduleMetadataWriteInternal(CacheFile * aFile)
michael@0 1444 {
michael@0 1445 MOZ_ASSERT(IsOnIOThreadOrCeased());
michael@0 1446
michael@0 1447 nsresult rv;
michael@0 1448
michael@0 1449 if (!mMetadataWritesTimer) {
michael@0 1450 mMetadataWritesTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
michael@0 1451 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1452
michael@0 1453 rv = mMetadataWritesTimer->InitWithCallback(
michael@0 1454 this, kMetadataWriteDelay, nsITimer::TYPE_ONE_SHOT);
michael@0 1455 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1456 }
michael@0 1457
michael@0 1458 if (mScheduledMetadataWrites.IndexOf(aFile) !=
michael@0 1459 mScheduledMetadataWrites.NoIndex) {
michael@0 1460 return NS_OK;
michael@0 1461 }
michael@0 1462
michael@0 1463 mScheduledMetadataWrites.AppendElement(aFile);
michael@0 1464
michael@0 1465 return NS_OK;
michael@0 1466 }
michael@0 1467
michael@0 1468 // static
michael@0 1469 nsresult
michael@0 1470 CacheFileIOManager::UnscheduleMetadataWrite(CacheFile * aFile)
michael@0 1471 {
michael@0 1472 nsRefPtr<CacheFileIOManager> ioMan = gInstance;
michael@0 1473 NS_ENSURE_TRUE(ioMan, NS_ERROR_NOT_INITIALIZED);
michael@0 1474
michael@0 1475 NS_ENSURE_TRUE(!ioMan->mShuttingDown, NS_ERROR_NOT_INITIALIZED);
michael@0 1476
michael@0 1477 nsRefPtr<MetadataWriteScheduleEvent> event = new MetadataWriteScheduleEvent(
michael@0 1478 ioMan, aFile, MetadataWriteScheduleEvent::UNSCHEDULE);
michael@0 1479 nsCOMPtr<nsIEventTarget> target = ioMan->IOTarget();
michael@0 1480 NS_ENSURE_TRUE(target, NS_ERROR_UNEXPECTED);
michael@0 1481 return target->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL);
michael@0 1482 }
michael@0 1483
michael@0 1484 nsresult
michael@0 1485 CacheFileIOManager::UnscheduleMetadataWriteInternal(CacheFile * aFile)
michael@0 1486 {
michael@0 1487 MOZ_ASSERT(IsOnIOThreadOrCeased());
michael@0 1488
michael@0 1489 mScheduledMetadataWrites.RemoveElement(aFile);
michael@0 1490
michael@0 1491 if (mScheduledMetadataWrites.Length() == 0 &&
michael@0 1492 mMetadataWritesTimer) {
michael@0 1493 mMetadataWritesTimer->Cancel();
michael@0 1494 mMetadataWritesTimer = nullptr;
michael@0 1495 }
michael@0 1496
michael@0 1497 return NS_OK;
michael@0 1498 }
michael@0 1499
michael@0 1500 // static
michael@0 1501 nsresult
michael@0 1502 CacheFileIOManager::ShutdownMetadataWriteScheduling()
michael@0 1503 {
michael@0 1504 nsRefPtr<CacheFileIOManager> ioMan = gInstance;
michael@0 1505 NS_ENSURE_TRUE(ioMan, NS_ERROR_NOT_INITIALIZED);
michael@0 1506
michael@0 1507 nsRefPtr<MetadataWriteScheduleEvent> event = new MetadataWriteScheduleEvent(
michael@0 1508 ioMan, nullptr, MetadataWriteScheduleEvent::SHUTDOWN);
michael@0 1509 nsCOMPtr<nsIEventTarget> target = ioMan->IOTarget();
michael@0 1510 NS_ENSURE_TRUE(target, NS_ERROR_UNEXPECTED);
michael@0 1511 return target->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL);
michael@0 1512 }
michael@0 1513
michael@0 1514 nsresult
michael@0 1515 CacheFileIOManager::ShutdownMetadataWriteSchedulingInternal()
michael@0 1516 {
michael@0 1517 MOZ_ASSERT(IsOnIOThreadOrCeased());
michael@0 1518
michael@0 1519 nsTArray<nsRefPtr<CacheFile> > files;
michael@0 1520 files.SwapElements(mScheduledMetadataWrites);
michael@0 1521 for (uint32_t i = 0; i < files.Length(); ++i) {
michael@0 1522 CacheFile * file = files[i];
michael@0 1523 file->WriteMetadataIfNeeded();
michael@0 1524 }
michael@0 1525
michael@0 1526 if (mMetadataWritesTimer) {
michael@0 1527 mMetadataWritesTimer->Cancel();
michael@0 1528 mMetadataWritesTimer = nullptr;
michael@0 1529 }
michael@0 1530
michael@0 1531 return NS_OK;
michael@0 1532 }
michael@0 1533
michael@0 1534 NS_IMETHODIMP
michael@0 1535 CacheFileIOManager::Notify(nsITimer * aTimer)
michael@0 1536 {
michael@0 1537 MOZ_ASSERT(IsOnIOThreadOrCeased());
michael@0 1538 MOZ_ASSERT(mMetadataWritesTimer == aTimer);
michael@0 1539
michael@0 1540 mMetadataWritesTimer = nullptr;
michael@0 1541
michael@0 1542 nsTArray<nsRefPtr<CacheFile> > files;
michael@0 1543 files.SwapElements(mScheduledMetadataWrites);
michael@0 1544 for (uint32_t i = 0; i < files.Length(); ++i) {
michael@0 1545 CacheFile * file = files[i];
michael@0 1546 file->WriteMetadataIfNeeded();
michael@0 1547 }
michael@0 1548
michael@0 1549 return NS_OK;
michael@0 1550 }
michael@0 1551
michael@0 1552 // static
michael@0 1553 nsresult
michael@0 1554 CacheFileIOManager::OpenFile(const nsACString &aKey,
michael@0 1555 uint32_t aFlags, bool aResultOnAnyThread,
michael@0 1556 CacheFileIOListener *aCallback)
michael@0 1557 {
michael@0 1558 LOG(("CacheFileIOManager::OpenFile() [key=%s, flags=%d, listener=%p]",
michael@0 1559 PromiseFlatCString(aKey).get(), aFlags, aCallback));
michael@0 1560
michael@0 1561 nsresult rv;
michael@0 1562 nsRefPtr<CacheFileIOManager> ioMan = gInstance;
michael@0 1563
michael@0 1564 if (!ioMan) {
michael@0 1565 return NS_ERROR_NOT_INITIALIZED;
michael@0 1566 }
michael@0 1567
michael@0 1568 bool priority = aFlags & CacheFileIOManager::PRIORITY;
michael@0 1569 nsRefPtr<OpenFileEvent> ev = new OpenFileEvent(aKey, aFlags, aResultOnAnyThread, aCallback);
michael@0 1570 rv = ioMan->mIOThread->Dispatch(ev, priority
michael@0 1571 ? CacheIOThread::OPEN_PRIORITY
michael@0 1572 : CacheIOThread::OPEN);
michael@0 1573 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1574
michael@0 1575 return NS_OK;
michael@0 1576 }
michael@0 1577
michael@0 1578 nsresult
michael@0 1579 CacheFileIOManager::OpenFileInternal(const SHA1Sum::Hash *aHash,
michael@0 1580 const nsACString &aKey,
michael@0 1581 uint32_t aFlags,
michael@0 1582 CacheFileHandle **_retval)
michael@0 1583 {
michael@0 1584 LOG(("CacheFileIOManager::OpenFileInternal() [hash=%08x%08x%08x%08x%08x, "
michael@0 1585 "key=%s, flags=%d]", LOGSHA1(aHash), PromiseFlatCString(aKey).get(),
michael@0 1586 aFlags));
michael@0 1587
michael@0 1588 MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
michael@0 1589
michael@0 1590 nsresult rv;
michael@0 1591
michael@0 1592 if (mShuttingDown) {
michael@0 1593 return NS_ERROR_NOT_INITIALIZED;
michael@0 1594 }
michael@0 1595
michael@0 1596 if (!mTreeCreated) {
michael@0 1597 rv = CreateCacheTree();
michael@0 1598 if (NS_FAILED(rv)) return rv;
michael@0 1599 }
michael@0 1600
michael@0 1601 nsCOMPtr<nsIFile> file;
michael@0 1602 rv = GetFile(aHash, getter_AddRefs(file));
michael@0 1603 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1604
michael@0 1605 nsRefPtr<CacheFileHandle> handle;
michael@0 1606 mHandles.GetHandle(aHash, false, getter_AddRefs(handle));
michael@0 1607
michael@0 1608 if ((aFlags & (OPEN | CREATE | CREATE_NEW)) == CREATE_NEW) {
michael@0 1609 if (handle) {
michael@0 1610 rv = DoomFileInternal(handle);
michael@0 1611 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1612 handle = nullptr;
michael@0 1613 }
michael@0 1614
michael@0 1615 rv = mHandles.NewHandle(aHash, aFlags & PRIORITY, getter_AddRefs(handle));
michael@0 1616 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1617
michael@0 1618 bool exists;
michael@0 1619 rv = file->Exists(&exists);
michael@0 1620 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1621
michael@0 1622 if (exists) {
michael@0 1623 CacheIndex::RemoveEntry(aHash);
michael@0 1624
michael@0 1625 LOG(("CacheFileIOManager::OpenFileInternal() - Removing old file from "
michael@0 1626 "disk"));
michael@0 1627 rv = file->Remove(false);
michael@0 1628 if (NS_FAILED(rv)) {
michael@0 1629 NS_WARNING("Cannot remove old entry from the disk");
michael@0 1630 LOG(("CacheFileIOManager::OpenFileInternal() - Removing old file failed"
michael@0 1631 ". [rv=0x%08x]", rv));
michael@0 1632 }
michael@0 1633 }
michael@0 1634
michael@0 1635 CacheIndex::AddEntry(aHash);
michael@0 1636 handle->mFile.swap(file);
michael@0 1637 handle->mFileSize = 0;
michael@0 1638 }
michael@0 1639
michael@0 1640 if (handle) {
michael@0 1641 handle.swap(*_retval);
michael@0 1642 return NS_OK;
michael@0 1643 }
michael@0 1644
michael@0 1645 bool exists;
michael@0 1646 rv = file->Exists(&exists);
michael@0 1647 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1648
michael@0 1649 if (exists && mContextEvictor) {
michael@0 1650 if (mContextEvictor->ContextsCount() == 0) {
michael@0 1651 mContextEvictor = nullptr;
michael@0 1652 } else {
michael@0 1653 bool wasEvicted = false;
michael@0 1654 mContextEvictor->WasEvicted(aKey, file, &wasEvicted);
michael@0 1655 if (wasEvicted) {
michael@0 1656 LOG(("CacheFileIOManager::OpenFileInternal() - Removing file since the "
michael@0 1657 "entry was evicted by EvictByContext()"));
michael@0 1658 exists = false;
michael@0 1659 file->Remove(false);
michael@0 1660 CacheIndex::RemoveEntry(aHash);
michael@0 1661 }
michael@0 1662 }
michael@0 1663 }
michael@0 1664
michael@0 1665 if (!exists && (aFlags & (OPEN | CREATE | CREATE_NEW)) == OPEN) {
michael@0 1666 return NS_ERROR_NOT_AVAILABLE;
michael@0 1667 }
michael@0 1668
michael@0 1669 rv = mHandles.NewHandle(aHash, aFlags & PRIORITY, getter_AddRefs(handle));
michael@0 1670 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1671
michael@0 1672 if (exists) {
michael@0 1673 rv = file->GetFileSize(&handle->mFileSize);
michael@0 1674 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1675
michael@0 1676 handle->mFileExists = true;
michael@0 1677
michael@0 1678 CacheIndex::EnsureEntryExists(aHash);
michael@0 1679 } else {
michael@0 1680 handle->mFileSize = 0;
michael@0 1681
michael@0 1682 CacheIndex::AddEntry(aHash);
michael@0 1683 }
michael@0 1684
michael@0 1685 handle->mFile.swap(file);
michael@0 1686 handle.swap(*_retval);
michael@0 1687 return NS_OK;
michael@0 1688 }
michael@0 1689
michael@0 1690 nsresult
michael@0 1691 CacheFileIOManager::OpenSpecialFileInternal(const nsACString &aKey,
michael@0 1692 uint32_t aFlags,
michael@0 1693 CacheFileHandle **_retval)
michael@0 1694 {
michael@0 1695 LOG(("CacheFileIOManager::OpenSpecialFileInternal() [key=%s, flags=%d]",
michael@0 1696 PromiseFlatCString(aKey).get(), aFlags));
michael@0 1697
michael@0 1698 MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
michael@0 1699
michael@0 1700 nsresult rv;
michael@0 1701
michael@0 1702 if (mShuttingDown) {
michael@0 1703 return NS_ERROR_NOT_INITIALIZED;
michael@0 1704 }
michael@0 1705
michael@0 1706 if (!mTreeCreated) {
michael@0 1707 rv = CreateCacheTree();
michael@0 1708 if (NS_FAILED(rv)) return rv;
michael@0 1709 }
michael@0 1710
michael@0 1711 nsCOMPtr<nsIFile> file;
michael@0 1712 rv = GetSpecialFile(aKey, getter_AddRefs(file));
michael@0 1713 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1714
michael@0 1715 nsRefPtr<CacheFileHandle> handle;
michael@0 1716 for (uint32_t i = 0 ; i < mSpecialHandles.Length() ; i++) {
michael@0 1717 if (!mSpecialHandles[i]->IsDoomed() && mSpecialHandles[i]->Key() == aKey) {
michael@0 1718 handle = mSpecialHandles[i];
michael@0 1719 break;
michael@0 1720 }
michael@0 1721 }
michael@0 1722
michael@0 1723 if ((aFlags & (OPEN | CREATE | CREATE_NEW)) == CREATE_NEW) {
michael@0 1724 if (handle) {
michael@0 1725 rv = DoomFileInternal(handle);
michael@0 1726 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1727 handle = nullptr;
michael@0 1728 }
michael@0 1729
michael@0 1730 handle = new CacheFileHandle(aKey, aFlags & PRIORITY);
michael@0 1731 mSpecialHandles.AppendElement(handle);
michael@0 1732
michael@0 1733 bool exists;
michael@0 1734 rv = file->Exists(&exists);
michael@0 1735 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1736
michael@0 1737 if (exists) {
michael@0 1738 LOG(("CacheFileIOManager::OpenSpecialFileInternal() - Removing file from "
michael@0 1739 "disk"));
michael@0 1740 rv = file->Remove(false);
michael@0 1741 if (NS_FAILED(rv)) {
michael@0 1742 NS_WARNING("Cannot remove old entry from the disk");
michael@0 1743 LOG(("CacheFileIOManager::OpenSpecialFileInternal() - Removing file "
michael@0 1744 "failed. [rv=0x%08x]", rv));
michael@0 1745 }
michael@0 1746 }
michael@0 1747
michael@0 1748 handle->mFile.swap(file);
michael@0 1749 handle->mFileSize = 0;
michael@0 1750 }
michael@0 1751
michael@0 1752 if (handle) {
michael@0 1753 handle.swap(*_retval);
michael@0 1754 return NS_OK;
michael@0 1755 }
michael@0 1756
michael@0 1757 bool exists;
michael@0 1758 rv = file->Exists(&exists);
michael@0 1759 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1760
michael@0 1761 if (!exists && (aFlags & (OPEN | CREATE | CREATE_NEW)) == OPEN) {
michael@0 1762 return NS_ERROR_NOT_AVAILABLE;
michael@0 1763 }
michael@0 1764
michael@0 1765 handle = new CacheFileHandle(aKey, aFlags & PRIORITY);
michael@0 1766 mSpecialHandles.AppendElement(handle);
michael@0 1767
michael@0 1768 if (exists) {
michael@0 1769 rv = file->GetFileSize(&handle->mFileSize);
michael@0 1770 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1771
michael@0 1772 handle->mFileExists = true;
michael@0 1773 } else {
michael@0 1774 handle->mFileSize = 0;
michael@0 1775 }
michael@0 1776
michael@0 1777 handle->mFile.swap(file);
michael@0 1778 handle.swap(*_retval);
michael@0 1779 return NS_OK;
michael@0 1780 }
michael@0 1781
michael@0 1782 nsresult
michael@0 1783 CacheFileIOManager::CloseHandleInternal(CacheFileHandle *aHandle)
michael@0 1784 {
michael@0 1785 LOG(("CacheFileIOManager::CloseHandleInternal() [handle=%p]", aHandle));
michael@0 1786 aHandle->Log();
michael@0 1787
michael@0 1788 MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
michael@0 1789
michael@0 1790 // Close file handle
michael@0 1791 if (aHandle->mFD) {
michael@0 1792 ReleaseNSPRHandleInternal(aHandle);
michael@0 1793 }
michael@0 1794
michael@0 1795 // Delete the file if the entry was doomed or invalid
michael@0 1796 if (aHandle->mIsDoomed || aHandle->mInvalid) {
michael@0 1797 LOG(("CacheFileIOManager::CloseHandleInternal() - Removing file from "
michael@0 1798 "disk"));
michael@0 1799 aHandle->mFile->Remove(false);
michael@0 1800 }
michael@0 1801
michael@0 1802 if (!aHandle->IsSpecialFile() && !aHandle->mIsDoomed &&
michael@0 1803 (aHandle->mInvalid || !aHandle->mFileExists)) {
michael@0 1804 CacheIndex::RemoveEntry(aHandle->Hash());
michael@0 1805 }
michael@0 1806
michael@0 1807 // Don't remove handles after shutdown
michael@0 1808 if (!mShuttingDown) {
michael@0 1809 if (aHandle->IsSpecialFile()) {
michael@0 1810 mSpecialHandles.RemoveElement(aHandle);
michael@0 1811 } else {
michael@0 1812 mHandles.RemoveHandle(aHandle);
michael@0 1813 }
michael@0 1814 }
michael@0 1815
michael@0 1816 return NS_OK;
michael@0 1817 }
michael@0 1818
michael@0 1819 // static
michael@0 1820 nsresult
michael@0 1821 CacheFileIOManager::Read(CacheFileHandle *aHandle, int64_t aOffset,
michael@0 1822 char *aBuf, int32_t aCount, bool aResultOnAnyThread,
michael@0 1823 CacheFileIOListener *aCallback)
michael@0 1824 {
michael@0 1825 LOG(("CacheFileIOManager::Read() [handle=%p, offset=%lld, count=%d, "
michael@0 1826 "listener=%p]", aHandle, aOffset, aCount, aCallback));
michael@0 1827
michael@0 1828 nsresult rv;
michael@0 1829 nsRefPtr<CacheFileIOManager> ioMan = gInstance;
michael@0 1830
michael@0 1831 if (aHandle->IsClosed() || !ioMan) {
michael@0 1832 return NS_ERROR_NOT_INITIALIZED;
michael@0 1833 }
michael@0 1834
michael@0 1835 nsRefPtr<ReadEvent> ev = new ReadEvent(aHandle, aOffset, aBuf, aCount,
michael@0 1836 aResultOnAnyThread, aCallback);
michael@0 1837 rv = ioMan->mIOThread->Dispatch(ev, aHandle->IsPriority()
michael@0 1838 ? CacheIOThread::READ_PRIORITY
michael@0 1839 : CacheIOThread::READ);
michael@0 1840 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1841
michael@0 1842 return NS_OK;
michael@0 1843 }
michael@0 1844
michael@0 1845 nsresult
michael@0 1846 CacheFileIOManager::ReadInternal(CacheFileHandle *aHandle, int64_t aOffset,
michael@0 1847 char *aBuf, int32_t aCount)
michael@0 1848 {
michael@0 1849 LOG(("CacheFileIOManager::ReadInternal() [handle=%p, offset=%lld, count=%d]",
michael@0 1850 aHandle, aOffset, aCount));
michael@0 1851
michael@0 1852 nsresult rv;
michael@0 1853
michael@0 1854 if (!aHandle->mFileExists) {
michael@0 1855 NS_WARNING("Trying to read from non-existent file");
michael@0 1856 return NS_ERROR_NOT_AVAILABLE;
michael@0 1857 }
michael@0 1858
michael@0 1859 if (!aHandle->mFD) {
michael@0 1860 rv = OpenNSPRHandle(aHandle);
michael@0 1861 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1862 } else {
michael@0 1863 NSPRHandleUsed(aHandle);
michael@0 1864 }
michael@0 1865
michael@0 1866 // Check again, OpenNSPRHandle could figure out the file was gone.
michael@0 1867 if (!aHandle->mFileExists) {
michael@0 1868 NS_WARNING("Trying to read from non-existent file");
michael@0 1869 return NS_ERROR_NOT_AVAILABLE;
michael@0 1870 }
michael@0 1871
michael@0 1872 int64_t offset = PR_Seek64(aHandle->mFD, aOffset, PR_SEEK_SET);
michael@0 1873 if (offset == -1) {
michael@0 1874 return NS_ERROR_FAILURE;
michael@0 1875 }
michael@0 1876
michael@0 1877 int32_t bytesRead = PR_Read(aHandle->mFD, aBuf, aCount);
michael@0 1878 if (bytesRead != aCount) {
michael@0 1879 return NS_ERROR_FAILURE;
michael@0 1880 }
michael@0 1881
michael@0 1882 return NS_OK;
michael@0 1883 }
michael@0 1884
michael@0 1885 // static
michael@0 1886 nsresult
michael@0 1887 CacheFileIOManager::Write(CacheFileHandle *aHandle, int64_t aOffset,
michael@0 1888 const char *aBuf, int32_t aCount, bool aValidate,
michael@0 1889 CacheFileIOListener *aCallback)
michael@0 1890 {
michael@0 1891 LOG(("CacheFileIOManager::Write() [handle=%p, offset=%lld, count=%d, "
michael@0 1892 "validate=%d, listener=%p]", aHandle, aOffset, aCount, aValidate,
michael@0 1893 aCallback));
michael@0 1894
michael@0 1895 nsresult rv;
michael@0 1896 nsRefPtr<CacheFileIOManager> ioMan = gInstance;
michael@0 1897
michael@0 1898 if (aHandle->IsClosed() || !ioMan) {
michael@0 1899 return NS_ERROR_NOT_INITIALIZED;
michael@0 1900 }
michael@0 1901
michael@0 1902 nsRefPtr<WriteEvent> ev = new WriteEvent(aHandle, aOffset, aBuf, aCount,
michael@0 1903 aValidate, aCallback);
michael@0 1904 rv = ioMan->mIOThread->Dispatch(ev, CacheIOThread::WRITE);
michael@0 1905 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1906
michael@0 1907 return NS_OK;
michael@0 1908 }
michael@0 1909
michael@0 1910 nsresult
michael@0 1911 CacheFileIOManager::WriteInternal(CacheFileHandle *aHandle, int64_t aOffset,
michael@0 1912 const char *aBuf, int32_t aCount,
michael@0 1913 bool aValidate)
michael@0 1914 {
michael@0 1915 LOG(("CacheFileIOManager::WriteInternal() [handle=%p, offset=%lld, count=%d, "
michael@0 1916 "validate=%d]", aHandle, aOffset, aCount, aValidate));
michael@0 1917
michael@0 1918 nsresult rv;
michael@0 1919
michael@0 1920 if (!aHandle->mFileExists) {
michael@0 1921 rv = CreateFile(aHandle);
michael@0 1922 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1923 }
michael@0 1924
michael@0 1925 if (!aHandle->mFD) {
michael@0 1926 rv = OpenNSPRHandle(aHandle);
michael@0 1927 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1928 } else {
michael@0 1929 NSPRHandleUsed(aHandle);
michael@0 1930 }
michael@0 1931
michael@0 1932 // Check again, OpenNSPRHandle could figure out the file was gone.
michael@0 1933 if (!aHandle->mFileExists) {
michael@0 1934 return NS_ERROR_NOT_AVAILABLE;
michael@0 1935 }
michael@0 1936
michael@0 1937 // Write invalidates the entry by default
michael@0 1938 aHandle->mInvalid = true;
michael@0 1939
michael@0 1940 int64_t offset = PR_Seek64(aHandle->mFD, aOffset, PR_SEEK_SET);
michael@0 1941 if (offset == -1) {
michael@0 1942 return NS_ERROR_FAILURE;
michael@0 1943 }
michael@0 1944
michael@0 1945 int32_t bytesWritten = PR_Write(aHandle->mFD, aBuf, aCount);
michael@0 1946
michael@0 1947 if (bytesWritten != -1 && aHandle->mFileSize < aOffset+bytesWritten) {
michael@0 1948 aHandle->mFileSize = aOffset+bytesWritten;
michael@0 1949
michael@0 1950 if (!aHandle->IsDoomed() && !aHandle->IsSpecialFile()) {
michael@0 1951 uint32_t size = aHandle->FileSizeInK();
michael@0 1952 CacheIndex::UpdateEntry(aHandle->Hash(), nullptr, nullptr, &size);
michael@0 1953 EvictIfOverLimitInternal();
michael@0 1954 }
michael@0 1955 }
michael@0 1956
michael@0 1957 if (bytesWritten != aCount) {
michael@0 1958 return NS_ERROR_FAILURE;
michael@0 1959 }
michael@0 1960
michael@0 1961 // Write was successful and this write validates the entry (i.e. metadata)
michael@0 1962 if (aValidate) {
michael@0 1963 aHandle->mInvalid = false;
michael@0 1964 }
michael@0 1965
michael@0 1966 return NS_OK;
michael@0 1967 }
michael@0 1968
michael@0 1969 // static
michael@0 1970 nsresult
michael@0 1971 CacheFileIOManager::DoomFile(CacheFileHandle *aHandle,
michael@0 1972 CacheFileIOListener *aCallback)
michael@0 1973 {
michael@0 1974 LOG(("CacheFileIOManager::DoomFile() [handle=%p, listener=%p]",
michael@0 1975 aHandle, aCallback));
michael@0 1976
michael@0 1977 nsresult rv;
michael@0 1978 nsRefPtr<CacheFileIOManager> ioMan = gInstance;
michael@0 1979
michael@0 1980 if (aHandle->IsClosed() || !ioMan) {
michael@0 1981 return NS_ERROR_NOT_INITIALIZED;
michael@0 1982 }
michael@0 1983
michael@0 1984 nsRefPtr<DoomFileEvent> ev = new DoomFileEvent(aHandle, aCallback);
michael@0 1985 rv = ioMan->mIOThread->Dispatch(ev, aHandle->IsPriority()
michael@0 1986 ? CacheIOThread::OPEN_PRIORITY
michael@0 1987 : CacheIOThread::OPEN);
michael@0 1988 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1989
michael@0 1990 return NS_OK;
michael@0 1991 }
michael@0 1992
michael@0 1993 nsresult
michael@0 1994 CacheFileIOManager::DoomFileInternal(CacheFileHandle *aHandle)
michael@0 1995 {
michael@0 1996 LOG(("CacheFileIOManager::DoomFileInternal() [handle=%p]", aHandle));
michael@0 1997 aHandle->Log();
michael@0 1998
michael@0 1999 nsresult rv;
michael@0 2000
michael@0 2001 if (aHandle->IsDoomed()) {
michael@0 2002 return NS_OK;
michael@0 2003 }
michael@0 2004
michael@0 2005 if (aHandle->mFileExists) {
michael@0 2006 // we need to move the current file to the doomed directory
michael@0 2007 if (aHandle->mFD) {
michael@0 2008 ReleaseNSPRHandleInternal(aHandle);
michael@0 2009 }
michael@0 2010
michael@0 2011 // find unused filename
michael@0 2012 nsCOMPtr<nsIFile> file;
michael@0 2013 rv = GetDoomedFile(getter_AddRefs(file));
michael@0 2014 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2015
michael@0 2016 nsCOMPtr<nsIFile> parentDir;
michael@0 2017 rv = file->GetParent(getter_AddRefs(parentDir));
michael@0 2018 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2019
michael@0 2020 nsAutoCString leafName;
michael@0 2021 rv = file->GetNativeLeafName(leafName);
michael@0 2022 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2023
michael@0 2024 rv = aHandle->mFile->MoveToNative(parentDir, leafName);
michael@0 2025 if (NS_ERROR_FILE_NOT_FOUND == rv || NS_ERROR_FILE_TARGET_DOES_NOT_EXIST == rv) {
michael@0 2026 LOG((" file already removed under our hands"));
michael@0 2027 aHandle->mFileExists = false;
michael@0 2028 rv = NS_OK;
michael@0 2029 } else {
michael@0 2030 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2031 aHandle->mFile.swap(file);
michael@0 2032 }
michael@0 2033 }
michael@0 2034
michael@0 2035 if (!aHandle->IsSpecialFile()) {
michael@0 2036 CacheIndex::RemoveEntry(aHandle->Hash());
michael@0 2037 }
michael@0 2038
michael@0 2039 aHandle->mIsDoomed = true;
michael@0 2040
michael@0 2041 if (!aHandle->IsSpecialFile()) {
michael@0 2042 nsRefPtr<CacheStorageService> storageService = CacheStorageService::Self();
michael@0 2043 if (storageService) {
michael@0 2044 nsAutoCString idExtension, url;
michael@0 2045 nsCOMPtr<nsILoadContextInfo> info =
michael@0 2046 CacheFileUtils::ParseKey(aHandle->Key(), &idExtension, &url);
michael@0 2047 MOZ_ASSERT(info);
michael@0 2048 if (info) {
michael@0 2049 storageService->CacheFileDoomed(info, idExtension, url);
michael@0 2050 }
michael@0 2051 }
michael@0 2052 }
michael@0 2053
michael@0 2054 return NS_OK;
michael@0 2055 }
michael@0 2056
michael@0 2057 // static
michael@0 2058 nsresult
michael@0 2059 CacheFileIOManager::DoomFileByKey(const nsACString &aKey,
michael@0 2060 CacheFileIOListener *aCallback)
michael@0 2061 {
michael@0 2062 LOG(("CacheFileIOManager::DoomFileByKey() [key=%s, listener=%p]",
michael@0 2063 PromiseFlatCString(aKey).get(), aCallback));
michael@0 2064
michael@0 2065 nsresult rv;
michael@0 2066 nsRefPtr<CacheFileIOManager> ioMan = gInstance;
michael@0 2067
michael@0 2068 if (!ioMan) {
michael@0 2069 return NS_ERROR_NOT_INITIALIZED;
michael@0 2070 }
michael@0 2071
michael@0 2072 nsRefPtr<DoomFileByKeyEvent> ev = new DoomFileByKeyEvent(aKey, aCallback);
michael@0 2073 rv = ioMan->mIOThread->DispatchAfterPendingOpens(ev);
michael@0 2074 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2075
michael@0 2076 return NS_OK;
michael@0 2077 }
michael@0 2078
michael@0 2079 nsresult
michael@0 2080 CacheFileIOManager::DoomFileByKeyInternal(const SHA1Sum::Hash *aHash)
michael@0 2081 {
michael@0 2082 LOG(("CacheFileIOManager::DoomFileByKeyInternal() [hash=%08x%08x%08x%08x%08x]"
michael@0 2083 , LOGSHA1(aHash)));
michael@0 2084
michael@0 2085 MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
michael@0 2086
michael@0 2087 nsresult rv;
michael@0 2088
michael@0 2089 if (mShuttingDown) {
michael@0 2090 return NS_ERROR_NOT_INITIALIZED;
michael@0 2091 }
michael@0 2092
michael@0 2093 if (!mCacheDirectory) {
michael@0 2094 return NS_ERROR_FILE_INVALID_PATH;
michael@0 2095 }
michael@0 2096
michael@0 2097 // Find active handle
michael@0 2098 nsRefPtr<CacheFileHandle> handle;
michael@0 2099 mHandles.GetHandle(aHash, true, getter_AddRefs(handle));
michael@0 2100
michael@0 2101 if (handle) {
michael@0 2102 handle->Log();
michael@0 2103
michael@0 2104 if (handle->IsDoomed()) {
michael@0 2105 return NS_OK;
michael@0 2106 }
michael@0 2107
michael@0 2108 return DoomFileInternal(handle);
michael@0 2109 }
michael@0 2110
michael@0 2111 // There is no handle for this file, delete the file if exists
michael@0 2112 nsCOMPtr<nsIFile> file;
michael@0 2113 rv = GetFile(aHash, getter_AddRefs(file));
michael@0 2114 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2115
michael@0 2116 bool exists;
michael@0 2117 rv = file->Exists(&exists);
michael@0 2118 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2119
michael@0 2120 if (!exists) {
michael@0 2121 return NS_ERROR_NOT_AVAILABLE;
michael@0 2122 }
michael@0 2123
michael@0 2124 LOG(("CacheFileIOManager::DoomFileByKeyInternal() - Removing file from "
michael@0 2125 "disk"));
michael@0 2126 rv = file->Remove(false);
michael@0 2127 if (NS_FAILED(rv)) {
michael@0 2128 NS_WARNING("Cannot remove old entry from the disk");
michael@0 2129 LOG(("CacheFileIOManager::DoomFileByKeyInternal() - Removing file failed. "
michael@0 2130 "[rv=0x%08x]", rv));
michael@0 2131 }
michael@0 2132
michael@0 2133 CacheIndex::RemoveEntry(aHash);
michael@0 2134
michael@0 2135 return NS_OK;
michael@0 2136 }
michael@0 2137
michael@0 2138 // static
michael@0 2139 nsresult
michael@0 2140 CacheFileIOManager::ReleaseNSPRHandle(CacheFileHandle *aHandle)
michael@0 2141 {
michael@0 2142 LOG(("CacheFileIOManager::ReleaseNSPRHandle() [handle=%p]", aHandle));
michael@0 2143
michael@0 2144 nsresult rv;
michael@0 2145 nsRefPtr<CacheFileIOManager> ioMan = gInstance;
michael@0 2146
michael@0 2147 if (aHandle->IsClosed() || !ioMan) {
michael@0 2148 return NS_ERROR_NOT_INITIALIZED;
michael@0 2149 }
michael@0 2150
michael@0 2151 nsRefPtr<ReleaseNSPRHandleEvent> ev = new ReleaseNSPRHandleEvent(aHandle);
michael@0 2152 rv = ioMan->mIOThread->Dispatch(ev, CacheIOThread::CLOSE);
michael@0 2153 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2154
michael@0 2155 return NS_OK;
michael@0 2156 }
michael@0 2157
michael@0 2158 nsresult
michael@0 2159 CacheFileIOManager::ReleaseNSPRHandleInternal(CacheFileHandle *aHandle)
michael@0 2160 {
michael@0 2161 LOG(("CacheFileIOManager::ReleaseNSPRHandleInternal() [handle=%p]", aHandle));
michael@0 2162
michael@0 2163 MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
michael@0 2164 MOZ_ASSERT(aHandle->mFD);
michael@0 2165
michael@0 2166 DebugOnly<bool> found;
michael@0 2167 found = mHandlesByLastUsed.RemoveElement(aHandle);
michael@0 2168 MOZ_ASSERT(found);
michael@0 2169
michael@0 2170 PR_Close(aHandle->mFD);
michael@0 2171 aHandle->mFD = nullptr;
michael@0 2172
michael@0 2173 return NS_OK;
michael@0 2174 }
michael@0 2175
michael@0 2176 // static
michael@0 2177 nsresult
michael@0 2178 CacheFileIOManager::TruncateSeekSetEOF(CacheFileHandle *aHandle,
michael@0 2179 int64_t aTruncatePos, int64_t aEOFPos,
michael@0 2180 CacheFileIOListener *aCallback)
michael@0 2181 {
michael@0 2182 LOG(("CacheFileIOManager::TruncateSeekSetEOF() [handle=%p, truncatePos=%lld, "
michael@0 2183 "EOFPos=%lld, listener=%p]", aHandle, aTruncatePos, aEOFPos, aCallback));
michael@0 2184
michael@0 2185 nsresult rv;
michael@0 2186 nsRefPtr<CacheFileIOManager> ioMan = gInstance;
michael@0 2187
michael@0 2188 if (aHandle->IsClosed() || !ioMan) {
michael@0 2189 return NS_ERROR_NOT_INITIALIZED;
michael@0 2190 }
michael@0 2191
michael@0 2192 nsRefPtr<TruncateSeekSetEOFEvent> ev = new TruncateSeekSetEOFEvent(
michael@0 2193 aHandle, aTruncatePos, aEOFPos,
michael@0 2194 aCallback);
michael@0 2195 rv = ioMan->mIOThread->Dispatch(ev, CacheIOThread::WRITE);
michael@0 2196 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2197
michael@0 2198 return NS_OK;
michael@0 2199 }
michael@0 2200
michael@0 2201 // static
michael@0 2202 void CacheFileIOManager::GetCacheDirectory(nsIFile** result)
michael@0 2203 {
michael@0 2204 *result = nullptr;
michael@0 2205
michael@0 2206 nsRefPtr<CacheFileIOManager> ioMan = gInstance;
michael@0 2207 if (!ioMan) {
michael@0 2208 return;
michael@0 2209 }
michael@0 2210
michael@0 2211 nsCOMPtr<nsIFile> file = ioMan->mCacheDirectory;
michael@0 2212 file.forget(result);
michael@0 2213 }
michael@0 2214
michael@0 2215 static nsresult
michael@0 2216 TruncFile(PRFileDesc *aFD, uint32_t aEOF)
michael@0 2217 {
michael@0 2218 #if defined(XP_UNIX)
michael@0 2219 if (ftruncate(PR_FileDesc2NativeHandle(aFD), aEOF) != 0) {
michael@0 2220 NS_ERROR("ftruncate failed");
michael@0 2221 return NS_ERROR_FAILURE;
michael@0 2222 }
michael@0 2223 #elif defined(XP_WIN)
michael@0 2224 int32_t cnt = PR_Seek(aFD, aEOF, PR_SEEK_SET);
michael@0 2225 if (cnt == -1) {
michael@0 2226 return NS_ERROR_FAILURE;
michael@0 2227 }
michael@0 2228 if (!SetEndOfFile((HANDLE) PR_FileDesc2NativeHandle(aFD))) {
michael@0 2229 NS_ERROR("SetEndOfFile failed");
michael@0 2230 return NS_ERROR_FAILURE;
michael@0 2231 }
michael@0 2232 #else
michael@0 2233 MOZ_ASSERT(false, "Not implemented!");
michael@0 2234 #endif
michael@0 2235
michael@0 2236 return NS_OK;
michael@0 2237 }
michael@0 2238
michael@0 2239 nsresult
michael@0 2240 CacheFileIOManager::TruncateSeekSetEOFInternal(CacheFileHandle *aHandle,
michael@0 2241 int64_t aTruncatePos,
michael@0 2242 int64_t aEOFPos)
michael@0 2243 {
michael@0 2244 LOG(("CacheFileIOManager::TruncateSeekSetEOFInternal() [handle=%p, "
michael@0 2245 "truncatePos=%lld, EOFPos=%lld]", aHandle, aTruncatePos, aEOFPos));
michael@0 2246
michael@0 2247 nsresult rv;
michael@0 2248
michael@0 2249 if (!aHandle->mFileExists) {
michael@0 2250 rv = CreateFile(aHandle);
michael@0 2251 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2252 }
michael@0 2253
michael@0 2254 if (!aHandle->mFD) {
michael@0 2255 rv = OpenNSPRHandle(aHandle);
michael@0 2256 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2257 } else {
michael@0 2258 NSPRHandleUsed(aHandle);
michael@0 2259 }
michael@0 2260
michael@0 2261 // Check again, OpenNSPRHandle could figure out the file was gone.
michael@0 2262 if (!aHandle->mFileExists) {
michael@0 2263 return NS_ERROR_NOT_AVAILABLE;
michael@0 2264 }
michael@0 2265
michael@0 2266 // This operation always invalidates the entry
michael@0 2267 aHandle->mInvalid = true;
michael@0 2268
michael@0 2269 rv = TruncFile(aHandle->mFD, static_cast<uint32_t>(aTruncatePos));
michael@0 2270 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2271
michael@0 2272 rv = TruncFile(aHandle->mFD, static_cast<uint32_t>(aEOFPos));
michael@0 2273 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2274
michael@0 2275 return NS_OK;
michael@0 2276 }
michael@0 2277
michael@0 2278 // static
michael@0 2279 nsresult
michael@0 2280 CacheFileIOManager::RenameFile(CacheFileHandle *aHandle,
michael@0 2281 const nsACString &aNewName,
michael@0 2282 CacheFileIOListener *aCallback)
michael@0 2283 {
michael@0 2284 LOG(("CacheFileIOManager::RenameFile() [handle=%p, newName=%s, listener=%p]",
michael@0 2285 aHandle, PromiseFlatCString(aNewName).get(), aCallback));
michael@0 2286
michael@0 2287 nsresult rv;
michael@0 2288 nsRefPtr<CacheFileIOManager> ioMan = gInstance;
michael@0 2289
michael@0 2290 if (aHandle->IsClosed() || !ioMan) {
michael@0 2291 return NS_ERROR_NOT_INITIALIZED;
michael@0 2292 }
michael@0 2293
michael@0 2294 if (!aHandle->IsSpecialFile()) {
michael@0 2295 return NS_ERROR_UNEXPECTED;
michael@0 2296 }
michael@0 2297
michael@0 2298 nsRefPtr<RenameFileEvent> ev = new RenameFileEvent(aHandle, aNewName,
michael@0 2299 aCallback);
michael@0 2300 rv = ioMan->mIOThread->Dispatch(ev, CacheIOThread::WRITE);
michael@0 2301 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2302
michael@0 2303 return NS_OK;
michael@0 2304 }
michael@0 2305
michael@0 2306 nsresult
michael@0 2307 CacheFileIOManager::RenameFileInternal(CacheFileHandle *aHandle,
michael@0 2308 const nsACString &aNewName)
michael@0 2309 {
michael@0 2310 LOG(("CacheFileIOManager::RenameFileInternal() [handle=%p, newName=%s]",
michael@0 2311 aHandle, PromiseFlatCString(aNewName).get()));
michael@0 2312
michael@0 2313 nsresult rv;
michael@0 2314
michael@0 2315 MOZ_ASSERT(aHandle->IsSpecialFile());
michael@0 2316
michael@0 2317 if (aHandle->IsDoomed()) {
michael@0 2318 return NS_ERROR_NOT_AVAILABLE;
michael@0 2319 }
michael@0 2320
michael@0 2321 // Doom old handle if it exists and is not doomed
michael@0 2322 for (uint32_t i = 0 ; i < mSpecialHandles.Length() ; i++) {
michael@0 2323 if (!mSpecialHandles[i]->IsDoomed() &&
michael@0 2324 mSpecialHandles[i]->Key() == aNewName) {
michael@0 2325 MOZ_ASSERT(aHandle != mSpecialHandles[i]);
michael@0 2326 rv = DoomFileInternal(mSpecialHandles[i]);
michael@0 2327 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2328 break;
michael@0 2329 }
michael@0 2330 }
michael@0 2331
michael@0 2332 nsCOMPtr<nsIFile> file;
michael@0 2333 rv = GetSpecialFile(aNewName, getter_AddRefs(file));
michael@0 2334 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2335
michael@0 2336 bool exists;
michael@0 2337 rv = file->Exists(&exists);
michael@0 2338 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2339
michael@0 2340 if (exists) {
michael@0 2341 LOG(("CacheFileIOManager::RenameFileInternal() - Removing old file from "
michael@0 2342 "disk"));
michael@0 2343 rv = file->Remove(false);
michael@0 2344 if (NS_FAILED(rv)) {
michael@0 2345 NS_WARNING("Cannot remove file from the disk");
michael@0 2346 LOG(("CacheFileIOManager::RenameFileInternal() - Removing old file failed"
michael@0 2347 ". [rv=0x%08x]", rv));
michael@0 2348 }
michael@0 2349 }
michael@0 2350
michael@0 2351 if (!aHandle->FileExists()) {
michael@0 2352 aHandle->mKey = aNewName;
michael@0 2353 return NS_OK;
michael@0 2354 }
michael@0 2355
michael@0 2356 if (aHandle->mFD) {
michael@0 2357 ReleaseNSPRHandleInternal(aHandle);
michael@0 2358 }
michael@0 2359
michael@0 2360 rv = aHandle->mFile->MoveToNative(nullptr, aNewName);
michael@0 2361 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2362
michael@0 2363 aHandle->mKey = aNewName;
michael@0 2364 return NS_OK;
michael@0 2365 }
michael@0 2366
michael@0 2367 // static
michael@0 2368 nsresult
michael@0 2369 CacheFileIOManager::EvictIfOverLimit()
michael@0 2370 {
michael@0 2371 LOG(("CacheFileIOManager::EvictIfOverLimit()"));
michael@0 2372
michael@0 2373 nsresult rv;
michael@0 2374 nsRefPtr<CacheFileIOManager> ioMan = gInstance;
michael@0 2375
michael@0 2376 if (!ioMan) {
michael@0 2377 return NS_ERROR_NOT_INITIALIZED;
michael@0 2378 }
michael@0 2379
michael@0 2380 nsCOMPtr<nsIRunnable> ev;
michael@0 2381 ev = NS_NewRunnableMethod(ioMan,
michael@0 2382 &CacheFileIOManager::EvictIfOverLimitInternal);
michael@0 2383
michael@0 2384 rv = ioMan->mIOThread->Dispatch(ev, CacheIOThread::EVICT);
michael@0 2385 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2386
michael@0 2387 return NS_OK;
michael@0 2388 }
michael@0 2389
michael@0 2390 nsresult
michael@0 2391 CacheFileIOManager::EvictIfOverLimitInternal()
michael@0 2392 {
michael@0 2393 LOG(("CacheFileIOManager::EvictIfOverLimitInternal()"));
michael@0 2394
michael@0 2395 nsresult rv;
michael@0 2396
michael@0 2397 MOZ_ASSERT(mIOThread->IsCurrentThread());
michael@0 2398
michael@0 2399 if (mShuttingDown) {
michael@0 2400 return NS_ERROR_NOT_INITIALIZED;
michael@0 2401 }
michael@0 2402
michael@0 2403 if (mOverLimitEvicting) {
michael@0 2404 LOG(("CacheFileIOManager::EvictIfOverLimitInternal() - Eviction already "
michael@0 2405 "running."));
michael@0 2406 return NS_OK;
michael@0 2407 }
michael@0 2408
michael@0 2409 UpdateSmartCacheSize();
michael@0 2410
michael@0 2411 uint32_t cacheUsage;
michael@0 2412 rv = CacheIndex::GetCacheSize(&cacheUsage);
michael@0 2413 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2414
michael@0 2415 uint32_t cacheLimit = CacheObserver::DiskCacheCapacity() >> 10;
michael@0 2416 if (cacheUsage <= cacheLimit) {
michael@0 2417 LOG(("CacheFileIOManager::EvictIfOverLimitInternal() - Cache size under "
michael@0 2418 "limit. [cacheSize=%u, limit=%u]", cacheUsage, cacheLimit));
michael@0 2419 return NS_OK;
michael@0 2420 }
michael@0 2421
michael@0 2422 LOG(("CacheFileIOManager::EvictIfOverLimitInternal() - Cache size exceeded "
michael@0 2423 "limit. Starting overlimit eviction. [cacheSize=%u, limit=%u]",
michael@0 2424 cacheUsage, cacheLimit));
michael@0 2425
michael@0 2426 nsCOMPtr<nsIRunnable> ev;
michael@0 2427 ev = NS_NewRunnableMethod(this,
michael@0 2428 &CacheFileIOManager::OverLimitEvictionInternal);
michael@0 2429
michael@0 2430 rv = mIOThread->Dispatch(ev, CacheIOThread::EVICT);
michael@0 2431 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2432
michael@0 2433 mOverLimitEvicting = true;
michael@0 2434 return NS_OK;
michael@0 2435 }
michael@0 2436
michael@0 2437 nsresult
michael@0 2438 CacheFileIOManager::OverLimitEvictionInternal()
michael@0 2439 {
michael@0 2440 LOG(("CacheFileIOManager::OverLimitEvictionInternal()"));
michael@0 2441
michael@0 2442 nsresult rv;
michael@0 2443
michael@0 2444 MOZ_ASSERT(mIOThread->IsCurrentThread());
michael@0 2445
michael@0 2446 // mOverLimitEvicting is accessed only on IO thread, so we can set it to false
michael@0 2447 // here and set it to true again once we dispatch another event that will
michael@0 2448 // continue with the eviction. The reason why we do so is that we can fail
michael@0 2449 // early anywhere in this method and the variable will contain a correct
michael@0 2450 // value. Otherwise we would need to set it to false on every failing place.
michael@0 2451 mOverLimitEvicting = false;
michael@0 2452
michael@0 2453 if (mShuttingDown) {
michael@0 2454 return NS_ERROR_NOT_INITIALIZED;
michael@0 2455 }
michael@0 2456
michael@0 2457 UpdateSmartCacheSize();
michael@0 2458
michael@0 2459 while (true) {
michael@0 2460 uint32_t cacheUsage;
michael@0 2461 rv = CacheIndex::GetCacheSize(&cacheUsage);
michael@0 2462 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2463
michael@0 2464 uint32_t cacheLimit = CacheObserver::DiskCacheCapacity() >> 10;
michael@0 2465 if (cacheUsage <= cacheLimit) {
michael@0 2466 LOG(("CacheFileIOManager::OverLimitEvictionInternal() - Cache size under "
michael@0 2467 "limit. [cacheSize=%u, limit=%u]", cacheUsage, cacheLimit));
michael@0 2468 return NS_OK;
michael@0 2469 }
michael@0 2470
michael@0 2471 LOG(("CacheFileIOManager::OverLimitEvictionInternal() - Cache size over "
michael@0 2472 "limit. [cacheSize=%u, limit=%u]", cacheUsage, cacheLimit));
michael@0 2473
michael@0 2474 if (CacheIOThread::YieldAndRerun()) {
michael@0 2475 LOG(("CacheFileIOManager::OverLimitEvictionInternal() - Breaking loop "
michael@0 2476 "for higher level events."));
michael@0 2477 mOverLimitEvicting = true;
michael@0 2478 return NS_OK;
michael@0 2479 }
michael@0 2480
michael@0 2481 SHA1Sum::Hash hash;
michael@0 2482 uint32_t cnt;
michael@0 2483 static uint32_t consecutiveFailures = 0;
michael@0 2484 rv = CacheIndex::GetEntryForEviction(&hash, &cnt);
michael@0 2485 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2486
michael@0 2487 rv = DoomFileByKeyInternal(&hash);
michael@0 2488 if (NS_SUCCEEDED(rv)) {
michael@0 2489 consecutiveFailures = 0;
michael@0 2490 } else if (rv == NS_ERROR_NOT_AVAILABLE) {
michael@0 2491 LOG(("CacheFileIOManager::OverLimitEvictionInternal() - "
michael@0 2492 "DoomFileByKeyInternal() failed. [rv=0x%08x]", rv));
michael@0 2493 // TODO index is outdated, start update
michael@0 2494
michael@0 2495 // Make sure index won't return the same entry again
michael@0 2496 CacheIndex::RemoveEntry(&hash);
michael@0 2497 consecutiveFailures = 0;
michael@0 2498 } else {
michael@0 2499 // This shouldn't normally happen, but the eviction must not fail
michael@0 2500 // completely if we ever encounter this problem.
michael@0 2501 NS_WARNING("CacheFileIOManager::OverLimitEvictionInternal() - Unexpected "
michael@0 2502 "failure of DoomFileByKeyInternal()");
michael@0 2503
michael@0 2504 LOG(("CacheFileIOManager::OverLimitEvictionInternal() - "
michael@0 2505 "DoomFileByKeyInternal() failed. [rv=0x%08x]", rv));
michael@0 2506
michael@0 2507 // Normally, CacheIndex::UpdateEntry() is called only to update newly
michael@0 2508 // created/opened entries which are always fresh and UpdateEntry() expects
michael@0 2509 // and checks this flag. The way we use UpdateEntry() here is a kind of
michael@0 2510 // hack and we must make sure the flag is set by calling
michael@0 2511 // EnsureEntryExists().
michael@0 2512 rv = CacheIndex::EnsureEntryExists(&hash);
michael@0 2513 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2514
michael@0 2515 // Move the entry at the end of both lists to make sure we won't end up
michael@0 2516 // failing on one entry forever.
michael@0 2517 uint32_t frecency = 0;
michael@0 2518 uint32_t expTime = nsICacheEntry::NO_EXPIRATION_TIME;
michael@0 2519 rv = CacheIndex::UpdateEntry(&hash, &frecency, &expTime, nullptr);
michael@0 2520 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2521
michael@0 2522 consecutiveFailures++;
michael@0 2523 if (consecutiveFailures >= cnt) {
michael@0 2524 // This doesn't necessarily mean that we've tried to doom every entry
michael@0 2525 // but we've reached a sane number of tries. It is likely that another
michael@0 2526 // eviction will start soon. And as said earlier, this normally doesn't
michael@0 2527 // happen at all.
michael@0 2528 return NS_OK;
michael@0 2529 }
michael@0 2530 }
michael@0 2531 }
michael@0 2532
michael@0 2533 NS_NOTREACHED("We should never get here");
michael@0 2534 return NS_OK;
michael@0 2535 }
michael@0 2536
michael@0 2537 // static
michael@0 2538 nsresult
michael@0 2539 CacheFileIOManager::EvictAll()
michael@0 2540 {
michael@0 2541 LOG(("CacheFileIOManager::EvictAll()"));
michael@0 2542
michael@0 2543 nsresult rv;
michael@0 2544 nsRefPtr<CacheFileIOManager> ioMan = gInstance;
michael@0 2545
michael@0 2546 if (!ioMan) {
michael@0 2547 return NS_ERROR_NOT_INITIALIZED;
michael@0 2548 }
michael@0 2549
michael@0 2550 nsCOMPtr<nsIRunnable> ev;
michael@0 2551 ev = NS_NewRunnableMethod(ioMan, &CacheFileIOManager::EvictAllInternal);
michael@0 2552
michael@0 2553 rv = ioMan->mIOThread->DispatchAfterPendingOpens(ev);
michael@0 2554 if (NS_WARN_IF(NS_FAILED(rv))) {
michael@0 2555 return rv;
michael@0 2556 }
michael@0 2557
michael@0 2558 return NS_OK;
michael@0 2559 }
michael@0 2560
michael@0 2561 namespace {
michael@0 2562
michael@0 2563 class EvictionNotifierRunnable : public nsRunnable
michael@0 2564 {
michael@0 2565 public:
michael@0 2566 NS_DECL_NSIRUNNABLE
michael@0 2567 };
michael@0 2568
michael@0 2569 NS_IMETHODIMP
michael@0 2570 EvictionNotifierRunnable::Run()
michael@0 2571 {
michael@0 2572 nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService();
michael@0 2573 if (obsSvc) {
michael@0 2574 obsSvc->NotifyObservers(nullptr, "cacheservice:empty-cache", nullptr);
michael@0 2575 }
michael@0 2576 return NS_OK;
michael@0 2577 }
michael@0 2578
michael@0 2579 } // anonymous namespace
michael@0 2580
michael@0 2581 nsresult
michael@0 2582 CacheFileIOManager::EvictAllInternal()
michael@0 2583 {
michael@0 2584 LOG(("CacheFileIOManager::EvictAllInternal()"));
michael@0 2585
michael@0 2586 nsresult rv;
michael@0 2587
michael@0 2588 MOZ_ASSERT(mIOThread->IsCurrentThread());
michael@0 2589
michael@0 2590 nsRefPtr<EvictionNotifierRunnable> r = new EvictionNotifierRunnable();
michael@0 2591
michael@0 2592 if (!mCacheDirectory) {
michael@0 2593 // This is a kind of hack. Somebody called EvictAll() without a profile.
michael@0 2594 // This happens in xpcshell tests that use cache without profile. We need
michael@0 2595 // to notify observers in this case since the tests are waiting for it.
michael@0 2596 NS_DispatchToMainThread(r);
michael@0 2597 return NS_ERROR_FILE_INVALID_PATH;
michael@0 2598 }
michael@0 2599
michael@0 2600 if (mShuttingDown) {
michael@0 2601 return NS_ERROR_NOT_INITIALIZED;
michael@0 2602 }
michael@0 2603
michael@0 2604 if (!mTreeCreated) {
michael@0 2605 rv = CreateCacheTree();
michael@0 2606 if (NS_FAILED(rv)) {
michael@0 2607 return rv;
michael@0 2608 }
michael@0 2609 }
michael@0 2610
michael@0 2611 // Doom all active handles
michael@0 2612 nsTArray<nsRefPtr<CacheFileHandle> > handles;
michael@0 2613 mHandles.GetActiveHandles(&handles);
michael@0 2614
michael@0 2615 for (uint32_t i = 0; i < handles.Length(); ++i) {
michael@0 2616 rv = DoomFileInternal(handles[i]);
michael@0 2617 if (NS_WARN_IF(NS_FAILED(rv))) {
michael@0 2618 LOG(("CacheFileIOManager::EvictAllInternal() - Cannot doom handle "
michael@0 2619 "[handle=%p]", handles[i].get()));
michael@0 2620 }
michael@0 2621 }
michael@0 2622
michael@0 2623 nsCOMPtr<nsIFile> file;
michael@0 2624 rv = mCacheDirectory->Clone(getter_AddRefs(file));
michael@0 2625 if (NS_WARN_IF(NS_FAILED(rv))) {
michael@0 2626 return rv;
michael@0 2627 }
michael@0 2628
michael@0 2629 rv = file->AppendNative(NS_LITERAL_CSTRING(kEntriesDir));
michael@0 2630 if (NS_WARN_IF(NS_FAILED(rv))) {
michael@0 2631 return rv;
michael@0 2632 }
michael@0 2633
michael@0 2634 // Trash current entries directory
michael@0 2635 rv = TrashDirectory(file);
michael@0 2636 if (NS_WARN_IF(NS_FAILED(rv))) {
michael@0 2637 return rv;
michael@0 2638 }
michael@0 2639
michael@0 2640 // Files are now inaccessible in entries directory, notify observers.
michael@0 2641 NS_DispatchToMainThread(r);
michael@0 2642
michael@0 2643 // Create a new empty entries directory
michael@0 2644 rv = CheckAndCreateDir(mCacheDirectory, kEntriesDir, false);
michael@0 2645 if (NS_WARN_IF(NS_FAILED(rv))) {
michael@0 2646 return rv;
michael@0 2647 }
michael@0 2648
michael@0 2649 CacheIndex::RemoveAll();
michael@0 2650
michael@0 2651 return NS_OK;
michael@0 2652 }
michael@0 2653
michael@0 2654 // static
michael@0 2655 nsresult
michael@0 2656 CacheFileIOManager::EvictByContext(nsILoadContextInfo *aLoadContextInfo)
michael@0 2657 {
michael@0 2658 LOG(("CacheFileIOManager::EvictByContext() [loadContextInfo=%p]",
michael@0 2659 aLoadContextInfo));
michael@0 2660
michael@0 2661 nsresult rv;
michael@0 2662 nsRefPtr<CacheFileIOManager> ioMan = gInstance;
michael@0 2663
michael@0 2664 if (!ioMan) {
michael@0 2665 return NS_ERROR_NOT_INITIALIZED;
michael@0 2666 }
michael@0 2667
michael@0 2668 nsCOMPtr<nsIRunnable> ev;
michael@0 2669 ev = NS_NewRunnableMethodWithArg<nsCOMPtr<nsILoadContextInfo> >
michael@0 2670 (ioMan, &CacheFileIOManager::EvictByContextInternal, aLoadContextInfo);
michael@0 2671
michael@0 2672 rv = ioMan->mIOThread->DispatchAfterPendingOpens(ev);
michael@0 2673 if (NS_WARN_IF(NS_FAILED(rv))) {
michael@0 2674 return rv;
michael@0 2675 }
michael@0 2676
michael@0 2677 return NS_OK;
michael@0 2678 }
michael@0 2679
michael@0 2680 nsresult
michael@0 2681 CacheFileIOManager::EvictByContextInternal(nsILoadContextInfo *aLoadContextInfo)
michael@0 2682 {
michael@0 2683 LOG(("CacheFileIOManager::EvictByContextInternal() [loadContextInfo=%p, "
michael@0 2684 "anonymous=%u, inBrowser=%u, appId=%u]", aLoadContextInfo,
michael@0 2685 aLoadContextInfo->IsAnonymous(), aLoadContextInfo->IsInBrowserElement(),
michael@0 2686 aLoadContextInfo->AppId()));
michael@0 2687
michael@0 2688 nsresult rv;
michael@0 2689
michael@0 2690 MOZ_ASSERT(mIOThread->IsCurrentThread());
michael@0 2691
michael@0 2692 MOZ_ASSERT(!aLoadContextInfo->IsPrivate());
michael@0 2693 if (aLoadContextInfo->IsPrivate()) {
michael@0 2694 return NS_ERROR_INVALID_ARG;
michael@0 2695 }
michael@0 2696
michael@0 2697 if (!mCacheDirectory) {
michael@0 2698 return NS_ERROR_FILE_INVALID_PATH;
michael@0 2699 }
michael@0 2700
michael@0 2701 if (mShuttingDown) {
michael@0 2702 return NS_ERROR_NOT_INITIALIZED;
michael@0 2703 }
michael@0 2704
michael@0 2705 if (!mTreeCreated) {
michael@0 2706 rv = CreateCacheTree();
michael@0 2707 if (NS_FAILED(rv)) {
michael@0 2708 return rv;
michael@0 2709 }
michael@0 2710 }
michael@0 2711
michael@0 2712 // Doom all active handles that matches the load context
michael@0 2713 nsTArray<nsRefPtr<CacheFileHandle> > handles;
michael@0 2714 mHandles.GetActiveHandles(&handles);
michael@0 2715
michael@0 2716 for (uint32_t i = 0; i < handles.Length(); ++i) {
michael@0 2717 bool equals;
michael@0 2718 rv = CacheFileUtils::KeyMatchesLoadContextInfo(handles[i]->Key(),
michael@0 2719 aLoadContextInfo,
michael@0 2720 &equals);
michael@0 2721 if (NS_FAILED(rv)) {
michael@0 2722 LOG(("CacheFileIOManager::EvictByContextInternal() - Cannot parse key in "
michael@0 2723 "handle! [handle=%p, key=%s]", handles[i].get(),
michael@0 2724 handles[i]->Key().get()));
michael@0 2725 MOZ_CRASH("Unexpected error!");
michael@0 2726 }
michael@0 2727
michael@0 2728 if (equals) {
michael@0 2729 rv = DoomFileInternal(handles[i]);
michael@0 2730 if (NS_WARN_IF(NS_FAILED(rv))) {
michael@0 2731 LOG(("CacheFileIOManager::EvictByContextInternal() - Cannot doom handle"
michael@0 2732 " [handle=%p]", handles[i].get()));
michael@0 2733 }
michael@0 2734 }
michael@0 2735 }
michael@0 2736
michael@0 2737 if (!mContextEvictor) {
michael@0 2738 mContextEvictor = new CacheFileContextEvictor();
michael@0 2739 mContextEvictor->Init(mCacheDirectory);
michael@0 2740 }
michael@0 2741
michael@0 2742 mContextEvictor->AddContext(aLoadContextInfo);
michael@0 2743
michael@0 2744 return NS_OK;
michael@0 2745 }
michael@0 2746
michael@0 2747 // static
michael@0 2748 nsresult
michael@0 2749 CacheFileIOManager::CacheIndexStateChanged()
michael@0 2750 {
michael@0 2751 LOG(("CacheFileIOManager::CacheIndexStateChanged()"));
michael@0 2752
michael@0 2753 nsresult rv;
michael@0 2754
michael@0 2755 // CacheFileIOManager lives longer than CacheIndex so gInstance must be
michael@0 2756 // non-null here.
michael@0 2757 MOZ_ASSERT(gInstance);
michael@0 2758
michael@0 2759 // We have to re-distatch even if we are on IO thread to prevent reentering
michael@0 2760 // the lock in CacheIndex
michael@0 2761 nsCOMPtr<nsIRunnable> ev;
michael@0 2762 ev = NS_NewRunnableMethod(
michael@0 2763 gInstance, &CacheFileIOManager::CacheIndexStateChangedInternal);
michael@0 2764
michael@0 2765 nsCOMPtr<nsIEventTarget> ioTarget = IOTarget();
michael@0 2766 MOZ_ASSERT(ioTarget);
michael@0 2767
michael@0 2768 rv = ioTarget->Dispatch(ev, nsIEventTarget::DISPATCH_NORMAL);
michael@0 2769 if (NS_WARN_IF(NS_FAILED(rv))) {
michael@0 2770 return rv;
michael@0 2771 }
michael@0 2772
michael@0 2773 return NS_OK;
michael@0 2774 }
michael@0 2775
michael@0 2776 nsresult
michael@0 2777 CacheFileIOManager::CacheIndexStateChangedInternal()
michael@0 2778 {
michael@0 2779 if (mShuttingDown) {
michael@0 2780 // ignore notification during shutdown
michael@0 2781 return NS_OK;
michael@0 2782 }
michael@0 2783
michael@0 2784 if (!mContextEvictor) {
michael@0 2785 return NS_OK;
michael@0 2786 }
michael@0 2787
michael@0 2788 mContextEvictor->CacheIndexStateChanged();
michael@0 2789 return NS_OK;
michael@0 2790 }
michael@0 2791
michael@0 2792 nsresult
michael@0 2793 CacheFileIOManager::TrashDirectory(nsIFile *aFile)
michael@0 2794 {
michael@0 2795 #ifdef PR_LOGGING
michael@0 2796 nsAutoCString path;
michael@0 2797 aFile->GetNativePath(path);
michael@0 2798 #endif
michael@0 2799 LOG(("CacheFileIOManager::TrashDirectory() [file=%s]", path.get()));
michael@0 2800
michael@0 2801 nsresult rv;
michael@0 2802
michael@0 2803 MOZ_ASSERT(mIOThread->IsCurrentThread());
michael@0 2804 MOZ_ASSERT(mCacheDirectory);
michael@0 2805
michael@0 2806 // When the directory is empty, it is cheaper to remove it directly instead of
michael@0 2807 // using the trash mechanism.
michael@0 2808 bool isEmpty;
michael@0 2809 rv = IsEmptyDirectory(aFile, &isEmpty);
michael@0 2810 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2811
michael@0 2812 if (isEmpty) {
michael@0 2813 rv = aFile->Remove(false);
michael@0 2814 LOG(("CacheFileIOManager::TrashDirectory() - Directory removed [rv=0x%08x]",
michael@0 2815 rv));
michael@0 2816 return rv;
michael@0 2817 }
michael@0 2818
michael@0 2819 #ifdef DEBUG
michael@0 2820 nsCOMPtr<nsIFile> dirCheck;
michael@0 2821 rv = aFile->GetParent(getter_AddRefs(dirCheck));
michael@0 2822 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2823
michael@0 2824 bool equals = false;
michael@0 2825 rv = dirCheck->Equals(mCacheDirectory, &equals);
michael@0 2826 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2827
michael@0 2828 MOZ_ASSERT(equals);
michael@0 2829 #endif
michael@0 2830
michael@0 2831 nsCOMPtr<nsIFile> dir, trash;
michael@0 2832 nsAutoCString leaf;
michael@0 2833
michael@0 2834 rv = aFile->Clone(getter_AddRefs(dir));
michael@0 2835 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2836
michael@0 2837 rv = aFile->Clone(getter_AddRefs(trash));
michael@0 2838 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2839
michael@0 2840 srand(static_cast<unsigned>(PR_Now()));
michael@0 2841 while (true) {
michael@0 2842 leaf = kTrashDir;
michael@0 2843 leaf.AppendInt(rand());
michael@0 2844 rv = trash->SetNativeLeafName(leaf);
michael@0 2845 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2846
michael@0 2847 bool exists;
michael@0 2848 if (NS_SUCCEEDED(trash->Exists(&exists)) && !exists) {
michael@0 2849 break;
michael@0 2850 }
michael@0 2851 }
michael@0 2852
michael@0 2853 LOG(("CacheFileIOManager::TrashDirectory() - Renaming directory [leaf=%s]",
michael@0 2854 leaf.get()));
michael@0 2855
michael@0 2856 rv = dir->MoveToNative(nullptr, leaf);
michael@0 2857 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2858
michael@0 2859 StartRemovingTrash();
michael@0 2860 return NS_OK;
michael@0 2861 }
michael@0 2862
michael@0 2863 // static
michael@0 2864 void
michael@0 2865 CacheFileIOManager::OnTrashTimer(nsITimer *aTimer, void *aClosure)
michael@0 2866 {
michael@0 2867 LOG(("CacheFileIOManager::OnTrashTimer() [timer=%p, closure=%p]", aTimer,
michael@0 2868 aClosure));
michael@0 2869
michael@0 2870 nsRefPtr<CacheFileIOManager> ioMan = gInstance;
michael@0 2871
michael@0 2872 if (!ioMan) {
michael@0 2873 return;
michael@0 2874 }
michael@0 2875
michael@0 2876 ioMan->mTrashTimer = nullptr;
michael@0 2877 ioMan->StartRemovingTrash();
michael@0 2878 }
michael@0 2879
michael@0 2880 nsresult
michael@0 2881 CacheFileIOManager::StartRemovingTrash()
michael@0 2882 {
michael@0 2883 LOG(("CacheFileIOManager::StartRemovingTrash()"));
michael@0 2884
michael@0 2885 nsresult rv;
michael@0 2886
michael@0 2887 MOZ_ASSERT(mIOThread->IsCurrentThread());
michael@0 2888
michael@0 2889 if (mShuttingDown) {
michael@0 2890 return NS_ERROR_NOT_INITIALIZED;
michael@0 2891 }
michael@0 2892
michael@0 2893 if (!mCacheDirectory) {
michael@0 2894 return NS_ERROR_FILE_INVALID_PATH;
michael@0 2895 }
michael@0 2896
michael@0 2897 if (mTrashTimer) {
michael@0 2898 LOG(("CacheFileIOManager::StartRemovingTrash() - Trash timer exists."));
michael@0 2899 return NS_OK;
michael@0 2900 }
michael@0 2901
michael@0 2902 if (mRemovingTrashDirs) {
michael@0 2903 LOG(("CacheFileIOManager::StartRemovingTrash() - Trash removing in "
michael@0 2904 "progress."));
michael@0 2905 return NS_OK;
michael@0 2906 }
michael@0 2907
michael@0 2908 uint32_t elapsed = (TimeStamp::NowLoRes() - mStartTime).ToMilliseconds();
michael@0 2909 if (elapsed < kRemoveTrashStartDelay) {
michael@0 2910 nsCOMPtr<nsITimer> timer = do_CreateInstance("@mozilla.org/timer;1", &rv);
michael@0 2911 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2912
michael@0 2913 nsCOMPtr<nsIEventTarget> ioTarget = IOTarget();
michael@0 2914 MOZ_ASSERT(ioTarget);
michael@0 2915
michael@0 2916 rv = timer->SetTarget(ioTarget);
michael@0 2917 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2918
michael@0 2919 rv = timer->InitWithFuncCallback(CacheFileIOManager::OnTrashTimer, nullptr,
michael@0 2920 kRemoveTrashStartDelay - elapsed,
michael@0 2921 nsITimer::TYPE_ONE_SHOT);
michael@0 2922 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2923
michael@0 2924 mTrashTimer.swap(timer);
michael@0 2925 return NS_OK;
michael@0 2926 }
michael@0 2927
michael@0 2928 nsCOMPtr<nsIRunnable> ev;
michael@0 2929 ev = NS_NewRunnableMethod(this,
michael@0 2930 &CacheFileIOManager::RemoveTrashInternal);
michael@0 2931
michael@0 2932 rv = mIOThread->Dispatch(ev, CacheIOThread::EVICT);
michael@0 2933 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2934
michael@0 2935 mRemovingTrashDirs = true;
michael@0 2936 return NS_OK;
michael@0 2937 }
michael@0 2938
michael@0 2939 nsresult
michael@0 2940 CacheFileIOManager::RemoveTrashInternal()
michael@0 2941 {
michael@0 2942 LOG(("CacheFileIOManager::RemoveTrashInternal()"));
michael@0 2943
michael@0 2944 nsresult rv;
michael@0 2945
michael@0 2946 MOZ_ASSERT(mIOThread->IsCurrentThread());
michael@0 2947
michael@0 2948 if (mShuttingDown) {
michael@0 2949 return NS_ERROR_NOT_INITIALIZED;
michael@0 2950 }
michael@0 2951
michael@0 2952 MOZ_ASSERT(!mTrashTimer);
michael@0 2953 MOZ_ASSERT(mRemovingTrashDirs);
michael@0 2954
michael@0 2955 if (!mTreeCreated) {
michael@0 2956 rv = CreateCacheTree();
michael@0 2957 if (NS_FAILED(rv)) {
michael@0 2958 return rv;
michael@0 2959 }
michael@0 2960 }
michael@0 2961
michael@0 2962 // mRemovingTrashDirs is accessed only on IO thread, so we can drop the flag
michael@0 2963 // here and set it again once we dispatch a continuation event. By doing so,
michael@0 2964 // we don't have to drop the flag on any possible early return.
michael@0 2965 mRemovingTrashDirs = false;
michael@0 2966
michael@0 2967 while (true) {
michael@0 2968 if (CacheIOThread::YieldAndRerun()) {
michael@0 2969 LOG(("CacheFileIOManager::RemoveTrashInternal() - Breaking loop for "
michael@0 2970 "higher level events."));
michael@0 2971 mRemovingTrashDirs = true;
michael@0 2972 return NS_OK;
michael@0 2973 }
michael@0 2974
michael@0 2975 // Find some trash directory
michael@0 2976 if (!mTrashDir) {
michael@0 2977 MOZ_ASSERT(!mTrashDirEnumerator);
michael@0 2978
michael@0 2979 rv = FindTrashDirToRemove();
michael@0 2980 if (rv == NS_ERROR_NOT_AVAILABLE) {
michael@0 2981 LOG(("CacheFileIOManager::RemoveTrashInternal() - No trash directory "
michael@0 2982 "found."));
michael@0 2983 return NS_OK;
michael@0 2984 }
michael@0 2985 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2986
michael@0 2987 nsCOMPtr<nsISimpleEnumerator> enumerator;
michael@0 2988 rv = mTrashDir->GetDirectoryEntries(getter_AddRefs(enumerator));
michael@0 2989 if (NS_SUCCEEDED(rv)) {
michael@0 2990 mTrashDirEnumerator = do_QueryInterface(enumerator, &rv);
michael@0 2991 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2992 }
michael@0 2993
michael@0 2994 continue; // check elapsed time
michael@0 2995 }
michael@0 2996
michael@0 2997 // We null out mTrashDirEnumerator once we remove all files in the
michael@0 2998 // directory, so remove the trash directory if we don't have enumerator.
michael@0 2999 if (!mTrashDirEnumerator) {
michael@0 3000 rv = mTrashDir->Remove(false);
michael@0 3001 if (NS_FAILED(rv)) {
michael@0 3002 // There is no reason why removing an empty directory should fail, but
michael@0 3003 // if it does, we should continue and try to remove all other trash
michael@0 3004 // directories.
michael@0 3005 nsAutoCString leafName;
michael@0 3006 mTrashDir->GetNativeLeafName(leafName);
michael@0 3007 mFailedTrashDirs.AppendElement(leafName);
michael@0 3008 LOG(("CacheFileIOManager::RemoveTrashInternal() - Cannot remove "
michael@0 3009 "trashdir. [name=%s]", leafName.get()));
michael@0 3010 }
michael@0 3011
michael@0 3012 mTrashDir = nullptr;
michael@0 3013 continue; // check elapsed time
michael@0 3014 }
michael@0 3015
michael@0 3016 nsCOMPtr<nsIFile> file;
michael@0 3017 rv = mTrashDirEnumerator->GetNextFile(getter_AddRefs(file));
michael@0 3018 if (!file) {
michael@0 3019 mTrashDirEnumerator->Close();
michael@0 3020 mTrashDirEnumerator = nullptr;
michael@0 3021 continue; // check elapsed time
michael@0 3022 } else {
michael@0 3023 bool isDir = false;
michael@0 3024 file->IsDirectory(&isDir);
michael@0 3025 if (isDir) {
michael@0 3026 NS_WARNING("Found a directory in a trash directory! It will be removed "
michael@0 3027 "recursively, but this can block IO thread for a while!");
michael@0 3028 #ifdef PR_LOGGING
michael@0 3029 nsAutoCString path;
michael@0 3030 file->GetNativePath(path);
michael@0 3031 #endif
michael@0 3032 LOG(("CacheFileIOManager::RemoveTrashInternal() - Found a directory in a trash "
michael@0 3033 "directory! It will be removed recursively, but this can block IO "
michael@0 3034 "thread for a while! [file=%s]", path.get()));
michael@0 3035 }
michael@0 3036 file->Remove(isDir);
michael@0 3037 }
michael@0 3038 }
michael@0 3039
michael@0 3040 NS_NOTREACHED("We should never get here");
michael@0 3041 return NS_OK;
michael@0 3042 }
michael@0 3043
michael@0 3044 nsresult
michael@0 3045 CacheFileIOManager::FindTrashDirToRemove()
michael@0 3046 {
michael@0 3047 LOG(("CacheFileIOManager::FindTrashDirToRemove()"));
michael@0 3048
michael@0 3049 nsresult rv;
michael@0 3050
michael@0 3051 // We call this method on the main thread during shutdown when user wants to
michael@0 3052 // remove all cache files.
michael@0 3053 MOZ_ASSERT(mIOThread->IsCurrentThread() || mShuttingDown);
michael@0 3054
michael@0 3055 nsCOMPtr<nsISimpleEnumerator> iter;
michael@0 3056 rv = mCacheDirectory->GetDirectoryEntries(getter_AddRefs(iter));
michael@0 3057 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3058
michael@0 3059 bool more;
michael@0 3060 nsCOMPtr<nsISupports> elem;
michael@0 3061
michael@0 3062 while (NS_SUCCEEDED(iter->HasMoreElements(&more)) && more) {
michael@0 3063 rv = iter->GetNext(getter_AddRefs(elem));
michael@0 3064 if (NS_FAILED(rv)) {
michael@0 3065 continue;
michael@0 3066 }
michael@0 3067
michael@0 3068 nsCOMPtr<nsIFile> file = do_QueryInterface(elem);
michael@0 3069 if (!file) {
michael@0 3070 continue;
michael@0 3071 }
michael@0 3072
michael@0 3073 bool isDir = false;
michael@0 3074 file->IsDirectory(&isDir);
michael@0 3075 if (!isDir) {
michael@0 3076 continue;
michael@0 3077 }
michael@0 3078
michael@0 3079 nsAutoCString leafName;
michael@0 3080 rv = file->GetNativeLeafName(leafName);
michael@0 3081 if (NS_FAILED(rv)) {
michael@0 3082 continue;
michael@0 3083 }
michael@0 3084
michael@0 3085 if (leafName.Length() < strlen(kTrashDir)) {
michael@0 3086 continue;
michael@0 3087 }
michael@0 3088
michael@0 3089 if (!StringBeginsWith(leafName, NS_LITERAL_CSTRING(kTrashDir))) {
michael@0 3090 continue;
michael@0 3091 }
michael@0 3092
michael@0 3093 if (mFailedTrashDirs.Contains(leafName)) {
michael@0 3094 continue;
michael@0 3095 }
michael@0 3096
michael@0 3097 LOG(("CacheFileIOManager::FindTrashDirToRemove() - Returning directory %s",
michael@0 3098 leafName.get()));
michael@0 3099
michael@0 3100 mTrashDir = file;
michael@0 3101 return NS_OK;
michael@0 3102 }
michael@0 3103
michael@0 3104 // When we're here we've tried to delete all trash directories. Clear
michael@0 3105 // mFailedTrashDirs so we will try to delete them again when we start removing
michael@0 3106 // trash directories next time.
michael@0 3107 mFailedTrashDirs.Clear();
michael@0 3108 return NS_ERROR_NOT_AVAILABLE;
michael@0 3109 }
michael@0 3110
michael@0 3111 // static
michael@0 3112 nsresult
michael@0 3113 CacheFileIOManager::InitIndexEntry(CacheFileHandle *aHandle,
michael@0 3114 uint32_t aAppId,
michael@0 3115 bool aAnonymous,
michael@0 3116 bool aInBrowser)
michael@0 3117 {
michael@0 3118 LOG(("CacheFileIOManager::InitIndexEntry() [handle=%p, appId=%u, anonymous=%d"
michael@0 3119 ", inBrowser=%d]", aHandle, aAppId, aAnonymous, aInBrowser));
michael@0 3120
michael@0 3121 nsresult rv;
michael@0 3122 nsRefPtr<CacheFileIOManager> ioMan = gInstance;
michael@0 3123
michael@0 3124 if (aHandle->IsClosed() || !ioMan) {
michael@0 3125 return NS_ERROR_NOT_INITIALIZED;
michael@0 3126 }
michael@0 3127
michael@0 3128 if (aHandle->IsSpecialFile()) {
michael@0 3129 return NS_ERROR_UNEXPECTED;
michael@0 3130 }
michael@0 3131
michael@0 3132 nsRefPtr<InitIndexEntryEvent> ev =
michael@0 3133 new InitIndexEntryEvent(aHandle, aAppId, aAnonymous, aInBrowser);
michael@0 3134 rv = ioMan->mIOThread->Dispatch(ev, CacheIOThread::WRITE);
michael@0 3135 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3136
michael@0 3137 return NS_OK;
michael@0 3138 }
michael@0 3139
michael@0 3140 // static
michael@0 3141 nsresult
michael@0 3142 CacheFileIOManager::UpdateIndexEntry(CacheFileHandle *aHandle,
michael@0 3143 const uint32_t *aFrecency,
michael@0 3144 const uint32_t *aExpirationTime)
michael@0 3145 {
michael@0 3146 LOG(("CacheFileIOManager::UpdateIndexEntry() [handle=%p, frecency=%s, "
michael@0 3147 "expirationTime=%s]", aHandle,
michael@0 3148 aFrecency ? nsPrintfCString("%u", *aFrecency).get() : "",
michael@0 3149 aExpirationTime ? nsPrintfCString("%u", *aExpirationTime).get() : ""));
michael@0 3150
michael@0 3151 nsresult rv;
michael@0 3152 nsRefPtr<CacheFileIOManager> ioMan = gInstance;
michael@0 3153
michael@0 3154 if (aHandle->IsClosed() || !ioMan) {
michael@0 3155 return NS_ERROR_NOT_INITIALIZED;
michael@0 3156 }
michael@0 3157
michael@0 3158 if (aHandle->IsSpecialFile()) {
michael@0 3159 return NS_ERROR_UNEXPECTED;
michael@0 3160 }
michael@0 3161
michael@0 3162 nsRefPtr<UpdateIndexEntryEvent> ev =
michael@0 3163 new UpdateIndexEntryEvent(aHandle, aFrecency, aExpirationTime);
michael@0 3164 rv = ioMan->mIOThread->Dispatch(ev, CacheIOThread::WRITE);
michael@0 3165 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3166
michael@0 3167 return NS_OK;
michael@0 3168 }
michael@0 3169
michael@0 3170 nsresult
michael@0 3171 CacheFileIOManager::CreateFile(CacheFileHandle *aHandle)
michael@0 3172 {
michael@0 3173 MOZ_ASSERT(!aHandle->mFD);
michael@0 3174 MOZ_ASSERT(aHandle->mFile);
michael@0 3175
michael@0 3176 nsresult rv;
michael@0 3177
michael@0 3178 if (aHandle->IsDoomed()) {
michael@0 3179 nsCOMPtr<nsIFile> file;
michael@0 3180
michael@0 3181 rv = GetDoomedFile(getter_AddRefs(file));
michael@0 3182 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3183
michael@0 3184 aHandle->mFile.swap(file);
michael@0 3185 } else {
michael@0 3186 bool exists;
michael@0 3187 if (NS_SUCCEEDED(aHandle->mFile->Exists(&exists)) && exists) {
michael@0 3188 NS_WARNING("Found a file that should not exist!");
michael@0 3189 }
michael@0 3190 }
michael@0 3191
michael@0 3192 rv = OpenNSPRHandle(aHandle, true);
michael@0 3193 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3194
michael@0 3195 aHandle->mFileSize = 0;
michael@0 3196 return NS_OK;
michael@0 3197 }
michael@0 3198
michael@0 3199 // static
michael@0 3200 void
michael@0 3201 CacheFileIOManager::HashToStr(const SHA1Sum::Hash *aHash, nsACString &_retval)
michael@0 3202 {
michael@0 3203 _retval.Assign("");
michael@0 3204 const char hexChars[] = {'0', '1', '2', '3', '4', '5', '6', '7',
michael@0 3205 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
michael@0 3206 for (uint32_t i=0 ; i<sizeof(SHA1Sum::Hash) ; i++) {
michael@0 3207 _retval.Append(hexChars[(*aHash)[i] >> 4]);
michael@0 3208 _retval.Append(hexChars[(*aHash)[i] & 0xF]);
michael@0 3209 }
michael@0 3210 }
michael@0 3211
michael@0 3212 // static
michael@0 3213 nsresult
michael@0 3214 CacheFileIOManager::StrToHash(const nsACString &aHash, SHA1Sum::Hash *_retval)
michael@0 3215 {
michael@0 3216 if (aHash.Length() != 2*sizeof(SHA1Sum::Hash)) {
michael@0 3217 return NS_ERROR_INVALID_ARG;
michael@0 3218 }
michael@0 3219
michael@0 3220 for (uint32_t i=0 ; i<aHash.Length() ; i++) {
michael@0 3221 uint8_t value;
michael@0 3222
michael@0 3223 if (aHash[i] >= '0' && aHash[i] <= '9') {
michael@0 3224 value = aHash[i] - '0';
michael@0 3225 } else if (aHash[i] >= 'A' && aHash[i] <= 'F') {
michael@0 3226 value = aHash[i] - 'A' + 10;
michael@0 3227 } else if (aHash[i] >= 'a' && aHash[i] <= 'f') {
michael@0 3228 value = aHash[i] - 'a' + 10;
michael@0 3229 } else {
michael@0 3230 return NS_ERROR_INVALID_ARG;
michael@0 3231 }
michael@0 3232
michael@0 3233 if (i%2 == 0) {
michael@0 3234 (reinterpret_cast<uint8_t *>(_retval))[i/2] = value << 4;
michael@0 3235 } else {
michael@0 3236 (reinterpret_cast<uint8_t *>(_retval))[i/2] += value;
michael@0 3237 }
michael@0 3238 }
michael@0 3239
michael@0 3240 return NS_OK;
michael@0 3241 }
michael@0 3242
michael@0 3243 nsresult
michael@0 3244 CacheFileIOManager::GetFile(const SHA1Sum::Hash *aHash, nsIFile **_retval)
michael@0 3245 {
michael@0 3246 nsresult rv;
michael@0 3247 nsCOMPtr<nsIFile> file;
michael@0 3248 rv = mCacheDirectory->Clone(getter_AddRefs(file));
michael@0 3249 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3250
michael@0 3251 rv = file->AppendNative(NS_LITERAL_CSTRING(kEntriesDir));
michael@0 3252 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3253
michael@0 3254 nsAutoCString leafName;
michael@0 3255 HashToStr(aHash, leafName);
michael@0 3256
michael@0 3257 rv = file->AppendNative(leafName);
michael@0 3258 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3259
michael@0 3260 file.swap(*_retval);
michael@0 3261 return NS_OK;
michael@0 3262 }
michael@0 3263
michael@0 3264 nsresult
michael@0 3265 CacheFileIOManager::GetSpecialFile(const nsACString &aKey, nsIFile **_retval)
michael@0 3266 {
michael@0 3267 nsresult rv;
michael@0 3268 nsCOMPtr<nsIFile> file;
michael@0 3269 rv = mCacheDirectory->Clone(getter_AddRefs(file));
michael@0 3270 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3271
michael@0 3272 rv = file->AppendNative(aKey);
michael@0 3273 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3274
michael@0 3275 file.swap(*_retval);
michael@0 3276 return NS_OK;
michael@0 3277 }
michael@0 3278
michael@0 3279 nsresult
michael@0 3280 CacheFileIOManager::GetDoomedFile(nsIFile **_retval)
michael@0 3281 {
michael@0 3282 nsresult rv;
michael@0 3283 nsCOMPtr<nsIFile> file;
michael@0 3284 rv = mCacheDirectory->Clone(getter_AddRefs(file));
michael@0 3285 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3286
michael@0 3287 rv = file->AppendNative(NS_LITERAL_CSTRING(kDoomedDir));
michael@0 3288 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3289
michael@0 3290 rv = file->AppendNative(NS_LITERAL_CSTRING("dummyleaf"));
michael@0 3291 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3292
michael@0 3293 srand(static_cast<unsigned>(PR_Now()));
michael@0 3294 nsAutoCString leafName;
michael@0 3295 uint32_t iter=0;
michael@0 3296 while (true) {
michael@0 3297 iter++;
michael@0 3298 leafName.AppendInt(rand());
michael@0 3299 rv = file->SetNativeLeafName(leafName);
michael@0 3300 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3301
michael@0 3302 bool exists;
michael@0 3303 if (NS_SUCCEEDED(file->Exists(&exists)) && !exists) {
michael@0 3304 break;
michael@0 3305 }
michael@0 3306
michael@0 3307 leafName.Truncate();
michael@0 3308 }
michael@0 3309
michael@0 3310 // Telemetry::Accumulate(Telemetry::DISK_CACHE_GETDOOMEDFILE_ITERATIONS, iter);
michael@0 3311
michael@0 3312 file.swap(*_retval);
michael@0 3313 return NS_OK;
michael@0 3314 }
michael@0 3315
michael@0 3316 nsresult
michael@0 3317 CacheFileIOManager::IsEmptyDirectory(nsIFile *aFile, bool *_retval)
michael@0 3318 {
michael@0 3319 MOZ_ASSERT(mIOThread->IsCurrentThread());
michael@0 3320
michael@0 3321 nsresult rv;
michael@0 3322
michael@0 3323 nsCOMPtr<nsISimpleEnumerator> enumerator;
michael@0 3324 rv = aFile->GetDirectoryEntries(getter_AddRefs(enumerator));
michael@0 3325 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3326
michael@0 3327 bool hasMoreElements = false;
michael@0 3328 rv = enumerator->HasMoreElements(&hasMoreElements);
michael@0 3329 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3330
michael@0 3331 *_retval = !hasMoreElements;
michael@0 3332 return NS_OK;
michael@0 3333 }
michael@0 3334
michael@0 3335 nsresult
michael@0 3336 CacheFileIOManager::CheckAndCreateDir(nsIFile *aFile, const char *aDir,
michael@0 3337 bool aEnsureEmptyDir)
michael@0 3338 {
michael@0 3339 nsresult rv;
michael@0 3340
michael@0 3341 nsCOMPtr<nsIFile> file;
michael@0 3342 if (!aDir) {
michael@0 3343 file = aFile;
michael@0 3344 } else {
michael@0 3345 nsAutoCString dir(aDir);
michael@0 3346 rv = aFile->Clone(getter_AddRefs(file));
michael@0 3347 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3348 rv = file->AppendNative(dir);
michael@0 3349 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3350 }
michael@0 3351
michael@0 3352 bool exists = false;
michael@0 3353 rv = file->Exists(&exists);
michael@0 3354 if (NS_SUCCEEDED(rv) && exists) {
michael@0 3355 bool isDirectory = false;
michael@0 3356 rv = file->IsDirectory(&isDirectory);
michael@0 3357 if (NS_FAILED(rv) || !isDirectory) {
michael@0 3358 // Try to remove the file
michael@0 3359 rv = file->Remove(false);
michael@0 3360 if (NS_SUCCEEDED(rv)) {
michael@0 3361 exists = false;
michael@0 3362 }
michael@0 3363 }
michael@0 3364 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3365 }
michael@0 3366
michael@0 3367 if (aEnsureEmptyDir && NS_SUCCEEDED(rv) && exists) {
michael@0 3368 bool isEmpty;
michael@0 3369 rv = IsEmptyDirectory(file, &isEmpty);
michael@0 3370 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3371
michael@0 3372 if (!isEmpty) {
michael@0 3373 rv = TrashDirectory(file);
michael@0 3374 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3375
michael@0 3376 exists = false;
michael@0 3377 }
michael@0 3378 }
michael@0 3379
michael@0 3380 if (NS_SUCCEEDED(rv) && !exists) {
michael@0 3381 rv = file->Create(nsIFile::DIRECTORY_TYPE, 0700);
michael@0 3382 }
michael@0 3383 if (NS_FAILED(rv)) {
michael@0 3384 NS_WARNING("Cannot create directory");
michael@0 3385 return NS_ERROR_FAILURE;
michael@0 3386 }
michael@0 3387
michael@0 3388 return NS_OK;
michael@0 3389 }
michael@0 3390
michael@0 3391 nsresult
michael@0 3392 CacheFileIOManager::CreateCacheTree()
michael@0 3393 {
michael@0 3394 MOZ_ASSERT(mIOThread->IsCurrentThread());
michael@0 3395 MOZ_ASSERT(!mTreeCreated);
michael@0 3396
michael@0 3397 if (!mCacheDirectory) {
michael@0 3398 return NS_ERROR_FILE_INVALID_PATH;
michael@0 3399 }
michael@0 3400
michael@0 3401 nsresult rv;
michael@0 3402
michael@0 3403 // ensure parent directory exists
michael@0 3404 nsCOMPtr<nsIFile> parentDir;
michael@0 3405 rv = mCacheDirectory->GetParent(getter_AddRefs(parentDir));
michael@0 3406 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3407 rv = CheckAndCreateDir(parentDir, nullptr, false);
michael@0 3408 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3409
michael@0 3410 // ensure cache directory exists
michael@0 3411 rv = CheckAndCreateDir(mCacheDirectory, nullptr, false);
michael@0 3412 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3413
michael@0 3414 // ensure entries directory exists
michael@0 3415 rv = CheckAndCreateDir(mCacheDirectory, kEntriesDir, false);
michael@0 3416 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3417
michael@0 3418 // ensure doomed directory exists
michael@0 3419 rv = CheckAndCreateDir(mCacheDirectory, kDoomedDir, true);
michael@0 3420 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3421
michael@0 3422 mTreeCreated = true;
michael@0 3423
michael@0 3424 if (!mContextEvictor) {
michael@0 3425 nsRefPtr<CacheFileContextEvictor> contextEvictor;
michael@0 3426 contextEvictor = new CacheFileContextEvictor();
michael@0 3427
michael@0 3428 // Init() method will try to load unfinished contexts from the disk. Store
michael@0 3429 // the evictor as a member only when there is some unfinished job.
michael@0 3430 contextEvictor->Init(mCacheDirectory);
michael@0 3431 if (contextEvictor->ContextsCount()) {
michael@0 3432 contextEvictor.swap(mContextEvictor);
michael@0 3433 }
michael@0 3434 }
michael@0 3435
michael@0 3436 StartRemovingTrash();
michael@0 3437
michael@0 3438 return NS_OK;
michael@0 3439 }
michael@0 3440
michael@0 3441 nsresult
michael@0 3442 CacheFileIOManager::OpenNSPRHandle(CacheFileHandle *aHandle, bool aCreate)
michael@0 3443 {
michael@0 3444 MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
michael@0 3445 MOZ_ASSERT(!aHandle->mFD);
michael@0 3446 MOZ_ASSERT(mHandlesByLastUsed.IndexOf(aHandle) == mHandlesByLastUsed.NoIndex);
michael@0 3447 MOZ_ASSERT(mHandlesByLastUsed.Length() <= kOpenHandlesLimit);
michael@0 3448 MOZ_ASSERT((aCreate && !aHandle->mFileExists) ||
michael@0 3449 (!aCreate && aHandle->mFileExists));
michael@0 3450
michael@0 3451 nsresult rv;
michael@0 3452
michael@0 3453 if (mHandlesByLastUsed.Length() == kOpenHandlesLimit) {
michael@0 3454 // close handle that hasn't been used for the longest time
michael@0 3455 rv = ReleaseNSPRHandleInternal(mHandlesByLastUsed[0]);
michael@0 3456 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3457 }
michael@0 3458
michael@0 3459 if (aCreate) {
michael@0 3460 rv = aHandle->mFile->OpenNSPRFileDesc(
michael@0 3461 PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE, 0600, &aHandle->mFD);
michael@0 3462 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3463
michael@0 3464 aHandle->mFileExists = true;
michael@0 3465 } else {
michael@0 3466 rv = aHandle->mFile->OpenNSPRFileDesc(PR_RDWR, 0600, &aHandle->mFD);
michael@0 3467 if (NS_ERROR_FILE_NOT_FOUND == rv) {
michael@0 3468 LOG((" file doesn't exists"));
michael@0 3469 aHandle->mFileExists = false;
michael@0 3470 return DoomFileInternal(aHandle);
michael@0 3471 }
michael@0 3472 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3473 }
michael@0 3474
michael@0 3475 mHandlesByLastUsed.AppendElement(aHandle);
michael@0 3476 return NS_OK;
michael@0 3477 }
michael@0 3478
michael@0 3479 void
michael@0 3480 CacheFileIOManager::NSPRHandleUsed(CacheFileHandle *aHandle)
michael@0 3481 {
michael@0 3482 MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
michael@0 3483 MOZ_ASSERT(aHandle->mFD);
michael@0 3484
michael@0 3485 DebugOnly<bool> found;
michael@0 3486 found = mHandlesByLastUsed.RemoveElement(aHandle);
michael@0 3487 MOZ_ASSERT(found);
michael@0 3488
michael@0 3489 mHandlesByLastUsed.AppendElement(aHandle);
michael@0 3490 }
michael@0 3491
michael@0 3492 nsresult
michael@0 3493 CacheFileIOManager::SyncRemoveDir(nsIFile *aFile, const char *aDir)
michael@0 3494 {
michael@0 3495 nsresult rv;
michael@0 3496 nsCOMPtr<nsIFile> file;
michael@0 3497
michael@0 3498 if (!aDir) {
michael@0 3499 file = aFile;
michael@0 3500 } else {
michael@0 3501 rv = aFile->Clone(getter_AddRefs(file));
michael@0 3502 if (NS_WARN_IF(NS_FAILED(rv))) {
michael@0 3503 return rv;
michael@0 3504 }
michael@0 3505
michael@0 3506 rv = file->AppendNative(nsDependentCString(aDir));
michael@0 3507 if (NS_WARN_IF(NS_FAILED(rv))) {
michael@0 3508 return rv;
michael@0 3509 }
michael@0 3510 }
michael@0 3511
michael@0 3512 #ifdef PR_LOGGING
michael@0 3513 nsAutoCString path;
michael@0 3514 file->GetNativePath(path);
michael@0 3515 #endif
michael@0 3516
michael@0 3517 LOG(("CacheFileIOManager::SyncRemoveDir() - Removing directory %s",
michael@0 3518 path.get()));
michael@0 3519
michael@0 3520 rv = file->Remove(true);
michael@0 3521 if (NS_WARN_IF(NS_FAILED(rv))) {
michael@0 3522 LOG(("CacheFileIOManager::SyncRemoveDir() - Removing failed! [rv=0x%08x]",
michael@0 3523 rv));
michael@0 3524 }
michael@0 3525
michael@0 3526 return rv;
michael@0 3527 }
michael@0 3528
michael@0 3529 void
michael@0 3530 CacheFileIOManager::SyncRemoveAllCacheFiles()
michael@0 3531 {
michael@0 3532 LOG(("CacheFileIOManager::SyncRemoveAllCacheFiles()"));
michael@0 3533
michael@0 3534 nsresult rv;
michael@0 3535
michael@0 3536 SyncRemoveDir(mCacheDirectory, kEntriesDir);
michael@0 3537 SyncRemoveDir(mCacheDirectory, kDoomedDir);
michael@0 3538
michael@0 3539 // Clear any intermediate state of trash dir enumeration.
michael@0 3540 mFailedTrashDirs.Clear();
michael@0 3541 mTrashDir = nullptr;
michael@0 3542
michael@0 3543 while (true) {
michael@0 3544 // FindTrashDirToRemove() fills mTrashDir if there is any trash directory.
michael@0 3545 rv = FindTrashDirToRemove();
michael@0 3546 if (rv == NS_ERROR_NOT_AVAILABLE) {
michael@0 3547 LOG(("CacheFileIOManager::SyncRemoveAllCacheFiles() - No trash directory "
michael@0 3548 "found."));
michael@0 3549 break;
michael@0 3550 }
michael@0 3551 if (NS_WARN_IF(NS_FAILED(rv))) {
michael@0 3552 LOG(("CacheFileIOManager::SyncRemoveAllCacheFiles() - "
michael@0 3553 "FindTrashDirToRemove() returned an unexpected error. [rv=0x%08x]",
michael@0 3554 rv));
michael@0 3555 break;
michael@0 3556 }
michael@0 3557
michael@0 3558 rv = SyncRemoveDir(mTrashDir, nullptr);
michael@0 3559 if (NS_FAILED(rv)) {
michael@0 3560 nsAutoCString leafName;
michael@0 3561 mTrashDir->GetNativeLeafName(leafName);
michael@0 3562 mFailedTrashDirs.AppendElement(leafName);
michael@0 3563 }
michael@0 3564 }
michael@0 3565 }
michael@0 3566
michael@0 3567 // Returns default ("smart") size (in KB) of cache, given available disk space
michael@0 3568 // (also in KB)
michael@0 3569 static uint32_t
michael@0 3570 SmartCacheSize(const uint32_t availKB)
michael@0 3571 {
michael@0 3572 uint32_t maxSize = kMaxCacheSizeKB;
michael@0 3573
michael@0 3574 if (availKB > 100 * 1024 * 1024) {
michael@0 3575 return maxSize; // skip computing if we're over 100 GB
michael@0 3576 }
michael@0 3577
michael@0 3578 // Grow/shrink in 10 MB units, deliberately, so that in the common case we
michael@0 3579 // don't shrink cache and evict items every time we startup (it's important
michael@0 3580 // that we don't slow down startup benchmarks).
michael@0 3581 uint32_t sz10MBs = 0;
michael@0 3582 uint32_t avail10MBs = availKB / (1024*10);
michael@0 3583
michael@0 3584 // .5% of space above 25 GB
michael@0 3585 if (avail10MBs > 2500) {
michael@0 3586 sz10MBs += static_cast<uint32_t>((avail10MBs - 2500)*.005);
michael@0 3587 avail10MBs = 2500;
michael@0 3588 }
michael@0 3589 // 1% of space between 7GB -> 25 GB
michael@0 3590 if (avail10MBs > 700) {
michael@0 3591 sz10MBs += static_cast<uint32_t>((avail10MBs - 700)*.01);
michael@0 3592 avail10MBs = 700;
michael@0 3593 }
michael@0 3594 // 5% of space between 500 MB -> 7 GB
michael@0 3595 if (avail10MBs > 50) {
michael@0 3596 sz10MBs += static_cast<uint32_t>((avail10MBs - 50)*.05);
michael@0 3597 avail10MBs = 50;
michael@0 3598 }
michael@0 3599
michael@0 3600 #ifdef ANDROID
michael@0 3601 // On Android, smaller/older devices may have very little storage and
michael@0 3602 // device owners may be sensitive to storage footprint: Use a smaller
michael@0 3603 // percentage of available space and a smaller minimum.
michael@0 3604
michael@0 3605 // 20% of space up to 500 MB (10 MB min)
michael@0 3606 sz10MBs += std::max<uint32_t>(1, static_cast<uint32_t>(avail10MBs * .2));
michael@0 3607 #else
michael@0 3608 // 40% of space up to 500 MB (50 MB min)
michael@0 3609 sz10MBs += std::max<uint32_t>(5, static_cast<uint32_t>(avail10MBs * .4));
michael@0 3610 #endif
michael@0 3611
michael@0 3612 return std::min<uint32_t>(maxSize, sz10MBs * 10 * 1024);
michael@0 3613 }
michael@0 3614
michael@0 3615 nsresult
michael@0 3616 CacheFileIOManager::UpdateSmartCacheSize()
michael@0 3617 {
michael@0 3618 MOZ_ASSERT(mIOThread->IsCurrentThread());
michael@0 3619
michael@0 3620 nsresult rv;
michael@0 3621
michael@0 3622 if (!CacheObserver::UseNewCache()) {
michael@0 3623 return NS_ERROR_NOT_AVAILABLE;
michael@0 3624 }
michael@0 3625
michael@0 3626 if (!CacheObserver::SmartCacheSizeEnabled()) {
michael@0 3627 return NS_ERROR_NOT_AVAILABLE;
michael@0 3628 }
michael@0 3629
michael@0 3630 // Wait at least kSmartSizeUpdateInterval before recomputing smart size.
michael@0 3631 static const TimeDuration kUpdateLimit =
michael@0 3632 TimeDuration::FromMilliseconds(kSmartSizeUpdateInterval);
michael@0 3633 if (!mLastSmartSizeTime.IsNull() &&
michael@0 3634 (TimeStamp::NowLoRes() - mLastSmartSizeTime) < kUpdateLimit) {
michael@0 3635 return NS_OK;
michael@0 3636 }
michael@0 3637
michael@0 3638 // Do not compute smart size when cache size is not reliable.
michael@0 3639 bool isUpToDate = false;
michael@0 3640 CacheIndex::IsUpToDate(&isUpToDate);
michael@0 3641 if (!isUpToDate) {
michael@0 3642 return NS_ERROR_NOT_AVAILABLE;
michael@0 3643 }
michael@0 3644
michael@0 3645 uint32_t cacheUsage;
michael@0 3646 rv = CacheIndex::GetCacheSize(&cacheUsage);
michael@0 3647 if (NS_WARN_IF(NS_FAILED(rv))) {
michael@0 3648 LOG(("CacheFileIOManager::UpdateSmartCacheSize() - Cannot get cacheUsage! "
michael@0 3649 "[rv=0x%08x]", rv));
michael@0 3650 return rv;
michael@0 3651 }
michael@0 3652
michael@0 3653 int64_t avail;
michael@0 3654 rv = mCacheDirectory->GetDiskSpaceAvailable(&avail);
michael@0 3655 if (NS_WARN_IF(NS_FAILED(rv))) {
michael@0 3656 // Do not change smart size.
michael@0 3657 LOG(("CacheFileIOManager::UpdateSmartCacheSize() - GetDiskSpaceAvailable() "
michael@0 3658 "failed! [rv=0x%08x]", rv));
michael@0 3659 return rv;
michael@0 3660 }
michael@0 3661
michael@0 3662 mLastSmartSizeTime = TimeStamp::NowLoRes();
michael@0 3663
michael@0 3664 uint32_t smartSize = SmartCacheSize(static_cast<uint32_t>(avail / 1024) +
michael@0 3665 cacheUsage);
michael@0 3666
michael@0 3667 if (smartSize == (CacheObserver::DiskCacheCapacity() >> 10)) {
michael@0 3668 // Smart size has not changed.
michael@0 3669 return NS_OK;
michael@0 3670 }
michael@0 3671
michael@0 3672 CacheObserver::SetDiskCacheCapacity(smartSize << 10);
michael@0 3673
michael@0 3674 return NS_OK;
michael@0 3675 }
michael@0 3676
michael@0 3677 // Memory reporting
michael@0 3678
michael@0 3679 namespace { // anon
michael@0 3680
michael@0 3681 // A helper class that dispatches and waits for an event that gets result of
michael@0 3682 // CacheFileIOManager->mHandles.SizeOfExcludingThis() on the I/O thread
michael@0 3683 // to safely get handles memory report.
michael@0 3684 // We must do this, since the handle list is only accessed and managed w/o
michael@0 3685 // locking on the I/O thread. That is by design.
michael@0 3686 class SizeOfHandlesRunnable : public nsRunnable
michael@0 3687 {
michael@0 3688 public:
michael@0 3689 SizeOfHandlesRunnable(mozilla::MallocSizeOf mallocSizeOf,
michael@0 3690 CacheFileHandles const &handles,
michael@0 3691 nsTArray<CacheFileHandle *> const &specialHandles)
michael@0 3692 : mMonitor("SizeOfHandlesRunnable.mMonitor")
michael@0 3693 , mMallocSizeOf(mallocSizeOf)
michael@0 3694 , mHandles(handles)
michael@0 3695 , mSpecialHandles(specialHandles)
michael@0 3696 {
michael@0 3697 }
michael@0 3698
michael@0 3699 size_t Get(CacheIOThread* thread)
michael@0 3700 {
michael@0 3701 nsCOMPtr<nsIEventTarget> target = thread->Target();
michael@0 3702 if (!target) {
michael@0 3703 NS_ERROR("If we have the I/O thread we also must have the I/O target");
michael@0 3704 return 0;
michael@0 3705 }
michael@0 3706
michael@0 3707 mozilla::MonitorAutoLock mon(mMonitor);
michael@0 3708 nsresult rv = target->Dispatch(this, nsIEventTarget::DISPATCH_NORMAL);
michael@0 3709 if (NS_FAILED(rv)) {
michael@0 3710 NS_ERROR("Dispatch failed, cannot do memory report of CacheFileHandles");
michael@0 3711 return 0;
michael@0 3712 }
michael@0 3713
michael@0 3714 mon.Wait();
michael@0 3715 return mSize;
michael@0 3716 }
michael@0 3717
michael@0 3718 NS_IMETHOD Run()
michael@0 3719 {
michael@0 3720 mozilla::MonitorAutoLock mon(mMonitor);
michael@0 3721 // Excluding this since the object itself is a member of CacheFileIOManager
michael@0 3722 // reported in CacheFileIOManager::SizeOfIncludingThis as part of |this|.
michael@0 3723 mSize = mHandles.SizeOfExcludingThis(mMallocSizeOf);
michael@0 3724 for (uint32_t i = 0; i < mSpecialHandles.Length(); ++i) {
michael@0 3725 mSize += mSpecialHandles[i]->SizeOfIncludingThis(mMallocSizeOf);
michael@0 3726 }
michael@0 3727
michael@0 3728 mon.Notify();
michael@0 3729 return NS_OK;
michael@0 3730 }
michael@0 3731
michael@0 3732 private:
michael@0 3733 mozilla::Monitor mMonitor;
michael@0 3734 mozilla::MallocSizeOf mMallocSizeOf;
michael@0 3735 CacheFileHandles const &mHandles;
michael@0 3736 nsTArray<CacheFileHandle *> const &mSpecialHandles;
michael@0 3737 size_t mSize;
michael@0 3738 };
michael@0 3739
michael@0 3740 } // anon
michael@0 3741
michael@0 3742 size_t
michael@0 3743 CacheFileIOManager::SizeOfExcludingThisInternal(mozilla::MallocSizeOf mallocSizeOf) const
michael@0 3744 {
michael@0 3745 size_t n = 0;
michael@0 3746 nsCOMPtr<nsISizeOf> sizeOf;
michael@0 3747
michael@0 3748 if (mIOThread) {
michael@0 3749 n += mIOThread->SizeOfIncludingThis(mallocSizeOf);
michael@0 3750
michael@0 3751 // mHandles and mSpecialHandles must be accessed only on the I/O thread,
michael@0 3752 // must sync dispatch.
michael@0 3753 nsRefPtr<SizeOfHandlesRunnable> sizeOfHandlesRunnable =
michael@0 3754 new SizeOfHandlesRunnable(mallocSizeOf, mHandles, mSpecialHandles);
michael@0 3755 n += sizeOfHandlesRunnable->Get(mIOThread);
michael@0 3756 }
michael@0 3757
michael@0 3758 // mHandlesByLastUsed just refers handles reported by mHandles.
michael@0 3759
michael@0 3760 sizeOf = do_QueryInterface(mCacheDirectory);
michael@0 3761 if (sizeOf)
michael@0 3762 n += sizeOf->SizeOfIncludingThis(mallocSizeOf);
michael@0 3763
michael@0 3764 sizeOf = do_QueryInterface(mMetadataWritesTimer);
michael@0 3765 if (sizeOf)
michael@0 3766 n += sizeOf->SizeOfIncludingThis(mallocSizeOf);
michael@0 3767
michael@0 3768 sizeOf = do_QueryInterface(mTrashTimer);
michael@0 3769 if (sizeOf)
michael@0 3770 n += sizeOf->SizeOfIncludingThis(mallocSizeOf);
michael@0 3771
michael@0 3772 sizeOf = do_QueryInterface(mTrashDir);
michael@0 3773 if (sizeOf)
michael@0 3774 n += sizeOf->SizeOfIncludingThis(mallocSizeOf);
michael@0 3775
michael@0 3776 for (uint32_t i = 0; i < mFailedTrashDirs.Length(); ++i) {
michael@0 3777 n += mFailedTrashDirs[i].SizeOfExcludingThisIfUnshared(mallocSizeOf);
michael@0 3778 }
michael@0 3779
michael@0 3780 return n;
michael@0 3781 }
michael@0 3782
michael@0 3783 // static
michael@0 3784 size_t
michael@0 3785 CacheFileIOManager::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf)
michael@0 3786 {
michael@0 3787 if (!gInstance)
michael@0 3788 return 0;
michael@0 3789
michael@0 3790 return gInstance->SizeOfExcludingThisInternal(mallocSizeOf);
michael@0 3791 }
michael@0 3792
michael@0 3793 // static
michael@0 3794 size_t
michael@0 3795 CacheFileIOManager::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf)
michael@0 3796 {
michael@0 3797 return mallocSizeOf(gInstance) + SizeOfExcludingThis(mallocSizeOf);
michael@0 3798 }
michael@0 3799
michael@0 3800 } // net
michael@0 3801 } // mozilla

mercurial