1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/netwerk/cache2/CacheFileChunk.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,717 @@ 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 "CacheFileChunk.h" 1.10 + 1.11 +#include "CacheFile.h" 1.12 +#include "nsThreadUtils.h" 1.13 +#include "nsAlgorithm.h" 1.14 +#include <algorithm> 1.15 + 1.16 +namespace mozilla { 1.17 +namespace net { 1.18 + 1.19 +#define kMinBufSize 512 1.20 + 1.21 +class NotifyUpdateListenerEvent : public nsRunnable { 1.22 +public: 1.23 + NotifyUpdateListenerEvent(CacheFileChunkListener *aCallback, 1.24 + CacheFileChunk *aChunk) 1.25 + : mCallback(aCallback) 1.26 + , mChunk(aChunk) 1.27 + { 1.28 + LOG(("NotifyUpdateListenerEvent::NotifyUpdateListenerEvent() [this=%p]", 1.29 + this)); 1.30 + MOZ_COUNT_CTOR(NotifyUpdateListenerEvent); 1.31 + } 1.32 + 1.33 + ~NotifyUpdateListenerEvent() 1.34 + { 1.35 + LOG(("NotifyUpdateListenerEvent::~NotifyUpdateListenerEvent() [this=%p]", 1.36 + this)); 1.37 + MOZ_COUNT_DTOR(NotifyUpdateListenerEvent); 1.38 + } 1.39 + 1.40 + NS_IMETHOD Run() 1.41 + { 1.42 + LOG(("NotifyUpdateListenerEvent::Run() [this=%p]", this)); 1.43 + 1.44 + mCallback->OnChunkUpdated(mChunk); 1.45 + return NS_OK; 1.46 + } 1.47 + 1.48 +protected: 1.49 + nsCOMPtr<CacheFileChunkListener> mCallback; 1.50 + nsRefPtr<CacheFileChunk> mChunk; 1.51 +}; 1.52 + 1.53 + 1.54 +class ValidityPair { 1.55 +public: 1.56 + ValidityPair(uint32_t aOffset, uint32_t aLen) 1.57 + : mOffset(aOffset), mLen(aLen) 1.58 + {} 1.59 + 1.60 + ValidityPair& operator=(const ValidityPair& aOther) { 1.61 + mOffset = aOther.mOffset; 1.62 + mLen = aOther.mLen; 1.63 + return *this; 1.64 + } 1.65 + 1.66 + bool Overlaps(const ValidityPair& aOther) const { 1.67 + if ((mOffset <= aOther.mOffset && mOffset + mLen >= aOther.mOffset) || 1.68 + (aOther.mOffset <= mOffset && aOther.mOffset + mLen >= mOffset)) 1.69 + return true; 1.70 + 1.71 + return false; 1.72 + } 1.73 + 1.74 + bool LessThan(const ValidityPair& aOther) const { 1.75 + if (mOffset < aOther.mOffset) 1.76 + return true; 1.77 + 1.78 + if (mOffset == aOther.mOffset && mLen < aOther.mLen) 1.79 + return true; 1.80 + 1.81 + return false; 1.82 + } 1.83 + 1.84 + void Merge(const ValidityPair& aOther) { 1.85 + MOZ_ASSERT(Overlaps(aOther)); 1.86 + 1.87 + uint32_t offset = std::min(mOffset, aOther.mOffset); 1.88 + uint32_t end = std::max(mOffset + mLen, aOther.mOffset + aOther.mLen); 1.89 + 1.90 + mOffset = offset; 1.91 + mLen = end - offset; 1.92 + } 1.93 + 1.94 + uint32_t Offset() { return mOffset; } 1.95 + uint32_t Len() { return mLen; } 1.96 + 1.97 +private: 1.98 + uint32_t mOffset; 1.99 + uint32_t mLen; 1.100 +}; 1.101 + 1.102 + 1.103 +NS_IMPL_ADDREF(CacheFileChunk) 1.104 +NS_IMETHODIMP_(MozExternalRefCountType) 1.105 +CacheFileChunk::Release() 1.106 +{ 1.107 + NS_PRECONDITION(0 != mRefCnt, "dup release"); 1.108 + nsrefcnt count = --mRefCnt; 1.109 + NS_LOG_RELEASE(this, count, "CacheFileChunk"); 1.110 + 1.111 + if (0 == count) { 1.112 + mRefCnt = 1; 1.113 + delete (this); 1.114 + return 0; 1.115 + } 1.116 + 1.117 + if (!mRemovingChunk && count == 1) { 1.118 + mFile->RemoveChunk(this); 1.119 + } 1.120 + 1.121 + return count; 1.122 +} 1.123 + 1.124 +NS_INTERFACE_MAP_BEGIN(CacheFileChunk) 1.125 + NS_INTERFACE_MAP_ENTRY(mozilla::net::CacheFileIOListener) 1.126 + NS_INTERFACE_MAP_ENTRY(nsISupports) 1.127 +NS_INTERFACE_MAP_END_THREADSAFE 1.128 + 1.129 +CacheFileChunk::CacheFileChunk(CacheFile *aFile, uint32_t aIndex) 1.130 + : CacheMemoryConsumer(aFile->mOpenAsMemoryOnly ? MEMORY_ONLY : DONT_REPORT) 1.131 + , mIndex(aIndex) 1.132 + , mState(INITIAL) 1.133 + , mStatus(NS_OK) 1.134 + , mIsDirty(false) 1.135 + , mRemovingChunk(false) 1.136 + , mDataSize(0) 1.137 + , mBuf(nullptr) 1.138 + , mBufSize(0) 1.139 + , mRWBuf(nullptr) 1.140 + , mRWBufSize(0) 1.141 + , mReadHash(0) 1.142 + , mFile(aFile) 1.143 +{ 1.144 + LOG(("CacheFileChunk::CacheFileChunk() [this=%p]", this)); 1.145 + MOZ_COUNT_CTOR(CacheFileChunk); 1.146 +} 1.147 + 1.148 +CacheFileChunk::~CacheFileChunk() 1.149 +{ 1.150 + LOG(("CacheFileChunk::~CacheFileChunk() [this=%p]", this)); 1.151 + MOZ_COUNT_DTOR(CacheFileChunk); 1.152 + 1.153 + if (mBuf) { 1.154 + free(mBuf); 1.155 + mBuf = nullptr; 1.156 + mBufSize = 0; 1.157 + } 1.158 + 1.159 + if (mRWBuf) { 1.160 + free(mRWBuf); 1.161 + mRWBuf = nullptr; 1.162 + mRWBufSize = 0; 1.163 + } 1.164 +} 1.165 + 1.166 +void 1.167 +CacheFileChunk::InitNew(CacheFileChunkListener *aCallback) 1.168 +{ 1.169 + mFile->AssertOwnsLock(); 1.170 + 1.171 + LOG(("CacheFileChunk::InitNew() [this=%p, listener=%p]", this, aCallback)); 1.172 + 1.173 + MOZ_ASSERT(mState == INITIAL); 1.174 + MOZ_ASSERT(!mBuf); 1.175 + MOZ_ASSERT(!mRWBuf); 1.176 + 1.177 + mBuf = static_cast<char *>(moz_xmalloc(kMinBufSize)); 1.178 + mBufSize = kMinBufSize; 1.179 + mDataSize = 0; 1.180 + mState = READY; 1.181 + mIsDirty = true; 1.182 + 1.183 + DoMemoryReport(MemorySize()); 1.184 +} 1.185 + 1.186 +nsresult 1.187 +CacheFileChunk::Read(CacheFileHandle *aHandle, uint32_t aLen, 1.188 + CacheHash::Hash16_t aHash, 1.189 + CacheFileChunkListener *aCallback) 1.190 +{ 1.191 + mFile->AssertOwnsLock(); 1.192 + 1.193 + LOG(("CacheFileChunk::Read() [this=%p, handle=%p, len=%d, listener=%p]", 1.194 + this, aHandle, aLen, aCallback)); 1.195 + 1.196 + MOZ_ASSERT(mState == INITIAL); 1.197 + MOZ_ASSERT(!mBuf); 1.198 + MOZ_ASSERT(!mRWBuf); 1.199 + MOZ_ASSERT(aLen); 1.200 + 1.201 + nsresult rv; 1.202 + 1.203 + mRWBuf = static_cast<char *>(moz_xmalloc(aLen)); 1.204 + mRWBufSize = aLen; 1.205 + 1.206 + DoMemoryReport(MemorySize()); 1.207 + 1.208 + rv = CacheFileIOManager::Read(aHandle, mIndex * kChunkSize, mRWBuf, aLen, 1.209 + true, this); 1.210 + if (NS_WARN_IF(NS_FAILED(rv))) { 1.211 + rv = mIndex ? NS_ERROR_FILE_CORRUPTED : NS_ERROR_FILE_NOT_FOUND; 1.212 + SetError(rv); 1.213 + } else { 1.214 + mState = READING; 1.215 + mListener = aCallback; 1.216 + mDataSize = aLen; 1.217 + mReadHash = aHash; 1.218 + } 1.219 + 1.220 + return rv; 1.221 +} 1.222 + 1.223 +nsresult 1.224 +CacheFileChunk::Write(CacheFileHandle *aHandle, 1.225 + CacheFileChunkListener *aCallback) 1.226 +{ 1.227 + mFile->AssertOwnsLock(); 1.228 + 1.229 + LOG(("CacheFileChunk::Write() [this=%p, handle=%p, listener=%p]", 1.230 + this, aHandle, aCallback)); 1.231 + 1.232 + MOZ_ASSERT(mState == READY); 1.233 + MOZ_ASSERT(!mRWBuf); 1.234 + MOZ_ASSERT(mBuf); 1.235 + MOZ_ASSERT(mDataSize); // Don't write chunk when it is empty 1.236 + 1.237 + nsresult rv; 1.238 + 1.239 + mRWBuf = mBuf; 1.240 + mRWBufSize = mBufSize; 1.241 + mBuf = nullptr; 1.242 + mBufSize = 0; 1.243 + 1.244 + rv = CacheFileIOManager::Write(aHandle, mIndex * kChunkSize, mRWBuf, 1.245 + mDataSize, false, this); 1.246 + if (NS_WARN_IF(NS_FAILED(rv))) { 1.247 + SetError(rv); 1.248 + } else { 1.249 + mState = WRITING; 1.250 + mListener = aCallback; 1.251 + mIsDirty = false; 1.252 + } 1.253 + 1.254 + return rv; 1.255 +} 1.256 + 1.257 +void 1.258 +CacheFileChunk::WaitForUpdate(CacheFileChunkListener *aCallback) 1.259 +{ 1.260 + mFile->AssertOwnsLock(); 1.261 + 1.262 + LOG(("CacheFileChunk::WaitForUpdate() [this=%p, listener=%p]", 1.263 + this, aCallback)); 1.264 + 1.265 + MOZ_ASSERT(mFile->mOutput); 1.266 + MOZ_ASSERT(IsReady()); 1.267 + 1.268 +#ifdef DEBUG 1.269 + for (uint32_t i = 0 ; i < mUpdateListeners.Length() ; i++) { 1.270 + MOZ_ASSERT(mUpdateListeners[i]->mCallback != aCallback); 1.271 + } 1.272 +#endif 1.273 + 1.274 + ChunkListenerItem *item = new ChunkListenerItem(); 1.275 + item->mTarget = NS_GetCurrentThread(); 1.276 + item->mCallback = aCallback; 1.277 + 1.278 + mUpdateListeners.AppendElement(item); 1.279 +} 1.280 + 1.281 +nsresult 1.282 +CacheFileChunk::CancelWait(CacheFileChunkListener *aCallback) 1.283 +{ 1.284 + mFile->AssertOwnsLock(); 1.285 + 1.286 + LOG(("CacheFileChunk::CancelWait() [this=%p, listener=%p]", this, aCallback)); 1.287 + 1.288 + MOZ_ASSERT(IsReady()); 1.289 + 1.290 + uint32_t i; 1.291 + for (i = 0 ; i < mUpdateListeners.Length() ; i++) { 1.292 + ChunkListenerItem *item = mUpdateListeners[i]; 1.293 + 1.294 + if (item->mCallback == aCallback) { 1.295 + mUpdateListeners.RemoveElementAt(i); 1.296 + delete item; 1.297 + break; 1.298 + } 1.299 + } 1.300 + 1.301 +#ifdef DEBUG 1.302 + for ( ; i < mUpdateListeners.Length() ; i++) { 1.303 + MOZ_ASSERT(mUpdateListeners[i]->mCallback != aCallback); 1.304 + } 1.305 +#endif 1.306 + 1.307 + return NS_OK; 1.308 +} 1.309 + 1.310 +nsresult 1.311 +CacheFileChunk::NotifyUpdateListeners() 1.312 +{ 1.313 + mFile->AssertOwnsLock(); 1.314 + 1.315 + LOG(("CacheFileChunk::NotifyUpdateListeners() [this=%p]", this)); 1.316 + 1.317 + MOZ_ASSERT(IsReady()); 1.318 + 1.319 + nsresult rv, rv2; 1.320 + 1.321 + rv = NS_OK; 1.322 + for (uint32_t i = 0 ; i < mUpdateListeners.Length() ; i++) { 1.323 + ChunkListenerItem *item = mUpdateListeners[i]; 1.324 + 1.325 + LOG(("CacheFileChunk::NotifyUpdateListeners() - Notifying listener %p " 1.326 + "[this=%p]", item->mCallback.get(), this)); 1.327 + 1.328 + nsRefPtr<NotifyUpdateListenerEvent> ev; 1.329 + ev = new NotifyUpdateListenerEvent(item->mCallback, this); 1.330 + rv2 = item->mTarget->Dispatch(ev, NS_DISPATCH_NORMAL); 1.331 + if (NS_FAILED(rv2) && NS_SUCCEEDED(rv)) 1.332 + rv = rv2; 1.333 + delete item; 1.334 + } 1.335 + 1.336 + mUpdateListeners.Clear(); 1.337 + 1.338 + return rv; 1.339 +} 1.340 + 1.341 +uint32_t 1.342 +CacheFileChunk::Index() 1.343 +{ 1.344 + return mIndex; 1.345 +} 1.346 + 1.347 +CacheHash::Hash16_t 1.348 +CacheFileChunk::Hash() 1.349 +{ 1.350 + mFile->AssertOwnsLock(); 1.351 + 1.352 + MOZ_ASSERT(mBuf); 1.353 + MOZ_ASSERT(!mListener); 1.354 + MOZ_ASSERT(IsReady()); 1.355 + 1.356 + return CacheHash::Hash16(BufForReading(), mDataSize); 1.357 +} 1.358 + 1.359 +uint32_t 1.360 +CacheFileChunk::DataSize() 1.361 +{ 1.362 + mFile->AssertOwnsLock(); 1.363 + return mDataSize; 1.364 +} 1.365 + 1.366 +void 1.367 +CacheFileChunk::UpdateDataSize(uint32_t aOffset, uint32_t aLen, bool aEOF) 1.368 +{ 1.369 + mFile->AssertOwnsLock(); 1.370 + 1.371 + MOZ_ASSERT(!aEOF, "Implement me! What to do with opened streams?"); 1.372 + MOZ_ASSERT(aOffset <= mDataSize); 1.373 + 1.374 + // UpdateDataSize() is called only when we've written some data to the chunk 1.375 + // and we never write data anymore once some error occurs. 1.376 + MOZ_ASSERT(mState != ERROR); 1.377 + 1.378 + LOG(("CacheFileChunk::UpdateDataSize() [this=%p, offset=%d, len=%d, EOF=%d]", 1.379 + this, aOffset, aLen, aEOF)); 1.380 + 1.381 + mIsDirty = true; 1.382 + 1.383 + int64_t fileSize = kChunkSize * mIndex + aOffset + aLen; 1.384 + bool notify = false; 1.385 + 1.386 + if (fileSize > mFile->mDataSize) 1.387 + mFile->mDataSize = fileSize; 1.388 + 1.389 + if (aOffset + aLen > mDataSize) { 1.390 + mDataSize = aOffset + aLen; 1.391 + notify = true; 1.392 + } 1.393 + 1.394 + if (mState == READY || mState == WRITING) { 1.395 + MOZ_ASSERT(mValidityMap.Length() == 0); 1.396 + 1.397 + if (notify) 1.398 + NotifyUpdateListeners(); 1.399 + 1.400 + return; 1.401 + } 1.402 + 1.403 + // We're still waiting for data from the disk. This chunk cannot be used by 1.404 + // input stream, so there must be no update listener. We also need to keep 1.405 + // track of where the data is written so that we can correctly merge the new 1.406 + // data with the old one. 1.407 + 1.408 + MOZ_ASSERT(mUpdateListeners.Length() == 0); 1.409 + MOZ_ASSERT(mState == READING); 1.410 + 1.411 + ValidityPair pair(aOffset, aLen); 1.412 + 1.413 + if (mValidityMap.Length() == 0) { 1.414 + mValidityMap.AppendElement(pair); 1.415 + return; 1.416 + } 1.417 + 1.418 + 1.419 + // Find out where to place this pair into the map, it can overlap with 1.420 + // one preceding pair and all subsequent pairs. 1.421 + uint32_t pos = 0; 1.422 + for (pos = mValidityMap.Length() ; pos > 0 ; pos--) { 1.423 + if (mValidityMap[pos-1].LessThan(pair)) { 1.424 + if (mValidityMap[pos-1].Overlaps(pair)) { 1.425 + // Merge with the preceding pair 1.426 + mValidityMap[pos-1].Merge(pair); 1.427 + pos--; // Point to the updated pair 1.428 + } 1.429 + else { 1.430 + if (pos == mValidityMap.Length()) 1.431 + mValidityMap.AppendElement(pair); 1.432 + else 1.433 + mValidityMap.InsertElementAt(pos, pair); 1.434 + } 1.435 + 1.436 + break; 1.437 + } 1.438 + } 1.439 + 1.440 + if (!pos) 1.441 + mValidityMap.InsertElementAt(0, pair); 1.442 + 1.443 + // Now pos points to merged or inserted pair, check whether it overlaps with 1.444 + // subsequent pairs. 1.445 + while (pos + 1 < mValidityMap.Length()) { 1.446 + if (mValidityMap[pos].Overlaps(mValidityMap[pos + 1])) { 1.447 + mValidityMap[pos].Merge(mValidityMap[pos + 1]); 1.448 + mValidityMap.RemoveElementAt(pos + 1); 1.449 + } 1.450 + else { 1.451 + break; 1.452 + } 1.453 + } 1.454 +} 1.455 + 1.456 +nsresult 1.457 +CacheFileChunk::OnFileOpened(CacheFileHandle *aHandle, nsresult aResult) 1.458 +{ 1.459 + MOZ_CRASH("CacheFileChunk::OnFileOpened should not be called!"); 1.460 + return NS_ERROR_UNEXPECTED; 1.461 +} 1.462 + 1.463 +nsresult 1.464 +CacheFileChunk::OnDataWritten(CacheFileHandle *aHandle, const char *aBuf, 1.465 + nsresult aResult) 1.466 +{ 1.467 + LOG(("CacheFileChunk::OnDataWritten() [this=%p, handle=%p, result=0x%08x]", 1.468 + this, aHandle, aResult)); 1.469 + 1.470 + nsCOMPtr<CacheFileChunkListener> listener; 1.471 + 1.472 + { 1.473 + CacheFileAutoLock lock(mFile); 1.474 + 1.475 + MOZ_ASSERT(mState == WRITING); 1.476 + MOZ_ASSERT(mListener); 1.477 + 1.478 + if (NS_WARN_IF(NS_FAILED(aResult))) { 1.479 + SetError(aResult); 1.480 + } else { 1.481 + mState = READY; 1.482 + } 1.483 + 1.484 + if (!mBuf) { 1.485 + mBuf = mRWBuf; 1.486 + mBufSize = mRWBufSize; 1.487 + } else { 1.488 + free(mRWBuf); 1.489 + } 1.490 + 1.491 + mRWBuf = nullptr; 1.492 + mRWBufSize = 0; 1.493 + 1.494 + DoMemoryReport(MemorySize()); 1.495 + 1.496 + mListener.swap(listener); 1.497 + } 1.498 + 1.499 + listener->OnChunkWritten(aResult, this); 1.500 + 1.501 + return NS_OK; 1.502 +} 1.503 + 1.504 +nsresult 1.505 +CacheFileChunk::OnDataRead(CacheFileHandle *aHandle, char *aBuf, 1.506 + nsresult aResult) 1.507 +{ 1.508 + LOG(("CacheFileChunk::OnDataRead() [this=%p, handle=%p, result=0x%08x]", 1.509 + this, aHandle, aResult)); 1.510 + 1.511 + nsCOMPtr<CacheFileChunkListener> listener; 1.512 + 1.513 + { 1.514 + CacheFileAutoLock lock(mFile); 1.515 + 1.516 + MOZ_ASSERT(mState == READING); 1.517 + MOZ_ASSERT(mListener); 1.518 + 1.519 + if (NS_SUCCEEDED(aResult)) { 1.520 + CacheHash::Hash16_t hash = CacheHash::Hash16(mRWBuf, mRWBufSize); 1.521 + if (hash != mReadHash) { 1.522 + LOG(("CacheFileChunk::OnDataRead() - Hash mismatch! Hash of the data is" 1.523 + " %hx, hash in metadata is %hx. [this=%p, idx=%d]", 1.524 + hash, mReadHash, this, mIndex)); 1.525 + aResult = NS_ERROR_FILE_CORRUPTED; 1.526 + } 1.527 + else { 1.528 + if (!mBuf) { 1.529 + // Just swap the buffers if we don't have mBuf yet 1.530 + MOZ_ASSERT(mDataSize == mRWBufSize); 1.531 + mBuf = mRWBuf; 1.532 + mBufSize = mRWBufSize; 1.533 + mRWBuf = nullptr; 1.534 + mRWBufSize = 0; 1.535 + } else { 1.536 + // Merge data with write buffer 1.537 + if (mRWBufSize < mBufSize) { 1.538 + mRWBuf = static_cast<char *>(moz_xrealloc(mRWBuf, mBufSize)); 1.539 + mRWBufSize = mBufSize; 1.540 + } 1.541 + 1.542 + for (uint32_t i = 0 ; i < mValidityMap.Length() ; i++) { 1.543 + memcpy(mRWBuf + mValidityMap[i].Offset(), 1.544 + mBuf + mValidityMap[i].Offset(), mValidityMap[i].Len()); 1.545 + } 1.546 + 1.547 + free(mBuf); 1.548 + mBuf = mRWBuf; 1.549 + mBufSize = mRWBufSize; 1.550 + mRWBuf = nullptr; 1.551 + mRWBufSize = 0; 1.552 + 1.553 + DoMemoryReport(MemorySize()); 1.554 + } 1.555 + } 1.556 + } 1.557 + 1.558 + if (NS_FAILED(aResult)) { 1.559 + aResult = mIndex ? NS_ERROR_FILE_CORRUPTED : NS_ERROR_FILE_NOT_FOUND; 1.560 + SetError(aResult); 1.561 + mDataSize = 0; 1.562 + } else { 1.563 + mState = READY; 1.564 + } 1.565 + 1.566 + mListener.swap(listener); 1.567 + } 1.568 + 1.569 + listener->OnChunkRead(aResult, this); 1.570 + 1.571 + return NS_OK; 1.572 +} 1.573 + 1.574 +nsresult 1.575 +CacheFileChunk::OnFileDoomed(CacheFileHandle *aHandle, nsresult aResult) 1.576 +{ 1.577 + MOZ_CRASH("CacheFileChunk::OnFileDoomed should not be called!"); 1.578 + return NS_ERROR_UNEXPECTED; 1.579 +} 1.580 + 1.581 +nsresult 1.582 +CacheFileChunk::OnEOFSet(CacheFileHandle *aHandle, nsresult aResult) 1.583 +{ 1.584 + MOZ_CRASH("CacheFileChunk::OnEOFSet should not be called!"); 1.585 + return NS_ERROR_UNEXPECTED; 1.586 +} 1.587 + 1.588 +nsresult 1.589 +CacheFileChunk::OnFileRenamed(CacheFileHandle *aHandle, nsresult aResult) 1.590 +{ 1.591 + MOZ_CRASH("CacheFileChunk::OnFileRenamed should not be called!"); 1.592 + return NS_ERROR_UNEXPECTED; 1.593 +} 1.594 + 1.595 +bool 1.596 +CacheFileChunk::IsReady() const 1.597 +{ 1.598 + mFile->AssertOwnsLock(); 1.599 + 1.600 + return (NS_SUCCEEDED(mStatus) && (mState == READY || mState == WRITING)); 1.601 +} 1.602 + 1.603 +bool 1.604 +CacheFileChunk::IsDirty() const 1.605 +{ 1.606 + mFile->AssertOwnsLock(); 1.607 + 1.608 + return mIsDirty; 1.609 +} 1.610 + 1.611 +nsresult 1.612 +CacheFileChunk::GetStatus() 1.613 +{ 1.614 + mFile->AssertOwnsLock(); 1.615 + 1.616 + return mStatus; 1.617 +} 1.618 + 1.619 +void 1.620 +CacheFileChunk::SetError(nsresult aStatus) 1.621 +{ 1.622 + if (NS_SUCCEEDED(mStatus)) { 1.623 + MOZ_ASSERT(mState != ERROR); 1.624 + mStatus = aStatus; 1.625 + mState = ERROR; 1.626 + } else { 1.627 + MOZ_ASSERT(mState == ERROR); 1.628 + } 1.629 +} 1.630 + 1.631 +char * 1.632 +CacheFileChunk::BufForWriting() const 1.633 +{ 1.634 + mFile->AssertOwnsLock(); 1.635 + 1.636 + MOZ_ASSERT(mBuf); // Writer should always first call EnsureBufSize() 1.637 + 1.638 + MOZ_ASSERT((mState == READY && !mRWBuf) || 1.639 + (mState == WRITING && mRWBuf) || 1.640 + (mState == READING && mRWBuf)); 1.641 + 1.642 + return mBuf; 1.643 +} 1.644 + 1.645 +const char * 1.646 +CacheFileChunk::BufForReading() const 1.647 +{ 1.648 + mFile->AssertOwnsLock(); 1.649 + 1.650 + MOZ_ASSERT((mState == READY && mBuf && !mRWBuf) || 1.651 + (mState == WRITING && mRWBuf)); 1.652 + 1.653 + return mBuf ? mBuf : mRWBuf; 1.654 +} 1.655 + 1.656 +void 1.657 +CacheFileChunk::EnsureBufSize(uint32_t aBufSize) 1.658 +{ 1.659 + mFile->AssertOwnsLock(); 1.660 + 1.661 + // EnsureBufSize() is called only when we want to write some data to the chunk 1.662 + // and we never write data anymore once some error occurs. 1.663 + MOZ_ASSERT(mState != ERROR); 1.664 + 1.665 + if (mBufSize >= aBufSize) 1.666 + return; 1.667 + 1.668 + bool copy = false; 1.669 + if (!mBuf && mState == WRITING) { 1.670 + // We need to duplicate the data that is being written on the background 1.671 + // thread, so make sure that all the data fits into the new buffer. 1.672 + copy = true; 1.673 + 1.674 + if (mRWBufSize > aBufSize) 1.675 + aBufSize = mRWBufSize; 1.676 + } 1.677 + 1.678 + // find smallest power of 2 greater than or equal to aBufSize 1.679 + aBufSize--; 1.680 + aBufSize |= aBufSize >> 1; 1.681 + aBufSize |= aBufSize >> 2; 1.682 + aBufSize |= aBufSize >> 4; 1.683 + aBufSize |= aBufSize >> 8; 1.684 + aBufSize |= aBufSize >> 16; 1.685 + aBufSize++; 1.686 + 1.687 + const uint32_t minBufSize = kMinBufSize; 1.688 + const uint32_t maxBufSize = kChunkSize; 1.689 + aBufSize = clamped(aBufSize, minBufSize, maxBufSize); 1.690 + 1.691 + mBuf = static_cast<char *>(moz_xrealloc(mBuf, aBufSize)); 1.692 + mBufSize = aBufSize; 1.693 + 1.694 + if (copy) 1.695 + memcpy(mBuf, mRWBuf, mRWBufSize); 1.696 + 1.697 + DoMemoryReport(MemorySize()); 1.698 +} 1.699 + 1.700 +// Memory reporting 1.701 + 1.702 +size_t 1.703 +CacheFileChunk::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const 1.704 +{ 1.705 + size_t n = 0; 1.706 + n += mallocSizeOf(mBuf); 1.707 + n += mallocSizeOf(mRWBuf); 1.708 + n += mValidityMap.SizeOfExcludingThis(mallocSizeOf); 1.709 + 1.710 + return n; 1.711 +} 1.712 + 1.713 +size_t 1.714 +CacheFileChunk::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const 1.715 +{ 1.716 + return mallocSizeOf(this) + SizeOfExcludingThis(mallocSizeOf); 1.717 +} 1.718 + 1.719 +} // net 1.720 +} // mozilla