netwerk/cache2/CacheFileOutputStream.cpp

Wed, 31 Dec 2014 06:55:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:50 +0100
changeset 2
7e26c7da4463
permissions
-rw-r--r--

Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2

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 "CacheFileOutputStream.h"
michael@0 7
michael@0 8 #include "CacheFile.h"
michael@0 9 #include "CacheEntry.h"
michael@0 10 #include "nsStreamUtils.h"
michael@0 11 #include "nsThreadUtils.h"
michael@0 12 #include "mozilla/DebugOnly.h"
michael@0 13 #include <algorithm>
michael@0 14
michael@0 15 namespace mozilla {
michael@0 16 namespace net {
michael@0 17
michael@0 18 NS_IMPL_ADDREF(CacheFileOutputStream)
michael@0 19 NS_IMETHODIMP_(MozExternalRefCountType)
michael@0 20 CacheFileOutputStream::Release()
michael@0 21 {
michael@0 22 NS_PRECONDITION(0 != mRefCnt, "dup release");
michael@0 23 nsrefcnt count = --mRefCnt;
michael@0 24 NS_LOG_RELEASE(this, count, "CacheFileOutputStream");
michael@0 25
michael@0 26 if (0 == count) {
michael@0 27 mRefCnt = 1;
michael@0 28 {
michael@0 29 CacheFileAutoLock lock(mFile);
michael@0 30 mFile->RemoveOutput(this);
michael@0 31 }
michael@0 32 delete (this);
michael@0 33 return 0;
michael@0 34 }
michael@0 35
michael@0 36 return count;
michael@0 37 }
michael@0 38
michael@0 39 NS_INTERFACE_MAP_BEGIN(CacheFileOutputStream)
michael@0 40 NS_INTERFACE_MAP_ENTRY(nsIOutputStream)
michael@0 41 NS_INTERFACE_MAP_ENTRY(nsIAsyncOutputStream)
michael@0 42 NS_INTERFACE_MAP_ENTRY(nsISeekableStream)
michael@0 43 NS_INTERFACE_MAP_ENTRY(mozilla::net::CacheFileChunkListener)
michael@0 44 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIOutputStream)
michael@0 45 NS_INTERFACE_MAP_END_THREADSAFE
michael@0 46
michael@0 47 CacheFileOutputStream::CacheFileOutputStream(CacheFile *aFile,
michael@0 48 CacheOutputCloseListener *aCloseListener)
michael@0 49 : mFile(aFile)
michael@0 50 , mCloseListener(aCloseListener)
michael@0 51 , mPos(0)
michael@0 52 , mClosed(false)
michael@0 53 , mStatus(NS_OK)
michael@0 54 , mCallbackFlags(0)
michael@0 55 {
michael@0 56 LOG(("CacheFileOutputStream::CacheFileOutputStream() [this=%p]", this));
michael@0 57 MOZ_COUNT_CTOR(CacheFileOutputStream);
michael@0 58 }
michael@0 59
michael@0 60 CacheFileOutputStream::~CacheFileOutputStream()
michael@0 61 {
michael@0 62 LOG(("CacheFileOutputStream::~CacheFileOutputStream() [this=%p]", this));
michael@0 63 MOZ_COUNT_DTOR(CacheFileOutputStream);
michael@0 64 }
michael@0 65
michael@0 66 // nsIOutputStream
michael@0 67 NS_IMETHODIMP
michael@0 68 CacheFileOutputStream::Close()
michael@0 69 {
michael@0 70 LOG(("CacheFileOutputStream::Close() [this=%p]", this));
michael@0 71 return CloseWithStatus(NS_OK);
michael@0 72 }
michael@0 73
michael@0 74 NS_IMETHODIMP
michael@0 75 CacheFileOutputStream::Flush()
michael@0 76 {
michael@0 77 // TODO do we need to implement flush ???
michael@0 78 LOG(("CacheFileOutputStream::Flush() [this=%p]", this));
michael@0 79 return NS_OK;
michael@0 80 }
michael@0 81
michael@0 82 NS_IMETHODIMP
michael@0 83 CacheFileOutputStream::Write(const char * aBuf, uint32_t aCount,
michael@0 84 uint32_t *_retval)
michael@0 85 {
michael@0 86 CacheFileAutoLock lock(mFile);
michael@0 87
michael@0 88 LOG(("CacheFileOutputStream::Write() [this=%p, count=%d]", this, aCount));
michael@0 89
michael@0 90 if (mClosed) {
michael@0 91 LOG(("CacheFileOutputStream::Write() - Stream is closed. [this=%p, "
michael@0 92 "status=0x%08x]", this, mStatus));
michael@0 93
michael@0 94 return NS_FAILED(mStatus) ? mStatus : NS_BASE_STREAM_CLOSED;
michael@0 95 }
michael@0 96
michael@0 97 *_retval = aCount;
michael@0 98
michael@0 99 while (aCount) {
michael@0 100 EnsureCorrectChunk(false);
michael@0 101 if (NS_FAILED(mStatus))
michael@0 102 return mStatus;
michael@0 103
michael@0 104 FillHole();
michael@0 105
michael@0 106 uint32_t chunkOffset = mPos - (mPos / kChunkSize) * kChunkSize;
michael@0 107 uint32_t canWrite = kChunkSize - chunkOffset;
michael@0 108 uint32_t thisWrite = std::min(static_cast<uint32_t>(canWrite), aCount);
michael@0 109 mChunk->EnsureBufSize(chunkOffset + thisWrite);
michael@0 110 memcpy(mChunk->BufForWriting() + chunkOffset, aBuf, thisWrite);
michael@0 111
michael@0 112 mPos += thisWrite;
michael@0 113 aBuf += thisWrite;
michael@0 114 aCount -= thisWrite;
michael@0 115
michael@0 116 mChunk->UpdateDataSize(chunkOffset, thisWrite, false);
michael@0 117 }
michael@0 118
michael@0 119 EnsureCorrectChunk(true);
michael@0 120
michael@0 121 LOG(("CacheFileOutputStream::Write() - Wrote %d bytes [this=%p]",
michael@0 122 *_retval, this));
michael@0 123
michael@0 124 return NS_OK;
michael@0 125 }
michael@0 126
michael@0 127 NS_IMETHODIMP
michael@0 128 CacheFileOutputStream::WriteFrom(nsIInputStream *aFromStream, uint32_t aCount,
michael@0 129 uint32_t *_retval)
michael@0 130 {
michael@0 131 LOG(("CacheFileOutputStream::WriteFrom() - NOT_IMPLEMENTED [this=%p, from=%p"
michael@0 132 ", count=%d]", this, aFromStream, aCount));
michael@0 133
michael@0 134 return NS_ERROR_NOT_IMPLEMENTED;
michael@0 135 }
michael@0 136
michael@0 137 NS_IMETHODIMP
michael@0 138 CacheFileOutputStream::WriteSegments(nsReadSegmentFun aReader, void *aClosure,
michael@0 139 uint32_t aCount, uint32_t *_retval)
michael@0 140 {
michael@0 141 LOG(("CacheFileOutputStream::WriteSegments() - NOT_IMPLEMENTED [this=%p, "
michael@0 142 "count=%d]", this, aCount));
michael@0 143
michael@0 144 return NS_ERROR_NOT_IMPLEMENTED;
michael@0 145 }
michael@0 146
michael@0 147 NS_IMETHODIMP
michael@0 148 CacheFileOutputStream::IsNonBlocking(bool *_retval)
michael@0 149 {
michael@0 150 *_retval = false;
michael@0 151 return NS_OK;
michael@0 152 }
michael@0 153
michael@0 154 // nsIAsyncOutputStream
michael@0 155 NS_IMETHODIMP
michael@0 156 CacheFileOutputStream::CloseWithStatus(nsresult aStatus)
michael@0 157 {
michael@0 158 CacheFileAutoLock lock(mFile);
michael@0 159
michael@0 160 LOG(("CacheFileOutputStream::CloseWithStatus() [this=%p, aStatus=0x%08x]",
michael@0 161 this, aStatus));
michael@0 162
michael@0 163 if (mClosed) {
michael@0 164 MOZ_ASSERT(!mCallback);
michael@0 165 return NS_OK;
michael@0 166 }
michael@0 167
michael@0 168 mClosed = true;
michael@0 169 mStatus = NS_FAILED(aStatus) ? aStatus : NS_BASE_STREAM_CLOSED;
michael@0 170
michael@0 171 if (mChunk)
michael@0 172 ReleaseChunk();
michael@0 173
michael@0 174 if (mCallback)
michael@0 175 NotifyListener();
michael@0 176
michael@0 177 mFile->RemoveOutput(this);
michael@0 178
michael@0 179 return NS_OK;
michael@0 180 }
michael@0 181
michael@0 182 NS_IMETHODIMP
michael@0 183 CacheFileOutputStream::AsyncWait(nsIOutputStreamCallback *aCallback,
michael@0 184 uint32_t aFlags,
michael@0 185 uint32_t aRequestedCount,
michael@0 186 nsIEventTarget *aEventTarget)
michael@0 187 {
michael@0 188 CacheFileAutoLock lock(mFile);
michael@0 189
michael@0 190 LOG(("CacheFileOutputStream::AsyncWait() [this=%p, callback=%p, flags=%d, "
michael@0 191 "requestedCount=%d, eventTarget=%p]", this, aCallback, aFlags,
michael@0 192 aRequestedCount, aEventTarget));
michael@0 193
michael@0 194 mCallback = aCallback;
michael@0 195 mCallbackFlags = aFlags;
michael@0 196
michael@0 197 if (!mCallback)
michael@0 198 return NS_OK;
michael@0 199
michael@0 200 // The stream is blocking so it is writable at any time
michael@0 201 if (mClosed || !(aFlags & WAIT_CLOSURE_ONLY))
michael@0 202 NotifyListener();
michael@0 203
michael@0 204 return NS_OK;
michael@0 205 }
michael@0 206
michael@0 207 // nsISeekableStream
michael@0 208 NS_IMETHODIMP
michael@0 209 CacheFileOutputStream::Seek(int32_t whence, int64_t offset)
michael@0 210 {
michael@0 211 CacheFileAutoLock lock(mFile);
michael@0 212
michael@0 213 LOG(("CacheFileOutputStream::Seek() [this=%p, whence=%d, offset=%lld]",
michael@0 214 this, whence, offset));
michael@0 215
michael@0 216 if (mClosed) {
michael@0 217 LOG(("CacheFileOutputStream::Seek() - Stream is closed. [this=%p]", this));
michael@0 218 return NS_BASE_STREAM_CLOSED;
michael@0 219 }
michael@0 220
michael@0 221 int64_t newPos = offset;
michael@0 222 switch (whence) {
michael@0 223 case NS_SEEK_SET:
michael@0 224 break;
michael@0 225 case NS_SEEK_CUR:
michael@0 226 newPos += mPos;
michael@0 227 break;
michael@0 228 case NS_SEEK_END:
michael@0 229 newPos += mFile->mDataSize;
michael@0 230 break;
michael@0 231 default:
michael@0 232 NS_ERROR("invalid whence");
michael@0 233 return NS_ERROR_INVALID_ARG;
michael@0 234 }
michael@0 235 mPos = newPos;
michael@0 236 EnsureCorrectChunk(true);
michael@0 237
michael@0 238 LOG(("CacheFileOutputStream::Seek() [this=%p, pos=%lld]", this, mPos));
michael@0 239 return NS_OK;
michael@0 240 }
michael@0 241
michael@0 242 NS_IMETHODIMP
michael@0 243 CacheFileOutputStream::Tell(int64_t *_retval)
michael@0 244 {
michael@0 245 CacheFileAutoLock lock(mFile);
michael@0 246
michael@0 247 if (mClosed) {
michael@0 248 LOG(("CacheFileOutputStream::Tell() - Stream is closed. [this=%p]", this));
michael@0 249 return NS_BASE_STREAM_CLOSED;
michael@0 250 }
michael@0 251
michael@0 252 *_retval = mPos;
michael@0 253
michael@0 254 LOG(("CacheFileOutputStream::Tell() [this=%p, retval=%lld]", this, *_retval));
michael@0 255 return NS_OK;
michael@0 256 }
michael@0 257
michael@0 258 NS_IMETHODIMP
michael@0 259 CacheFileOutputStream::SetEOF()
michael@0 260 {
michael@0 261 MOZ_ASSERT(false, "CacheFileOutputStream::SetEOF() not implemented");
michael@0 262 // Right now we don't use SetEOF(). If we ever need this method, we need
michael@0 263 // to think about what to do with input streams that already points beyond
michael@0 264 // new EOF.
michael@0 265 return NS_ERROR_NOT_IMPLEMENTED;
michael@0 266 }
michael@0 267
michael@0 268 // CacheFileChunkListener
michael@0 269 nsresult
michael@0 270 CacheFileOutputStream::OnChunkRead(nsresult aResult, CacheFileChunk *aChunk)
michael@0 271 {
michael@0 272 MOZ_CRASH("CacheFileOutputStream::OnChunkRead should not be called!");
michael@0 273 return NS_ERROR_UNEXPECTED;
michael@0 274 }
michael@0 275
michael@0 276 nsresult
michael@0 277 CacheFileOutputStream::OnChunkWritten(nsresult aResult, CacheFileChunk *aChunk)
michael@0 278 {
michael@0 279 MOZ_CRASH(
michael@0 280 "CacheFileOutputStream::OnChunkWritten should not be called!");
michael@0 281 return NS_ERROR_UNEXPECTED;
michael@0 282 }
michael@0 283
michael@0 284 nsresult
michael@0 285 CacheFileOutputStream::OnChunkAvailable(nsresult aResult,
michael@0 286 uint32_t aChunkIdx,
michael@0 287 CacheFileChunk *aChunk)
michael@0 288 {
michael@0 289 MOZ_CRASH(
michael@0 290 "CacheFileOutputStream::OnChunkAvailable should not be called!");
michael@0 291 return NS_ERROR_UNEXPECTED;
michael@0 292 }
michael@0 293
michael@0 294 nsresult
michael@0 295 CacheFileOutputStream::OnChunkUpdated(CacheFileChunk *aChunk)
michael@0 296 {
michael@0 297 MOZ_CRASH(
michael@0 298 "CacheFileOutputStream::OnChunkUpdated should not be called!");
michael@0 299 return NS_ERROR_UNEXPECTED;
michael@0 300 }
michael@0 301
michael@0 302 void CacheFileOutputStream::NotifyCloseListener()
michael@0 303 {
michael@0 304 nsRefPtr<CacheOutputCloseListener> listener;
michael@0 305 listener.swap(mCloseListener);
michael@0 306 if (!listener)
michael@0 307 return;
michael@0 308
michael@0 309 listener->OnOutputClosed();
michael@0 310 }
michael@0 311
michael@0 312 void
michael@0 313 CacheFileOutputStream::ReleaseChunk()
michael@0 314 {
michael@0 315 LOG(("CacheFileOutputStream::ReleaseChunk() [this=%p, idx=%d]",
michael@0 316 this, mChunk->Index()));
michael@0 317
michael@0 318 mFile->ReleaseOutsideLock(mChunk.forget().take());
michael@0 319 }
michael@0 320
michael@0 321 void
michael@0 322 CacheFileOutputStream::EnsureCorrectChunk(bool aReleaseOnly)
michael@0 323 {
michael@0 324 mFile->AssertOwnsLock();
michael@0 325
michael@0 326 LOG(("CacheFileOutputStream::EnsureCorrectChunk() [this=%p, releaseOnly=%d]",
michael@0 327 this, aReleaseOnly));
michael@0 328
michael@0 329 uint32_t chunkIdx = mPos / kChunkSize;
michael@0 330
michael@0 331 if (mChunk) {
michael@0 332 if (mChunk->Index() == chunkIdx) {
michael@0 333 // we have a correct chunk
michael@0 334 LOG(("CacheFileOutputStream::EnsureCorrectChunk() - Have correct chunk "
michael@0 335 "[this=%p, idx=%d]", this, chunkIdx));
michael@0 336
michael@0 337 return;
michael@0 338 }
michael@0 339 else {
michael@0 340 ReleaseChunk();
michael@0 341 }
michael@0 342 }
michael@0 343
michael@0 344 if (aReleaseOnly)
michael@0 345 return;
michael@0 346
michael@0 347 nsresult rv;
michael@0 348 rv = mFile->GetChunkLocked(chunkIdx, true, nullptr, getter_AddRefs(mChunk));
michael@0 349 if (NS_FAILED(rv)) {
michael@0 350 LOG(("CacheFileOutputStream::EnsureCorrectChunk() - GetChunkLocked failed. "
michael@0 351 "[this=%p, idx=%d, rv=0x%08x]", this, chunkIdx, rv));
michael@0 352 mStatus = rv;
michael@0 353 }
michael@0 354 }
michael@0 355
michael@0 356 void
michael@0 357 CacheFileOutputStream::FillHole()
michael@0 358 {
michael@0 359 mFile->AssertOwnsLock();
michael@0 360
michael@0 361 MOZ_ASSERT(mChunk);
michael@0 362 MOZ_ASSERT(mPos / kChunkSize == mChunk->Index());
michael@0 363
michael@0 364 uint32_t pos = mPos - (mPos / kChunkSize) * kChunkSize;
michael@0 365 if (mChunk->DataSize() >= pos)
michael@0 366 return;
michael@0 367
michael@0 368 LOG(("CacheFileOutputStream::FillHole() - Zeroing hole in chunk %d, range "
michael@0 369 "%d-%d [this=%p]", mChunk->Index(), mChunk->DataSize(), pos - 1, this));
michael@0 370
michael@0 371 mChunk->EnsureBufSize(pos);
michael@0 372 memset(mChunk->BufForWriting() + mChunk->DataSize(), 0,
michael@0 373 pos - mChunk->DataSize());
michael@0 374
michael@0 375 mChunk->UpdateDataSize(mChunk->DataSize(), pos - mChunk->DataSize(), false);
michael@0 376 }
michael@0 377
michael@0 378 void
michael@0 379 CacheFileOutputStream::NotifyListener()
michael@0 380 {
michael@0 381 mFile->AssertOwnsLock();
michael@0 382
michael@0 383 LOG(("CacheFileOutputStream::NotifyListener() [this=%p]", this));
michael@0 384
michael@0 385 MOZ_ASSERT(mCallback);
michael@0 386
michael@0 387 if (!mCallbackTarget)
michael@0 388 mCallbackTarget = NS_GetCurrentThread();
michael@0 389
michael@0 390 nsCOMPtr<nsIOutputStreamCallback> asyncCallback =
michael@0 391 NS_NewOutputStreamReadyEvent(mCallback, mCallbackTarget);
michael@0 392
michael@0 393 mCallback = nullptr;
michael@0 394 mCallbackTarget = nullptr;
michael@0 395
michael@0 396 asyncCallback->OnOutputStreamReady(this);
michael@0 397 }
michael@0 398
michael@0 399 // Memory reporting
michael@0 400
michael@0 401 size_t
michael@0 402 CacheFileOutputStream::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
michael@0 403 {
michael@0 404 // Everything the stream keeps a reference to is already reported somewhere else.
michael@0 405 // mFile reports itself.
michael@0 406 // mChunk reported as part of CacheFile.
michael@0 407 // mCloseListener is CacheEntry, already reported.
michael@0 408 // mCallback is usually CacheFile or a class that is reported elsewhere.
michael@0 409 return mallocSizeOf(this);
michael@0 410 }
michael@0 411
michael@0 412 } // net
michael@0 413 } // mozilla

mercurial