Bumping manifests a=b2g-bump
[gecko.git] / netwerk / cache2 / CacheFileOutputStream.cpp
blob802259549f51907eacc8f8ac16a42e6c534a82e1
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #include "CacheLog.h"
6 #include "CacheFileOutputStream.h"
8 #include "CacheFile.h"
9 #include "CacheEntry.h"
10 #include "nsStreamUtils.h"
11 #include "nsThreadUtils.h"
12 #include "mozilla/DebugOnly.h"
13 #include <algorithm>
15 namespace mozilla {
16 namespace net {
18 NS_IMPL_ADDREF(CacheFileOutputStream)
19 NS_IMETHODIMP_(MozExternalRefCountType)
20 CacheFileOutputStream::Release()
22 NS_PRECONDITION(0 != mRefCnt, "dup release");
23 nsrefcnt count = --mRefCnt;
24 NS_LOG_RELEASE(this, count, "CacheFileOutputStream");
26 if (0 == count) {
27 mRefCnt = 1;
29 CacheFileAutoLock lock(mFile);
30 mFile->RemoveOutput(this, mStatus);
32 delete (this);
33 return 0;
36 return count;
39 NS_INTERFACE_MAP_BEGIN(CacheFileOutputStream)
40 NS_INTERFACE_MAP_ENTRY(nsIOutputStream)
41 NS_INTERFACE_MAP_ENTRY(nsIAsyncOutputStream)
42 NS_INTERFACE_MAP_ENTRY(nsISeekableStream)
43 NS_INTERFACE_MAP_ENTRY(mozilla::net::CacheFileChunkListener)
44 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIOutputStream)
45 NS_INTERFACE_MAP_END_THREADSAFE
47 CacheFileOutputStream::CacheFileOutputStream(CacheFile *aFile,
48 CacheOutputCloseListener *aCloseListener)
49 : mFile(aFile)
50 , mCloseListener(aCloseListener)
51 , mPos(0)
52 , mClosed(false)
53 , mStatus(NS_OK)
54 , mCallbackFlags(0)
56 LOG(("CacheFileOutputStream::CacheFileOutputStream() [this=%p]", this));
57 MOZ_COUNT_CTOR(CacheFileOutputStream);
60 CacheFileOutputStream::~CacheFileOutputStream()
62 LOG(("CacheFileOutputStream::~CacheFileOutputStream() [this=%p]", this));
63 MOZ_COUNT_DTOR(CacheFileOutputStream);
66 // nsIOutputStream
67 NS_IMETHODIMP
68 CacheFileOutputStream::Close()
70 LOG(("CacheFileOutputStream::Close() [this=%p]", this));
71 return CloseWithStatus(NS_OK);
74 NS_IMETHODIMP
75 CacheFileOutputStream::Flush()
77 // TODO do we need to implement flush ???
78 LOG(("CacheFileOutputStream::Flush() [this=%p]", this));
79 return NS_OK;
82 NS_IMETHODIMP
83 CacheFileOutputStream::Write(const char * aBuf, uint32_t aCount,
84 uint32_t *_retval)
86 CacheFileAutoLock lock(mFile);
88 LOG(("CacheFileOutputStream::Write() [this=%p, count=%d]", this, aCount));
90 if (mClosed) {
91 LOG(("CacheFileOutputStream::Write() - Stream is closed. [this=%p, "
92 "status=0x%08x]", this, mStatus));
94 return NS_FAILED(mStatus) ? mStatus : NS_BASE_STREAM_CLOSED;
97 if (CacheObserver::EntryIsTooBig(mPos + aCount, !mFile->mMemoryOnly)) {
98 LOG(("CacheFileOutputStream::Write() - Entry is too big, failing and "
99 "dooming the entry. [this=%p]", this));
101 mFile->DoomLocked(nullptr);
102 CloseWithStatusLocked(NS_ERROR_FILE_TOO_BIG);
103 return NS_ERROR_FILE_TOO_BIG;
106 *_retval = aCount;
108 while (aCount) {
109 EnsureCorrectChunk(false);
110 if (NS_FAILED(mStatus)) {
111 return mStatus;
114 FillHole();
115 if (NS_FAILED(mStatus)) {
116 return mStatus;
119 uint32_t chunkOffset = mPos - (mPos / kChunkSize) * kChunkSize;
120 uint32_t canWrite = kChunkSize - chunkOffset;
121 uint32_t thisWrite = std::min(static_cast<uint32_t>(canWrite), aCount);
122 nsresult rv = mChunk->EnsureBufSize(chunkOffset + thisWrite);
123 if (NS_FAILED(rv)) {
124 CloseWithStatusLocked(rv);
125 return rv;
127 memcpy(mChunk->BufForWriting() + chunkOffset, aBuf, thisWrite);
129 mPos += thisWrite;
130 aBuf += thisWrite;
131 aCount -= thisWrite;
133 mChunk->UpdateDataSize(chunkOffset, thisWrite, false);
136 EnsureCorrectChunk(true);
138 LOG(("CacheFileOutputStream::Write() - Wrote %d bytes [this=%p]",
139 *_retval, this));
141 return NS_OK;
144 NS_IMETHODIMP
145 CacheFileOutputStream::WriteFrom(nsIInputStream *aFromStream, uint32_t aCount,
146 uint32_t *_retval)
148 LOG(("CacheFileOutputStream::WriteFrom() - NOT_IMPLEMENTED [this=%p, from=%p"
149 ", count=%d]", this, aFromStream, aCount));
151 return NS_ERROR_NOT_IMPLEMENTED;
154 NS_IMETHODIMP
155 CacheFileOutputStream::WriteSegments(nsReadSegmentFun aReader, void *aClosure,
156 uint32_t aCount, uint32_t *_retval)
158 LOG(("CacheFileOutputStream::WriteSegments() - NOT_IMPLEMENTED [this=%p, "
159 "count=%d]", this, aCount));
161 return NS_ERROR_NOT_IMPLEMENTED;
164 NS_IMETHODIMP
165 CacheFileOutputStream::IsNonBlocking(bool *_retval)
167 *_retval = false;
168 return NS_OK;
171 // nsIAsyncOutputStream
172 NS_IMETHODIMP
173 CacheFileOutputStream::CloseWithStatus(nsresult aStatus)
175 CacheFileAutoLock lock(mFile);
177 LOG(("CacheFileOutputStream::CloseWithStatus() [this=%p, aStatus=0x%08x]",
178 this, aStatus));
180 return CloseWithStatusLocked(aStatus);
183 nsresult
184 CacheFileOutputStream::CloseWithStatusLocked(nsresult aStatus)
186 LOG(("CacheFileOutputStream::CloseWithStatusLocked() [this=%p, "
187 "aStatus=0x%08x]", this, aStatus));
189 if (mClosed) {
190 MOZ_ASSERT(!mCallback);
191 return NS_OK;
194 mClosed = true;
195 mStatus = NS_FAILED(aStatus) ? aStatus : NS_BASE_STREAM_CLOSED;
197 if (mChunk) {
198 ReleaseChunk();
201 if (mCallback) {
202 NotifyListener();
205 mFile->RemoveOutput(this, mStatus);
207 return NS_OK;
210 NS_IMETHODIMP
211 CacheFileOutputStream::AsyncWait(nsIOutputStreamCallback *aCallback,
212 uint32_t aFlags,
213 uint32_t aRequestedCount,
214 nsIEventTarget *aEventTarget)
216 CacheFileAutoLock lock(mFile);
218 LOG(("CacheFileOutputStream::AsyncWait() [this=%p, callback=%p, flags=%d, "
219 "requestedCount=%d, eventTarget=%p]", this, aCallback, aFlags,
220 aRequestedCount, aEventTarget));
222 mCallback = aCallback;
223 mCallbackFlags = aFlags;
224 mCallbackTarget = aEventTarget;
226 if (!mCallback)
227 return NS_OK;
229 // The stream is blocking so it is writable at any time
230 if (mClosed || !(aFlags & WAIT_CLOSURE_ONLY))
231 NotifyListener();
233 return NS_OK;
236 // nsISeekableStream
237 NS_IMETHODIMP
238 CacheFileOutputStream::Seek(int32_t whence, int64_t offset)
240 CacheFileAutoLock lock(mFile);
242 LOG(("CacheFileOutputStream::Seek() [this=%p, whence=%d, offset=%lld]",
243 this, whence, offset));
245 if (mClosed) {
246 LOG(("CacheFileOutputStream::Seek() - Stream is closed. [this=%p]", this));
247 return NS_BASE_STREAM_CLOSED;
250 int64_t newPos = offset;
251 switch (whence) {
252 case NS_SEEK_SET:
253 break;
254 case NS_SEEK_CUR:
255 newPos += mPos;
256 break;
257 case NS_SEEK_END:
258 newPos += mFile->mDataSize;
259 break;
260 default:
261 NS_ERROR("invalid whence");
262 return NS_ERROR_INVALID_ARG;
264 mPos = newPos;
265 EnsureCorrectChunk(true);
267 LOG(("CacheFileOutputStream::Seek() [this=%p, pos=%lld]", this, mPos));
268 return NS_OK;
271 NS_IMETHODIMP
272 CacheFileOutputStream::Tell(int64_t *_retval)
274 CacheFileAutoLock lock(mFile);
276 if (mClosed) {
277 LOG(("CacheFileOutputStream::Tell() - Stream is closed. [this=%p]", this));
278 return NS_BASE_STREAM_CLOSED;
281 *_retval = mPos;
283 LOG(("CacheFileOutputStream::Tell() [this=%p, retval=%lld]", this, *_retval));
284 return NS_OK;
287 NS_IMETHODIMP
288 CacheFileOutputStream::SetEOF()
290 MOZ_ASSERT(false, "CacheFileOutputStream::SetEOF() not implemented");
291 // Right now we don't use SetEOF(). If we ever need this method, we need
292 // to think about what to do with input streams that already points beyond
293 // new EOF.
294 return NS_ERROR_NOT_IMPLEMENTED;
297 // CacheFileChunkListener
298 nsresult
299 CacheFileOutputStream::OnChunkRead(nsresult aResult, CacheFileChunk *aChunk)
301 MOZ_CRASH("CacheFileOutputStream::OnChunkRead should not be called!");
302 return NS_ERROR_UNEXPECTED;
305 nsresult
306 CacheFileOutputStream::OnChunkWritten(nsresult aResult, CacheFileChunk *aChunk)
308 MOZ_CRASH(
309 "CacheFileOutputStream::OnChunkWritten should not be called!");
310 return NS_ERROR_UNEXPECTED;
313 nsresult
314 CacheFileOutputStream::OnChunkAvailable(nsresult aResult,
315 uint32_t aChunkIdx,
316 CacheFileChunk *aChunk)
318 MOZ_CRASH(
319 "CacheFileOutputStream::OnChunkAvailable should not be called!");
320 return NS_ERROR_UNEXPECTED;
323 nsresult
324 CacheFileOutputStream::OnChunkUpdated(CacheFileChunk *aChunk)
326 MOZ_CRASH(
327 "CacheFileOutputStream::OnChunkUpdated should not be called!");
328 return NS_ERROR_UNEXPECTED;
331 void CacheFileOutputStream::NotifyCloseListener()
333 nsRefPtr<CacheOutputCloseListener> listener;
334 listener.swap(mCloseListener);
335 if (!listener)
336 return;
338 listener->OnOutputClosed();
341 void
342 CacheFileOutputStream::ReleaseChunk()
344 LOG(("CacheFileOutputStream::ReleaseChunk() [this=%p, idx=%d]",
345 this, mChunk->Index()));
347 mFile->ReleaseOutsideLock(mChunk.forget().take());
350 void
351 CacheFileOutputStream::EnsureCorrectChunk(bool aReleaseOnly)
353 mFile->AssertOwnsLock();
355 LOG(("CacheFileOutputStream::EnsureCorrectChunk() [this=%p, releaseOnly=%d]",
356 this, aReleaseOnly));
358 uint32_t chunkIdx = mPos / kChunkSize;
360 if (mChunk) {
361 if (mChunk->Index() == chunkIdx) {
362 // we have a correct chunk
363 LOG(("CacheFileOutputStream::EnsureCorrectChunk() - Have correct chunk "
364 "[this=%p, idx=%d]", this, chunkIdx));
366 return;
368 else {
369 ReleaseChunk();
373 if (aReleaseOnly)
374 return;
376 nsresult rv;
377 rv = mFile->GetChunkLocked(chunkIdx, CacheFile::WRITER, nullptr,
378 getter_AddRefs(mChunk));
379 if (NS_FAILED(rv)) {
380 LOG(("CacheFileOutputStream::EnsureCorrectChunk() - GetChunkLocked failed. "
381 "[this=%p, idx=%d, rv=0x%08x]", this, chunkIdx, rv));
382 CloseWithStatusLocked(rv);
386 void
387 CacheFileOutputStream::FillHole()
389 mFile->AssertOwnsLock();
391 MOZ_ASSERT(mChunk);
392 MOZ_ASSERT(mPos / kChunkSize == mChunk->Index());
394 uint32_t pos = mPos - (mPos / kChunkSize) * kChunkSize;
395 if (mChunk->DataSize() >= pos)
396 return;
398 LOG(("CacheFileOutputStream::FillHole() - Zeroing hole in chunk %d, range "
399 "%d-%d [this=%p]", mChunk->Index(), mChunk->DataSize(), pos - 1, this));
401 nsresult rv = mChunk->EnsureBufSize(pos);
402 if (NS_FAILED(rv)) {
403 CloseWithStatusLocked(rv);
404 return;
407 memset(mChunk->BufForWriting() + mChunk->DataSize(), 0,
408 pos - mChunk->DataSize());
410 mChunk->UpdateDataSize(mChunk->DataSize(), pos - mChunk->DataSize(), false);
413 void
414 CacheFileOutputStream::NotifyListener()
416 mFile->AssertOwnsLock();
418 LOG(("CacheFileOutputStream::NotifyListener() [this=%p]", this));
420 MOZ_ASSERT(mCallback);
422 if (!mCallbackTarget) {
423 mCallbackTarget = CacheFileIOManager::IOTarget();
424 if (!mCallbackTarget) {
425 LOG(("CacheFileOutputStream::NotifyListener() - Cannot get Cache I/O "
426 "thread! Using main thread for callback."));
427 mCallbackTarget = do_GetMainThread();
431 nsCOMPtr<nsIOutputStreamCallback> asyncCallback =
432 NS_NewOutputStreamReadyEvent(mCallback, mCallbackTarget);
434 mCallback = nullptr;
435 mCallbackTarget = nullptr;
437 asyncCallback->OnOutputStreamReady(this);
440 // Memory reporting
442 size_t
443 CacheFileOutputStream::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
445 // Everything the stream keeps a reference to is already reported somewhere else.
446 // mFile reports itself.
447 // mChunk reported as part of CacheFile.
448 // mCloseListener is CacheEntry, already reported.
449 // mCallback is usually CacheFile or a class that is reported elsewhere.
450 return mallocSizeOf(this);
453 } // net
454 } // mozilla