nsresult CacheFile::OnChunkWritten(nsresult aResult, CacheFileChunk *aChunk) { CacheFileAutoLock lock(this); nsresult rv; LOG(("CacheFile::OnChunkWritten() [this=%p, rv=0x%08x, chunk=%p, idx=%d]", this, aResult, aChunk, aChunk->Index())); MOZ_ASSERT(!mMemoryOnly); // TODO handle ERROR state if (NS_FAILED(aResult)) { // TODO ??? doom entry // TODO mark this chunk as memory only, since it wasn't written to disk and // therefore cannot be released from memory // LOG } if (NS_SUCCEEDED(aResult) && !aChunk->IsDirty()) { // update hash value in metadata mMetadata->SetHash(aChunk->Index(), aChunk->Hash()); } // notify listeners if there is any if (HaveChunkListeners(aChunk->Index())) { // don't release the chunk since there are some listeners queued rv = NotifyChunkListeners(aChunk->Index(), NS_OK, aChunk); if (NS_SUCCEEDED(rv)) { MOZ_ASSERT(aChunk->mRefCnt != 2); return NS_OK; } } if (aChunk->mRefCnt != 2) { LOG(("CacheFile::OnChunkWritten() - Chunk is still used [this=%p, chunk=%p," " refcnt=%d]", this, aChunk, aChunk->mRefCnt.get())); return NS_OK; } LOG(("CacheFile::OnChunkWritten() - Caching unused chunk [this=%p, chunk=%p]", this, aChunk)); aChunk->mRemovingChunk = true; ReleaseOutsideLock(static_cast<CacheFileChunkListener *>( aChunk->mFile.forget().get())); mCachedChunks.Put(aChunk->Index(), aChunk); mChunks.Remove(aChunk->Index()); WriteMetadataIfNeededLocked(); return NS_OK; }
nsresult CacheFile::OnChunkRead(nsresult aResult, CacheFileChunk *aChunk) { CacheFileAutoLock lock(this); nsresult rv; uint32_t index = aChunk->Index(); LOG(("CacheFile::OnChunkRead() [this=%p, rv=0x%08x, chunk=%p, idx=%d]", this, aResult, aChunk, index)); // TODO handle ERROR state if (HaveChunkListeners(index)) { rv = NotifyChunkListeners(index, aResult, aChunk); NS_ENSURE_SUCCESS(rv, rv); } return NS_OK; }
nsresult CacheFile::GetChunkLocked(uint32_t aIndex, bool aWriter, CacheFileChunkListener *aCallback, CacheFileChunk **_retval) { AssertOwnsLock(); LOG(("CacheFile::GetChunkLocked() [this=%p, idx=%d, writer=%d, listener=%p]", this, aIndex, aWriter, aCallback)); MOZ_ASSERT(mReady); MOZ_ASSERT(mHandle || mMemoryOnly || mOpeningFile); MOZ_ASSERT((aWriter && !aCallback) || (!aWriter && aCallback)); nsresult rv; nsRefPtr<CacheFileChunk> chunk; if (mChunks.Get(aIndex, getter_AddRefs(chunk))) { LOG(("CacheFile::GetChunkLocked() - Found chunk %p in mChunks [this=%p]", chunk.get(), this)); if (chunk->IsReady() || aWriter) { chunk.swap(*_retval); } else { rv = QueueChunkListener(aIndex, aCallback); NS_ENSURE_SUCCESS(rv, rv); } return NS_OK; } if (mCachedChunks.Get(aIndex, getter_AddRefs(chunk))) { LOG(("CacheFile::GetChunkLocked() - Reusing cached chunk %p [this=%p]", chunk.get(), this)); mChunks.Put(aIndex, chunk); mCachedChunks.Remove(aIndex); chunk->mFile = this; chunk->mRemovingChunk = false; MOZ_ASSERT(chunk->IsReady()); chunk.swap(*_retval); return NS_OK; } int64_t off = aIndex * kChunkSize; if (off < mDataSize) { // We cannot be here if this is memory only entry since the chunk must exist MOZ_ASSERT(!mMemoryOnly); chunk = new CacheFileChunk(this, aIndex); mChunks.Put(aIndex, chunk); LOG(("CacheFile::GetChunkLocked() - Reading newly created chunk %p from " "the disk [this=%p]", chunk.get(), this)); // Read the chunk from the disk rv = chunk->Read(mHandle, std::min(static_cast<uint32_t>(mDataSize - off), static_cast<uint32_t>(kChunkSize)), mMetadata->GetHash(aIndex), this); if (NS_FAILED(rv)) { chunk->mRemovingChunk = true; ReleaseOutsideLock(static_cast<CacheFileChunkListener *>( chunk->mFile.forget().get())); mChunks.Remove(aIndex); NS_ENSURE_SUCCESS(rv, rv); } if (aWriter) { chunk.swap(*_retval); } else { rv = QueueChunkListener(aIndex, aCallback); NS_ENSURE_SUCCESS(rv, rv); } return NS_OK; } else if (off == mDataSize) { if (aWriter) { // this listener is going to write to the chunk chunk = new CacheFileChunk(this, aIndex); mChunks.Put(aIndex, chunk); LOG(("CacheFile::GetChunkLocked() - Created new empty chunk %p [this=%p]", chunk.get(), this)); chunk->InitNew(this); mMetadata->SetHash(aIndex, chunk->Hash()); if (HaveChunkListeners(aIndex)) { rv = NotifyChunkListeners(aIndex, NS_OK, chunk); NS_ENSURE_SUCCESS(rv, rv); } chunk.swap(*_retval); return NS_OK; } } else { if (aWriter) { // this chunk was requested by writer, but we need to fill the gap first // Fill with zero the last chunk if it is incomplete if (mDataSize % kChunkSize) { rv = PadChunkWithZeroes(mDataSize / kChunkSize); NS_ENSURE_SUCCESS(rv, rv); MOZ_ASSERT(!(mDataSize % kChunkSize)); } uint32_t startChunk = mDataSize / kChunkSize; if (mMemoryOnly) { // We need to create all missing CacheFileChunks if this is memory-only // entry for (uint32_t i = startChunk ; i < aIndex ; i++) { rv = PadChunkWithZeroes(i); NS_ENSURE_SUCCESS(rv, rv); } } else { // We don't need to create CacheFileChunk for other empty chunks unless // there is some input stream waiting for this chunk. if (startChunk != aIndex) { // Make sure the file contains zeroes at the end of the file rv = CacheFileIOManager::TruncateSeekSetEOF(mHandle, startChunk * kChunkSize, aIndex * kChunkSize, nullptr); NS_ENSURE_SUCCESS(rv, rv); } for (uint32_t i = startChunk ; i < aIndex ; i++) { if (HaveChunkListeners(i)) { rv = PadChunkWithZeroes(i); NS_ENSURE_SUCCESS(rv, rv); } else { mMetadata->SetHash(i, kEmptyChunkHash); mDataSize = (i + 1) * kChunkSize; } } } MOZ_ASSERT(mDataSize == off); rv = GetChunkLocked(aIndex, true, nullptr, getter_AddRefs(chunk)); NS_ENSURE_SUCCESS(rv, rv); chunk.swap(*_retval); return NS_OK; } } if (mOutput) { // the chunk doesn't exist but mOutput may create it rv = QueueChunkListener(aIndex, aCallback); NS_ENSURE_SUCCESS(rv, rv); } else { return NS_ERROR_NOT_AVAILABLE; } return NS_OK; }