netwerk/cache2/CacheFile.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial