netwerk/cache2/CacheFileOutputStream.cpp

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

mercurial