1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/netwerk/cache2/CacheFileOutputStream.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,413 @@ 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 "CacheFileOutputStream.h" 1.10 + 1.11 +#include "CacheFile.h" 1.12 +#include "CacheEntry.h" 1.13 +#include "nsStreamUtils.h" 1.14 +#include "nsThreadUtils.h" 1.15 +#include "mozilla/DebugOnly.h" 1.16 +#include <algorithm> 1.17 + 1.18 +namespace mozilla { 1.19 +namespace net { 1.20 + 1.21 +NS_IMPL_ADDREF(CacheFileOutputStream) 1.22 +NS_IMETHODIMP_(MozExternalRefCountType) 1.23 +CacheFileOutputStream::Release() 1.24 +{ 1.25 + NS_PRECONDITION(0 != mRefCnt, "dup release"); 1.26 + nsrefcnt count = --mRefCnt; 1.27 + NS_LOG_RELEASE(this, count, "CacheFileOutputStream"); 1.28 + 1.29 + if (0 == count) { 1.30 + mRefCnt = 1; 1.31 + { 1.32 + CacheFileAutoLock lock(mFile); 1.33 + mFile->RemoveOutput(this); 1.34 + } 1.35 + delete (this); 1.36 + return 0; 1.37 + } 1.38 + 1.39 + return count; 1.40 +} 1.41 + 1.42 +NS_INTERFACE_MAP_BEGIN(CacheFileOutputStream) 1.43 + NS_INTERFACE_MAP_ENTRY(nsIOutputStream) 1.44 + NS_INTERFACE_MAP_ENTRY(nsIAsyncOutputStream) 1.45 + NS_INTERFACE_MAP_ENTRY(nsISeekableStream) 1.46 + NS_INTERFACE_MAP_ENTRY(mozilla::net::CacheFileChunkListener) 1.47 + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIOutputStream) 1.48 +NS_INTERFACE_MAP_END_THREADSAFE 1.49 + 1.50 +CacheFileOutputStream::CacheFileOutputStream(CacheFile *aFile, 1.51 + CacheOutputCloseListener *aCloseListener) 1.52 + : mFile(aFile) 1.53 + , mCloseListener(aCloseListener) 1.54 + , mPos(0) 1.55 + , mClosed(false) 1.56 + , mStatus(NS_OK) 1.57 + , mCallbackFlags(0) 1.58 +{ 1.59 + LOG(("CacheFileOutputStream::CacheFileOutputStream() [this=%p]", this)); 1.60 + MOZ_COUNT_CTOR(CacheFileOutputStream); 1.61 +} 1.62 + 1.63 +CacheFileOutputStream::~CacheFileOutputStream() 1.64 +{ 1.65 + LOG(("CacheFileOutputStream::~CacheFileOutputStream() [this=%p]", this)); 1.66 + MOZ_COUNT_DTOR(CacheFileOutputStream); 1.67 +} 1.68 + 1.69 +// nsIOutputStream 1.70 +NS_IMETHODIMP 1.71 +CacheFileOutputStream::Close() 1.72 +{ 1.73 + LOG(("CacheFileOutputStream::Close() [this=%p]", this)); 1.74 + return CloseWithStatus(NS_OK); 1.75 +} 1.76 + 1.77 +NS_IMETHODIMP 1.78 +CacheFileOutputStream::Flush() 1.79 +{ 1.80 + // TODO do we need to implement flush ??? 1.81 + LOG(("CacheFileOutputStream::Flush() [this=%p]", this)); 1.82 + return NS_OK; 1.83 +} 1.84 + 1.85 +NS_IMETHODIMP 1.86 +CacheFileOutputStream::Write(const char * aBuf, uint32_t aCount, 1.87 + uint32_t *_retval) 1.88 +{ 1.89 + CacheFileAutoLock lock(mFile); 1.90 + 1.91 + LOG(("CacheFileOutputStream::Write() [this=%p, count=%d]", this, aCount)); 1.92 + 1.93 + if (mClosed) { 1.94 + LOG(("CacheFileOutputStream::Write() - Stream is closed. [this=%p, " 1.95 + "status=0x%08x]", this, mStatus)); 1.96 + 1.97 + return NS_FAILED(mStatus) ? mStatus : NS_BASE_STREAM_CLOSED; 1.98 + } 1.99 + 1.100 + *_retval = aCount; 1.101 + 1.102 + while (aCount) { 1.103 + EnsureCorrectChunk(false); 1.104 + if (NS_FAILED(mStatus)) 1.105 + return mStatus; 1.106 + 1.107 + FillHole(); 1.108 + 1.109 + uint32_t chunkOffset = mPos - (mPos / kChunkSize) * kChunkSize; 1.110 + uint32_t canWrite = kChunkSize - chunkOffset; 1.111 + uint32_t thisWrite = std::min(static_cast<uint32_t>(canWrite), aCount); 1.112 + mChunk->EnsureBufSize(chunkOffset + thisWrite); 1.113 + memcpy(mChunk->BufForWriting() + chunkOffset, aBuf, thisWrite); 1.114 + 1.115 + mPos += thisWrite; 1.116 + aBuf += thisWrite; 1.117 + aCount -= thisWrite; 1.118 + 1.119 + mChunk->UpdateDataSize(chunkOffset, thisWrite, false); 1.120 + } 1.121 + 1.122 + EnsureCorrectChunk(true); 1.123 + 1.124 + LOG(("CacheFileOutputStream::Write() - Wrote %d bytes [this=%p]", 1.125 + *_retval, this)); 1.126 + 1.127 + return NS_OK; 1.128 +} 1.129 + 1.130 +NS_IMETHODIMP 1.131 +CacheFileOutputStream::WriteFrom(nsIInputStream *aFromStream, uint32_t aCount, 1.132 + uint32_t *_retval) 1.133 +{ 1.134 + LOG(("CacheFileOutputStream::WriteFrom() - NOT_IMPLEMENTED [this=%p, from=%p" 1.135 + ", count=%d]", this, aFromStream, aCount)); 1.136 + 1.137 + return NS_ERROR_NOT_IMPLEMENTED; 1.138 +} 1.139 + 1.140 +NS_IMETHODIMP 1.141 +CacheFileOutputStream::WriteSegments(nsReadSegmentFun aReader, void *aClosure, 1.142 + uint32_t aCount, uint32_t *_retval) 1.143 +{ 1.144 + LOG(("CacheFileOutputStream::WriteSegments() - NOT_IMPLEMENTED [this=%p, " 1.145 + "count=%d]", this, aCount)); 1.146 + 1.147 + return NS_ERROR_NOT_IMPLEMENTED; 1.148 +} 1.149 + 1.150 +NS_IMETHODIMP 1.151 +CacheFileOutputStream::IsNonBlocking(bool *_retval) 1.152 +{ 1.153 + *_retval = false; 1.154 + return NS_OK; 1.155 +} 1.156 + 1.157 +// nsIAsyncOutputStream 1.158 +NS_IMETHODIMP 1.159 +CacheFileOutputStream::CloseWithStatus(nsresult aStatus) 1.160 +{ 1.161 + CacheFileAutoLock lock(mFile); 1.162 + 1.163 + LOG(("CacheFileOutputStream::CloseWithStatus() [this=%p, aStatus=0x%08x]", 1.164 + this, aStatus)); 1.165 + 1.166 + if (mClosed) { 1.167 + MOZ_ASSERT(!mCallback); 1.168 + return NS_OK; 1.169 + } 1.170 + 1.171 + mClosed = true; 1.172 + mStatus = NS_FAILED(aStatus) ? aStatus : NS_BASE_STREAM_CLOSED; 1.173 + 1.174 + if (mChunk) 1.175 + ReleaseChunk(); 1.176 + 1.177 + if (mCallback) 1.178 + NotifyListener(); 1.179 + 1.180 + mFile->RemoveOutput(this); 1.181 + 1.182 + return NS_OK; 1.183 +} 1.184 + 1.185 +NS_IMETHODIMP 1.186 +CacheFileOutputStream::AsyncWait(nsIOutputStreamCallback *aCallback, 1.187 + uint32_t aFlags, 1.188 + uint32_t aRequestedCount, 1.189 + nsIEventTarget *aEventTarget) 1.190 +{ 1.191 + CacheFileAutoLock lock(mFile); 1.192 + 1.193 + LOG(("CacheFileOutputStream::AsyncWait() [this=%p, callback=%p, flags=%d, " 1.194 + "requestedCount=%d, eventTarget=%p]", this, aCallback, aFlags, 1.195 + aRequestedCount, aEventTarget)); 1.196 + 1.197 + mCallback = aCallback; 1.198 + mCallbackFlags = aFlags; 1.199 + 1.200 + if (!mCallback) 1.201 + return NS_OK; 1.202 + 1.203 + // The stream is blocking so it is writable at any time 1.204 + if (mClosed || !(aFlags & WAIT_CLOSURE_ONLY)) 1.205 + NotifyListener(); 1.206 + 1.207 + return NS_OK; 1.208 +} 1.209 + 1.210 +// nsISeekableStream 1.211 +NS_IMETHODIMP 1.212 +CacheFileOutputStream::Seek(int32_t whence, int64_t offset) 1.213 +{ 1.214 + CacheFileAutoLock lock(mFile); 1.215 + 1.216 + LOG(("CacheFileOutputStream::Seek() [this=%p, whence=%d, offset=%lld]", 1.217 + this, whence, offset)); 1.218 + 1.219 + if (mClosed) { 1.220 + LOG(("CacheFileOutputStream::Seek() - Stream is closed. [this=%p]", this)); 1.221 + return NS_BASE_STREAM_CLOSED; 1.222 + } 1.223 + 1.224 + int64_t newPos = offset; 1.225 + switch (whence) { 1.226 + case NS_SEEK_SET: 1.227 + break; 1.228 + case NS_SEEK_CUR: 1.229 + newPos += mPos; 1.230 + break; 1.231 + case NS_SEEK_END: 1.232 + newPos += mFile->mDataSize; 1.233 + break; 1.234 + default: 1.235 + NS_ERROR("invalid whence"); 1.236 + return NS_ERROR_INVALID_ARG; 1.237 + } 1.238 + mPos = newPos; 1.239 + EnsureCorrectChunk(true); 1.240 + 1.241 + LOG(("CacheFileOutputStream::Seek() [this=%p, pos=%lld]", this, mPos)); 1.242 + return NS_OK; 1.243 +} 1.244 + 1.245 +NS_IMETHODIMP 1.246 +CacheFileOutputStream::Tell(int64_t *_retval) 1.247 +{ 1.248 + CacheFileAutoLock lock(mFile); 1.249 + 1.250 + if (mClosed) { 1.251 + LOG(("CacheFileOutputStream::Tell() - Stream is closed. [this=%p]", this)); 1.252 + return NS_BASE_STREAM_CLOSED; 1.253 + } 1.254 + 1.255 + *_retval = mPos; 1.256 + 1.257 + LOG(("CacheFileOutputStream::Tell() [this=%p, retval=%lld]", this, *_retval)); 1.258 + return NS_OK; 1.259 +} 1.260 + 1.261 +NS_IMETHODIMP 1.262 +CacheFileOutputStream::SetEOF() 1.263 +{ 1.264 + MOZ_ASSERT(false, "CacheFileOutputStream::SetEOF() not implemented"); 1.265 + // Right now we don't use SetEOF(). If we ever need this method, we need 1.266 + // to think about what to do with input streams that already points beyond 1.267 + // new EOF. 1.268 + return NS_ERROR_NOT_IMPLEMENTED; 1.269 +} 1.270 + 1.271 +// CacheFileChunkListener 1.272 +nsresult 1.273 +CacheFileOutputStream::OnChunkRead(nsresult aResult, CacheFileChunk *aChunk) 1.274 +{ 1.275 + MOZ_CRASH("CacheFileOutputStream::OnChunkRead should not be called!"); 1.276 + return NS_ERROR_UNEXPECTED; 1.277 +} 1.278 + 1.279 +nsresult 1.280 +CacheFileOutputStream::OnChunkWritten(nsresult aResult, CacheFileChunk *aChunk) 1.281 +{ 1.282 + MOZ_CRASH( 1.283 + "CacheFileOutputStream::OnChunkWritten should not be called!"); 1.284 + return NS_ERROR_UNEXPECTED; 1.285 +} 1.286 + 1.287 +nsresult 1.288 +CacheFileOutputStream::OnChunkAvailable(nsresult aResult, 1.289 + uint32_t aChunkIdx, 1.290 + CacheFileChunk *aChunk) 1.291 +{ 1.292 + MOZ_CRASH( 1.293 + "CacheFileOutputStream::OnChunkAvailable should not be called!"); 1.294 + return NS_ERROR_UNEXPECTED; 1.295 +} 1.296 + 1.297 +nsresult 1.298 +CacheFileOutputStream::OnChunkUpdated(CacheFileChunk *aChunk) 1.299 +{ 1.300 + MOZ_CRASH( 1.301 + "CacheFileOutputStream::OnChunkUpdated should not be called!"); 1.302 + return NS_ERROR_UNEXPECTED; 1.303 +} 1.304 + 1.305 +void CacheFileOutputStream::NotifyCloseListener() 1.306 +{ 1.307 + nsRefPtr<CacheOutputCloseListener> listener; 1.308 + listener.swap(mCloseListener); 1.309 + if (!listener) 1.310 + return; 1.311 + 1.312 + listener->OnOutputClosed(); 1.313 +} 1.314 + 1.315 +void 1.316 +CacheFileOutputStream::ReleaseChunk() 1.317 +{ 1.318 + LOG(("CacheFileOutputStream::ReleaseChunk() [this=%p, idx=%d]", 1.319 + this, mChunk->Index())); 1.320 + 1.321 + mFile->ReleaseOutsideLock(mChunk.forget().take()); 1.322 +} 1.323 + 1.324 +void 1.325 +CacheFileOutputStream::EnsureCorrectChunk(bool aReleaseOnly) 1.326 +{ 1.327 + mFile->AssertOwnsLock(); 1.328 + 1.329 + LOG(("CacheFileOutputStream::EnsureCorrectChunk() [this=%p, releaseOnly=%d]", 1.330 + this, aReleaseOnly)); 1.331 + 1.332 + uint32_t chunkIdx = mPos / kChunkSize; 1.333 + 1.334 + if (mChunk) { 1.335 + if (mChunk->Index() == chunkIdx) { 1.336 + // we have a correct chunk 1.337 + LOG(("CacheFileOutputStream::EnsureCorrectChunk() - Have correct chunk " 1.338 + "[this=%p, idx=%d]", this, chunkIdx)); 1.339 + 1.340 + return; 1.341 + } 1.342 + else { 1.343 + ReleaseChunk(); 1.344 + } 1.345 + } 1.346 + 1.347 + if (aReleaseOnly) 1.348 + return; 1.349 + 1.350 + nsresult rv; 1.351 + rv = mFile->GetChunkLocked(chunkIdx, true, nullptr, getter_AddRefs(mChunk)); 1.352 + if (NS_FAILED(rv)) { 1.353 + LOG(("CacheFileOutputStream::EnsureCorrectChunk() - GetChunkLocked failed. " 1.354 + "[this=%p, idx=%d, rv=0x%08x]", this, chunkIdx, rv)); 1.355 + mStatus = rv; 1.356 + } 1.357 +} 1.358 + 1.359 +void 1.360 +CacheFileOutputStream::FillHole() 1.361 +{ 1.362 + mFile->AssertOwnsLock(); 1.363 + 1.364 + MOZ_ASSERT(mChunk); 1.365 + MOZ_ASSERT(mPos / kChunkSize == mChunk->Index()); 1.366 + 1.367 + uint32_t pos = mPos - (mPos / kChunkSize) * kChunkSize; 1.368 + if (mChunk->DataSize() >= pos) 1.369 + return; 1.370 + 1.371 + LOG(("CacheFileOutputStream::FillHole() - Zeroing hole in chunk %d, range " 1.372 + "%d-%d [this=%p]", mChunk->Index(), mChunk->DataSize(), pos - 1, this)); 1.373 + 1.374 + mChunk->EnsureBufSize(pos); 1.375 + memset(mChunk->BufForWriting() + mChunk->DataSize(), 0, 1.376 + pos - mChunk->DataSize()); 1.377 + 1.378 + mChunk->UpdateDataSize(mChunk->DataSize(), pos - mChunk->DataSize(), false); 1.379 +} 1.380 + 1.381 +void 1.382 +CacheFileOutputStream::NotifyListener() 1.383 +{ 1.384 + mFile->AssertOwnsLock(); 1.385 + 1.386 + LOG(("CacheFileOutputStream::NotifyListener() [this=%p]", this)); 1.387 + 1.388 + MOZ_ASSERT(mCallback); 1.389 + 1.390 + if (!mCallbackTarget) 1.391 + mCallbackTarget = NS_GetCurrentThread(); 1.392 + 1.393 + nsCOMPtr<nsIOutputStreamCallback> asyncCallback = 1.394 + NS_NewOutputStreamReadyEvent(mCallback, mCallbackTarget); 1.395 + 1.396 + mCallback = nullptr; 1.397 + mCallbackTarget = nullptr; 1.398 + 1.399 + asyncCallback->OnOutputStreamReady(this); 1.400 +} 1.401 + 1.402 +// Memory reporting 1.403 + 1.404 +size_t 1.405 +CacheFileOutputStream::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const 1.406 +{ 1.407 + // Everything the stream keeps a reference to is already reported somewhere else. 1.408 + // mFile reports itself. 1.409 + // mChunk reported as part of CacheFile. 1.410 + // mCloseListener is CacheEntry, already reported. 1.411 + // mCallback is usually CacheFile or a class that is reported elsewhere. 1.412 + return mallocSizeOf(this); 1.413 +} 1.414 + 1.415 +} // net 1.416 +} // mozilla