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