netwerk/cache2/CacheFileInputStream.cpp

changeset 2
7e26c7da4463
equal deleted inserted replaced
-1:000000000000 0:41402cbf34ba
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 "CacheFileInputStream.h"
7
8 #include "CacheFile.h"
9 #include "nsStreamUtils.h"
10 #include "nsThreadUtils.h"
11 #include <algorithm>
12
13 namespace mozilla {
14 namespace net {
15
16 NS_IMPL_ADDREF(CacheFileInputStream)
17 NS_IMETHODIMP_(MozExternalRefCountType)
18 CacheFileInputStream::Release()
19 {
20 NS_PRECONDITION(0 != mRefCnt, "dup release");
21 nsrefcnt count = --mRefCnt;
22 NS_LOG_RELEASE(this, count, "CacheFileInputStream");
23
24 if (0 == count) {
25 mRefCnt = 1;
26 delete (this);
27 return 0;
28 }
29
30 if (count == 1) {
31 mFile->RemoveInput(this);
32 }
33
34 return count;
35 }
36
37 NS_INTERFACE_MAP_BEGIN(CacheFileInputStream)
38 NS_INTERFACE_MAP_ENTRY(nsIInputStream)
39 NS_INTERFACE_MAP_ENTRY(nsIAsyncInputStream)
40 NS_INTERFACE_MAP_ENTRY(nsISeekableStream)
41 NS_INTERFACE_MAP_ENTRY(mozilla::net::CacheFileChunkListener)
42 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInputStream)
43 NS_INTERFACE_MAP_END_THREADSAFE
44
45 CacheFileInputStream::CacheFileInputStream(CacheFile *aFile)
46 : mFile(aFile)
47 , mPos(0)
48 , mClosed(false)
49 , mStatus(NS_OK)
50 , mWaitingForUpdate(false)
51 , mListeningForChunk(-1)
52 , mInReadSegments(false)
53 , mCallbackFlags(0)
54 {
55 LOG(("CacheFileInputStream::CacheFileInputStream() [this=%p]", this));
56 MOZ_COUNT_CTOR(CacheFileInputStream);
57 }
58
59 CacheFileInputStream::~CacheFileInputStream()
60 {
61 LOG(("CacheFileInputStream::~CacheFileInputStream() [this=%p]", this));
62 MOZ_COUNT_DTOR(CacheFileInputStream);
63 }
64
65 // nsIInputStream
66 NS_IMETHODIMP
67 CacheFileInputStream::Close()
68 {
69 LOG(("CacheFileInputStream::Close() [this=%p]", this));
70 return CloseWithStatus(NS_OK);
71 }
72
73 NS_IMETHODIMP
74 CacheFileInputStream::Available(uint64_t *_retval)
75 {
76 CacheFileAutoLock lock(mFile);
77 MOZ_ASSERT(!mInReadSegments);
78
79 if (mClosed) {
80 LOG(("CacheFileInputStream::Available() - Stream is closed. [this=%p, "
81 "status=0x%08x]", this, mStatus));
82 return NS_FAILED(mStatus) ? mStatus : NS_BASE_STREAM_CLOSED;
83 }
84
85 EnsureCorrectChunk(false);
86 if (NS_FAILED(mStatus))
87 return mStatus;
88
89 *_retval = 0;
90
91 if (mChunk) {
92 int64_t canRead;
93 const char *buf;
94 CanRead(&canRead, &buf);
95
96 if (canRead > 0)
97 *_retval = canRead;
98 else if (canRead == 0 && !mFile->mOutput)
99 return NS_BASE_STREAM_CLOSED;
100 }
101
102 LOG(("CacheFileInputStream::Available() [this=%p, retval=%lld]",
103 this, *_retval));
104
105 return NS_OK;
106 }
107
108 NS_IMETHODIMP
109 CacheFileInputStream::Read(char *aBuf, uint32_t aCount, uint32_t *_retval)
110 {
111 CacheFileAutoLock lock(mFile);
112 MOZ_ASSERT(!mInReadSegments);
113
114 LOG(("CacheFileInputStream::Read() [this=%p, count=%d]", this, aCount));
115
116 nsresult rv;
117
118 if (mClosed) {
119 LOG(("CacheFileInputStream::Read() - Stream is closed. [this=%p, "
120 "status=0x%08x]", this, mStatus));
121
122 if NS_FAILED(mStatus)
123 return mStatus;
124
125 *_retval = 0;
126 return NS_OK;
127 }
128
129 EnsureCorrectChunk(false);
130 if (NS_FAILED(mStatus))
131 return mStatus;
132
133 if (!mChunk) {
134 if (mListeningForChunk == -1) {
135 LOG((" no chunk, returning 0 read and NS_OK"));
136 *_retval = 0;
137 return NS_OK;
138 }
139 else {
140 LOG((" waiting for chuck, returning WOULD_BLOCK"));
141 return NS_BASE_STREAM_WOULD_BLOCK;
142 }
143 }
144
145 int64_t canRead;
146 const char *buf;
147 CanRead(&canRead, &buf);
148
149 if (canRead < 0) {
150 // file was truncated ???
151 MOZ_ASSERT(false, "SetEOF is currenty not implemented?!");
152 *_retval = 0;
153 rv = NS_OK;
154 }
155 else if (canRead > 0) {
156 *_retval = std::min(static_cast<uint32_t>(canRead), aCount);
157 memcpy(aBuf, buf, *_retval);
158 mPos += *_retval;
159
160 EnsureCorrectChunk(!(canRead < aCount && mPos % kChunkSize == 0));
161
162 rv = NS_OK;
163 }
164 else {
165 if (mFile->mOutput)
166 rv = NS_BASE_STREAM_WOULD_BLOCK;
167 else {
168 *_retval = 0;
169 rv = NS_OK;
170 }
171 }
172
173 LOG(("CacheFileInputStream::Read() [this=%p, rv=0x%08x, retval=%d",
174 this, rv, *_retval));
175
176 return rv;
177 }
178
179 NS_IMETHODIMP
180 CacheFileInputStream::ReadSegments(nsWriteSegmentFun aWriter, void *aClosure,
181 uint32_t aCount, uint32_t *_retval)
182 {
183 CacheFileAutoLock lock(mFile);
184 MOZ_ASSERT(!mInReadSegments);
185
186 LOG(("CacheFileInputStream::ReadSegments() [this=%p, count=%d]",
187 this, aCount));
188
189 nsresult rv;
190
191 if (mClosed) {
192 LOG(("CacheFileInputStream::ReadSegments() - Stream is closed. [this=%p, "
193 "status=0x%08x]", this, mStatus));
194
195 if NS_FAILED(mStatus)
196 return mStatus;
197
198 *_retval = 0;
199 return NS_OK;
200 }
201
202 EnsureCorrectChunk(false);
203 if (NS_FAILED(mStatus))
204 return mStatus;
205
206 if (!mChunk) {
207 if (mListeningForChunk == -1) {
208 *_retval = 0;
209 return NS_OK;
210 }
211 else {
212 return NS_BASE_STREAM_WOULD_BLOCK;
213 }
214 }
215
216 int64_t canRead;
217 const char *buf;
218 CanRead(&canRead, &buf);
219
220 if (canRead < 0) {
221 // file was truncated ???
222 MOZ_ASSERT(false, "SetEOF is currenty not implemented?!");
223 *_retval = 0;
224 rv = NS_OK;
225 }
226 else if (canRead > 0) {
227 uint32_t toRead = std::min(static_cast<uint32_t>(canRead), aCount);
228
229 // We need to release the lock to avoid lock re-entering
230 #ifdef DEBUG
231 int64_t oldPos = mPos;
232 #endif
233 mInReadSegments = true;
234 lock.Unlock();
235 rv = aWriter(this, aClosure, buf, 0, toRead, _retval);
236 lock.Lock();
237 mInReadSegments = false;
238 #ifdef DEBUG
239 MOZ_ASSERT(oldPos == mPos);
240 #endif
241
242 if (NS_SUCCEEDED(rv)) {
243 MOZ_ASSERT(*_retval <= toRead,
244 "writer should not write more than we asked it to write");
245 mPos += *_retval;
246 }
247
248 EnsureCorrectChunk(!(canRead < aCount && mPos % kChunkSize == 0));
249
250 rv = NS_OK;
251 }
252 else {
253 if (mFile->mOutput)
254 rv = NS_BASE_STREAM_WOULD_BLOCK;
255 else {
256 *_retval = 0;
257 rv = NS_OK;
258 }
259 }
260
261 LOG(("CacheFileInputStream::ReadSegments() [this=%p, rv=0x%08x, retval=%d",
262 this, rv, *_retval));
263
264 return rv;
265 }
266
267 NS_IMETHODIMP
268 CacheFileInputStream::IsNonBlocking(bool *_retval)
269 {
270 *_retval = true;
271 return NS_OK;
272 }
273
274 // nsIAsyncInputStream
275 NS_IMETHODIMP
276 CacheFileInputStream::CloseWithStatus(nsresult aStatus)
277 {
278 CacheFileAutoLock lock(mFile);
279 MOZ_ASSERT(!mInReadSegments);
280
281 LOG(("CacheFileInputStream::CloseWithStatus() [this=%p, aStatus=0x%08x]",
282 this, aStatus));
283
284 if (mClosed) {
285 MOZ_ASSERT(!mCallback);
286 return NS_OK;
287 }
288
289 mClosed = true;
290 mStatus = NS_FAILED(aStatus) ? aStatus : NS_BASE_STREAM_CLOSED;
291
292 if (mChunk)
293 ReleaseChunk();
294
295 // TODO propagate error from input stream to other streams ???
296
297 MaybeNotifyListener();
298
299 return NS_OK;
300 }
301
302 NS_IMETHODIMP
303 CacheFileInputStream::AsyncWait(nsIInputStreamCallback *aCallback,
304 uint32_t aFlags,
305 uint32_t aRequestedCount,
306 nsIEventTarget *aEventTarget)
307 {
308 CacheFileAutoLock lock(mFile);
309 MOZ_ASSERT(!mInReadSegments);
310
311 LOG(("CacheFileInputStream::AsyncWait() [this=%p, callback=%p, flags=%d, "
312 "requestedCount=%d, eventTarget=%p]", this, aCallback, aFlags,
313 aRequestedCount, aEventTarget));
314
315 mCallback = aCallback;
316 mCallbackFlags = aFlags;
317
318 if (!mCallback) {
319 if (mWaitingForUpdate) {
320 mChunk->CancelWait(this);
321 mWaitingForUpdate = false;
322 }
323 return NS_OK;
324 }
325
326 if (mClosed) {
327 NotifyListener();
328 return NS_OK;
329 }
330
331 EnsureCorrectChunk(false);
332
333 MaybeNotifyListener();
334
335 return NS_OK;
336 }
337
338 // nsISeekableStream
339 NS_IMETHODIMP
340 CacheFileInputStream::Seek(int32_t whence, int64_t offset)
341 {
342 CacheFileAutoLock lock(mFile);
343 MOZ_ASSERT(!mInReadSegments);
344
345 LOG(("CacheFileInputStream::Seek() [this=%p, whence=%d, offset=%lld]",
346 this, whence, offset));
347
348 if (mClosed) {
349 LOG(("CacheFileInputStream::Seek() - Stream is closed. [this=%p]", this));
350 return NS_BASE_STREAM_CLOSED;
351 }
352
353 int64_t newPos = offset;
354 switch (whence) {
355 case NS_SEEK_SET:
356 break;
357 case NS_SEEK_CUR:
358 newPos += mPos;
359 break;
360 case NS_SEEK_END:
361 newPos += mFile->mDataSize;
362 break;
363 default:
364 NS_ERROR("invalid whence");
365 return NS_ERROR_INVALID_ARG;
366 }
367 mPos = newPos;
368 EnsureCorrectChunk(true);
369
370 LOG(("CacheFileInputStream::Seek() [this=%p, pos=%lld]", this, mPos));
371 return NS_OK;
372 }
373
374 NS_IMETHODIMP
375 CacheFileInputStream::Tell(int64_t *_retval)
376 {
377 CacheFileAutoLock lock(mFile);
378 MOZ_ASSERT(!mInReadSegments);
379
380 if (mClosed) {
381 LOG(("CacheFileInputStream::Tell() - Stream is closed. [this=%p]", this));
382 return NS_BASE_STREAM_CLOSED;
383 }
384
385 *_retval = mPos;
386
387 LOG(("CacheFileInputStream::Tell() [this=%p, retval=%lld]", this, *_retval));
388 return NS_OK;
389 }
390
391 NS_IMETHODIMP
392 CacheFileInputStream::SetEOF()
393 {
394 MOZ_ASSERT(false, "Don't call SetEOF on cache input stream");
395 return NS_ERROR_NOT_IMPLEMENTED;
396 }
397
398 // CacheFileChunkListener
399 nsresult
400 CacheFileInputStream::OnChunkRead(nsresult aResult, CacheFileChunk *aChunk)
401 {
402 MOZ_CRASH("CacheFileInputStream::OnChunkRead should not be called!");
403 return NS_ERROR_UNEXPECTED;
404 }
405
406 nsresult
407 CacheFileInputStream::OnChunkWritten(nsresult aResult, CacheFileChunk *aChunk)
408 {
409 MOZ_CRASH("CacheFileInputStream::OnChunkWritten should not be called!");
410 return NS_ERROR_UNEXPECTED;
411 }
412
413 nsresult
414 CacheFileInputStream::OnChunkAvailable(nsresult aResult, uint32_t aChunkIdx,
415 CacheFileChunk *aChunk)
416 {
417 CacheFileAutoLock lock(mFile);
418 MOZ_ASSERT(!mInReadSegments);
419
420 LOG(("CacheFileInputStream::OnChunkAvailable() [this=%p, result=0x%08x, "
421 "idx=%d, chunk=%p]", this, aResult, aChunkIdx, aChunk));
422
423 MOZ_ASSERT(mListeningForChunk != -1);
424
425 if (mListeningForChunk != static_cast<int64_t>(aChunkIdx)) {
426 // This is not a chunk that we're waiting for
427 LOG(("CacheFileInputStream::OnChunkAvailable() - Notification is for a "
428 "different chunk. [this=%p, listeningForChunk=%lld]",
429 this, mListeningForChunk));
430
431 return NS_OK;
432 }
433
434 MOZ_ASSERT(!mChunk);
435 MOZ_ASSERT(!mWaitingForUpdate);
436 mListeningForChunk = -1;
437
438 if (mClosed) {
439 MOZ_ASSERT(!mCallback);
440
441 LOG(("CacheFileInputStream::OnChunkAvailable() - Stream is closed, "
442 "ignoring notification. [this=%p]", this));
443
444 return NS_OK;
445 }
446
447 if (NS_SUCCEEDED(aResult)) {
448 mChunk = aChunk;
449 } else if (aResult != NS_ERROR_NOT_AVAILABLE) {
450 // We store the error in mStatus, so we can propagate it later to consumer
451 // in Read(), Available() etc. We need to handle NS_ERROR_NOT_AVAILABLE
452 // differently since it is returned when the requested chunk is not
453 // available and there is no writer that could create it, i.e. it means that
454 // we've reached the end of the file.
455 mStatus = aResult;
456 }
457
458 MaybeNotifyListener();
459
460 return NS_OK;
461 }
462
463 nsresult
464 CacheFileInputStream::OnChunkUpdated(CacheFileChunk *aChunk)
465 {
466 CacheFileAutoLock lock(mFile);
467 MOZ_ASSERT(!mInReadSegments);
468
469 LOG(("CacheFileInputStream::OnChunkUpdated() [this=%p, idx=%d]",
470 this, aChunk->Index()));
471
472 if (!mWaitingForUpdate) {
473 LOG(("CacheFileInputStream::OnChunkUpdated() - Ignoring notification since "
474 "mWaitingforUpdate == false. [this=%p]", this));
475
476 return NS_OK;
477 }
478 else {
479 mWaitingForUpdate = false;
480 }
481
482 MOZ_ASSERT(mChunk == aChunk);
483
484 MaybeNotifyListener();
485
486 return NS_OK;
487 }
488
489 void
490 CacheFileInputStream::ReleaseChunk()
491 {
492 mFile->AssertOwnsLock();
493
494 LOG(("CacheFileInputStream::ReleaseChunk() [this=%p, idx=%d]",
495 this, mChunk->Index()));
496
497 if (mWaitingForUpdate) {
498 LOG(("CacheFileInputStream::ReleaseChunk() - Canceling waiting for update. "
499 "[this=%p]", this));
500
501 mChunk->CancelWait(this);
502 mWaitingForUpdate = false;
503 }
504
505 mFile->ReleaseOutsideLock(mChunk.forget().take());
506 }
507
508 void
509 CacheFileInputStream::EnsureCorrectChunk(bool aReleaseOnly)
510 {
511 mFile->AssertOwnsLock();
512
513 LOG(("CacheFileInputStream::EnsureCorrectChunk() [this=%p, releaseOnly=%d]",
514 this, aReleaseOnly));
515
516 nsresult rv;
517
518 uint32_t chunkIdx = mPos / kChunkSize;
519
520 if (mChunk) {
521 if (mChunk->Index() == chunkIdx) {
522 // we have a correct chunk
523 LOG(("CacheFileInputStream::EnsureCorrectChunk() - Have correct chunk "
524 "[this=%p, idx=%d]", this, chunkIdx));
525
526 return;
527 }
528 else {
529 ReleaseChunk();
530 }
531 }
532
533 MOZ_ASSERT(!mWaitingForUpdate);
534
535 if (aReleaseOnly)
536 return;
537
538 if (mListeningForChunk == static_cast<int64_t>(chunkIdx)) {
539 // We're already waiting for this chunk
540 LOG(("CacheFileInputStream::EnsureCorrectChunk() - Already listening for "
541 "chunk %lld [this=%p]", mListeningForChunk, this));
542
543 return;
544 }
545
546 rv = mFile->GetChunkLocked(chunkIdx, false, this, getter_AddRefs(mChunk));
547 if (NS_FAILED(rv)) {
548 LOG(("CacheFileInputStream::EnsureCorrectChunk() - GetChunkLocked failed. "
549 "[this=%p, idx=%d, rv=0x%08x]", this, chunkIdx, rv));
550 if (rv != NS_ERROR_NOT_AVAILABLE) {
551 // We store the error in mStatus, so we can propagate it later to consumer
552 // in Read(), Available() etc. We need to handle NS_ERROR_NOT_AVAILABLE
553 // differently since it is returned when the requested chunk is not
554 // available and there is no writer that could create it, i.e. it means
555 // that we've reached the end of the file.
556 mStatus = rv;
557 }
558 }
559 else if (!mChunk) {
560 mListeningForChunk = static_cast<int64_t>(chunkIdx);
561 }
562
563 MaybeNotifyListener();
564 }
565
566 void
567 CacheFileInputStream::CanRead(int64_t *aCanRead, const char **aBuf)
568 {
569 mFile->AssertOwnsLock();
570
571 MOZ_ASSERT(mChunk);
572 MOZ_ASSERT(mPos / kChunkSize == mChunk->Index());
573
574 uint32_t chunkOffset = mPos - (mPos / kChunkSize) * kChunkSize;
575 *aCanRead = mChunk->DataSize() - chunkOffset;
576 *aBuf = mChunk->BufForReading() + chunkOffset;
577
578 LOG(("CacheFileInputStream::CanRead() [this=%p, canRead=%lld]",
579 this, *aCanRead));
580 }
581
582 void
583 CacheFileInputStream::NotifyListener()
584 {
585 mFile->AssertOwnsLock();
586
587 LOG(("CacheFileInputStream::NotifyListener() [this=%p]", this));
588
589 MOZ_ASSERT(mCallback);
590
591 if (!mCallbackTarget)
592 mCallbackTarget = NS_GetCurrentThread();
593
594 nsCOMPtr<nsIInputStreamCallback> asyncCallback =
595 NS_NewInputStreamReadyEvent(mCallback, mCallbackTarget);
596
597 mCallback = nullptr;
598 mCallbackTarget = nullptr;
599
600 asyncCallback->OnInputStreamReady(this);
601 }
602
603 void
604 CacheFileInputStream::MaybeNotifyListener()
605 {
606 mFile->AssertOwnsLock();
607
608 LOG(("CacheFileInputStream::MaybeNotifyListener() [this=%p, mCallback=%p, "
609 "mClosed=%d, mStatus=0x%08x, mChunk=%p, mListeningForChunk=%lld, "
610 "mWaitingForUpdate=%d]", this, mCallback.get(), mClosed, mStatus,
611 mChunk.get(), mListeningForChunk, mWaitingForUpdate));
612
613 if (!mCallback)
614 return;
615
616 if (mClosed || NS_FAILED(mStatus)) {
617 NotifyListener();
618 return;
619 }
620
621 if (!mChunk) {
622 if (mListeningForChunk == -1) {
623 // EOF, should we notify even if mCallbackFlags == WAIT_CLOSURE_ONLY ??
624 NotifyListener();
625 }
626 return;
627 }
628
629 MOZ_ASSERT(mPos / kChunkSize == mChunk->Index());
630
631 if (mWaitingForUpdate)
632 return;
633
634 int64_t canRead;
635 const char *buf;
636 CanRead(&canRead, &buf);
637
638 if (canRead > 0) {
639 if (!(mCallbackFlags & WAIT_CLOSURE_ONLY))
640 NotifyListener();
641 }
642 else if (canRead == 0) {
643 if (!mFile->mOutput) {
644 // EOF
645 NotifyListener();
646 }
647 else {
648 mChunk->WaitForUpdate(this);
649 mWaitingForUpdate = true;
650 }
651 }
652 else {
653 // Output have set EOF before mPos?
654 MOZ_ASSERT(false, "SetEOF is currenty not implemented?!");
655 NotifyListener();
656 }
657 }
658
659 // Memory reporting
660
661 size_t
662 CacheFileInputStream::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
663 {
664 // Everything the stream keeps a reference to is already reported somewhere else.
665 // mFile reports itself.
666 // mChunk reported as part of CacheFile.
667 // mCallback is usually CacheFile or a class that is reported elsewhere.
668 return mallocSizeOf(this);
669 }
670
671 } // net
672 } // mozilla

mercurial