netwerk/cache2/CacheFileChunk.cpp

branch
TOR_BUG_9701
changeset 10
ac0c01689b40
equal deleted inserted replaced
-1:000000000000 0:91347c8b33f1
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 "CacheFileChunk.h"
7
8 #include "CacheFile.h"
9 #include "nsThreadUtils.h"
10 #include "nsAlgorithm.h"
11 #include <algorithm>
12
13 namespace mozilla {
14 namespace net {
15
16 #define kMinBufSize 512
17
18 class NotifyUpdateListenerEvent : public nsRunnable {
19 public:
20 NotifyUpdateListenerEvent(CacheFileChunkListener *aCallback,
21 CacheFileChunk *aChunk)
22 : mCallback(aCallback)
23 , mChunk(aChunk)
24 {
25 LOG(("NotifyUpdateListenerEvent::NotifyUpdateListenerEvent() [this=%p]",
26 this));
27 MOZ_COUNT_CTOR(NotifyUpdateListenerEvent);
28 }
29
30 ~NotifyUpdateListenerEvent()
31 {
32 LOG(("NotifyUpdateListenerEvent::~NotifyUpdateListenerEvent() [this=%p]",
33 this));
34 MOZ_COUNT_DTOR(NotifyUpdateListenerEvent);
35 }
36
37 NS_IMETHOD Run()
38 {
39 LOG(("NotifyUpdateListenerEvent::Run() [this=%p]", this));
40
41 mCallback->OnChunkUpdated(mChunk);
42 return NS_OK;
43 }
44
45 protected:
46 nsCOMPtr<CacheFileChunkListener> mCallback;
47 nsRefPtr<CacheFileChunk> mChunk;
48 };
49
50
51 class ValidityPair {
52 public:
53 ValidityPair(uint32_t aOffset, uint32_t aLen)
54 : mOffset(aOffset), mLen(aLen)
55 {}
56
57 ValidityPair& operator=(const ValidityPair& aOther) {
58 mOffset = aOther.mOffset;
59 mLen = aOther.mLen;
60 return *this;
61 }
62
63 bool Overlaps(const ValidityPair& aOther) const {
64 if ((mOffset <= aOther.mOffset && mOffset + mLen >= aOther.mOffset) ||
65 (aOther.mOffset <= mOffset && aOther.mOffset + mLen >= mOffset))
66 return true;
67
68 return false;
69 }
70
71 bool LessThan(const ValidityPair& aOther) const {
72 if (mOffset < aOther.mOffset)
73 return true;
74
75 if (mOffset == aOther.mOffset && mLen < aOther.mLen)
76 return true;
77
78 return false;
79 }
80
81 void Merge(const ValidityPair& aOther) {
82 MOZ_ASSERT(Overlaps(aOther));
83
84 uint32_t offset = std::min(mOffset, aOther.mOffset);
85 uint32_t end = std::max(mOffset + mLen, aOther.mOffset + aOther.mLen);
86
87 mOffset = offset;
88 mLen = end - offset;
89 }
90
91 uint32_t Offset() { return mOffset; }
92 uint32_t Len() { return mLen; }
93
94 private:
95 uint32_t mOffset;
96 uint32_t mLen;
97 };
98
99
100 NS_IMPL_ADDREF(CacheFileChunk)
101 NS_IMETHODIMP_(MozExternalRefCountType)
102 CacheFileChunk::Release()
103 {
104 NS_PRECONDITION(0 != mRefCnt, "dup release");
105 nsrefcnt count = --mRefCnt;
106 NS_LOG_RELEASE(this, count, "CacheFileChunk");
107
108 if (0 == count) {
109 mRefCnt = 1;
110 delete (this);
111 return 0;
112 }
113
114 if (!mRemovingChunk && count == 1) {
115 mFile->RemoveChunk(this);
116 }
117
118 return count;
119 }
120
121 NS_INTERFACE_MAP_BEGIN(CacheFileChunk)
122 NS_INTERFACE_MAP_ENTRY(mozilla::net::CacheFileIOListener)
123 NS_INTERFACE_MAP_ENTRY(nsISupports)
124 NS_INTERFACE_MAP_END_THREADSAFE
125
126 CacheFileChunk::CacheFileChunk(CacheFile *aFile, uint32_t aIndex)
127 : CacheMemoryConsumer(aFile->mOpenAsMemoryOnly ? MEMORY_ONLY : DONT_REPORT)
128 , mIndex(aIndex)
129 , mState(INITIAL)
130 , mStatus(NS_OK)
131 , mIsDirty(false)
132 , mRemovingChunk(false)
133 , mDataSize(0)
134 , mBuf(nullptr)
135 , mBufSize(0)
136 , mRWBuf(nullptr)
137 , mRWBufSize(0)
138 , mReadHash(0)
139 , mFile(aFile)
140 {
141 LOG(("CacheFileChunk::CacheFileChunk() [this=%p]", this));
142 MOZ_COUNT_CTOR(CacheFileChunk);
143 }
144
145 CacheFileChunk::~CacheFileChunk()
146 {
147 LOG(("CacheFileChunk::~CacheFileChunk() [this=%p]", this));
148 MOZ_COUNT_DTOR(CacheFileChunk);
149
150 if (mBuf) {
151 free(mBuf);
152 mBuf = nullptr;
153 mBufSize = 0;
154 }
155
156 if (mRWBuf) {
157 free(mRWBuf);
158 mRWBuf = nullptr;
159 mRWBufSize = 0;
160 }
161 }
162
163 void
164 CacheFileChunk::InitNew(CacheFileChunkListener *aCallback)
165 {
166 mFile->AssertOwnsLock();
167
168 LOG(("CacheFileChunk::InitNew() [this=%p, listener=%p]", this, aCallback));
169
170 MOZ_ASSERT(mState == INITIAL);
171 MOZ_ASSERT(!mBuf);
172 MOZ_ASSERT(!mRWBuf);
173
174 mBuf = static_cast<char *>(moz_xmalloc(kMinBufSize));
175 mBufSize = kMinBufSize;
176 mDataSize = 0;
177 mState = READY;
178 mIsDirty = true;
179
180 DoMemoryReport(MemorySize());
181 }
182
183 nsresult
184 CacheFileChunk::Read(CacheFileHandle *aHandle, uint32_t aLen,
185 CacheHash::Hash16_t aHash,
186 CacheFileChunkListener *aCallback)
187 {
188 mFile->AssertOwnsLock();
189
190 LOG(("CacheFileChunk::Read() [this=%p, handle=%p, len=%d, listener=%p]",
191 this, aHandle, aLen, aCallback));
192
193 MOZ_ASSERT(mState == INITIAL);
194 MOZ_ASSERT(!mBuf);
195 MOZ_ASSERT(!mRWBuf);
196 MOZ_ASSERT(aLen);
197
198 nsresult rv;
199
200 mRWBuf = static_cast<char *>(moz_xmalloc(aLen));
201 mRWBufSize = aLen;
202
203 DoMemoryReport(MemorySize());
204
205 rv = CacheFileIOManager::Read(aHandle, mIndex * kChunkSize, mRWBuf, aLen,
206 true, this);
207 if (NS_WARN_IF(NS_FAILED(rv))) {
208 rv = mIndex ? NS_ERROR_FILE_CORRUPTED : NS_ERROR_FILE_NOT_FOUND;
209 SetError(rv);
210 } else {
211 mState = READING;
212 mListener = aCallback;
213 mDataSize = aLen;
214 mReadHash = aHash;
215 }
216
217 return rv;
218 }
219
220 nsresult
221 CacheFileChunk::Write(CacheFileHandle *aHandle,
222 CacheFileChunkListener *aCallback)
223 {
224 mFile->AssertOwnsLock();
225
226 LOG(("CacheFileChunk::Write() [this=%p, handle=%p, listener=%p]",
227 this, aHandle, aCallback));
228
229 MOZ_ASSERT(mState == READY);
230 MOZ_ASSERT(!mRWBuf);
231 MOZ_ASSERT(mBuf);
232 MOZ_ASSERT(mDataSize); // Don't write chunk when it is empty
233
234 nsresult rv;
235
236 mRWBuf = mBuf;
237 mRWBufSize = mBufSize;
238 mBuf = nullptr;
239 mBufSize = 0;
240
241 rv = CacheFileIOManager::Write(aHandle, mIndex * kChunkSize, mRWBuf,
242 mDataSize, false, this);
243 if (NS_WARN_IF(NS_FAILED(rv))) {
244 SetError(rv);
245 } else {
246 mState = WRITING;
247 mListener = aCallback;
248 mIsDirty = false;
249 }
250
251 return rv;
252 }
253
254 void
255 CacheFileChunk::WaitForUpdate(CacheFileChunkListener *aCallback)
256 {
257 mFile->AssertOwnsLock();
258
259 LOG(("CacheFileChunk::WaitForUpdate() [this=%p, listener=%p]",
260 this, aCallback));
261
262 MOZ_ASSERT(mFile->mOutput);
263 MOZ_ASSERT(IsReady());
264
265 #ifdef DEBUG
266 for (uint32_t i = 0 ; i < mUpdateListeners.Length() ; i++) {
267 MOZ_ASSERT(mUpdateListeners[i]->mCallback != aCallback);
268 }
269 #endif
270
271 ChunkListenerItem *item = new ChunkListenerItem();
272 item->mTarget = NS_GetCurrentThread();
273 item->mCallback = aCallback;
274
275 mUpdateListeners.AppendElement(item);
276 }
277
278 nsresult
279 CacheFileChunk::CancelWait(CacheFileChunkListener *aCallback)
280 {
281 mFile->AssertOwnsLock();
282
283 LOG(("CacheFileChunk::CancelWait() [this=%p, listener=%p]", this, aCallback));
284
285 MOZ_ASSERT(IsReady());
286
287 uint32_t i;
288 for (i = 0 ; i < mUpdateListeners.Length() ; i++) {
289 ChunkListenerItem *item = mUpdateListeners[i];
290
291 if (item->mCallback == aCallback) {
292 mUpdateListeners.RemoveElementAt(i);
293 delete item;
294 break;
295 }
296 }
297
298 #ifdef DEBUG
299 for ( ; i < mUpdateListeners.Length() ; i++) {
300 MOZ_ASSERT(mUpdateListeners[i]->mCallback != aCallback);
301 }
302 #endif
303
304 return NS_OK;
305 }
306
307 nsresult
308 CacheFileChunk::NotifyUpdateListeners()
309 {
310 mFile->AssertOwnsLock();
311
312 LOG(("CacheFileChunk::NotifyUpdateListeners() [this=%p]", this));
313
314 MOZ_ASSERT(IsReady());
315
316 nsresult rv, rv2;
317
318 rv = NS_OK;
319 for (uint32_t i = 0 ; i < mUpdateListeners.Length() ; i++) {
320 ChunkListenerItem *item = mUpdateListeners[i];
321
322 LOG(("CacheFileChunk::NotifyUpdateListeners() - Notifying listener %p "
323 "[this=%p]", item->mCallback.get(), this));
324
325 nsRefPtr<NotifyUpdateListenerEvent> ev;
326 ev = new NotifyUpdateListenerEvent(item->mCallback, this);
327 rv2 = item->mTarget->Dispatch(ev, NS_DISPATCH_NORMAL);
328 if (NS_FAILED(rv2) && NS_SUCCEEDED(rv))
329 rv = rv2;
330 delete item;
331 }
332
333 mUpdateListeners.Clear();
334
335 return rv;
336 }
337
338 uint32_t
339 CacheFileChunk::Index()
340 {
341 return mIndex;
342 }
343
344 CacheHash::Hash16_t
345 CacheFileChunk::Hash()
346 {
347 mFile->AssertOwnsLock();
348
349 MOZ_ASSERT(mBuf);
350 MOZ_ASSERT(!mListener);
351 MOZ_ASSERT(IsReady());
352
353 return CacheHash::Hash16(BufForReading(), mDataSize);
354 }
355
356 uint32_t
357 CacheFileChunk::DataSize()
358 {
359 mFile->AssertOwnsLock();
360 return mDataSize;
361 }
362
363 void
364 CacheFileChunk::UpdateDataSize(uint32_t aOffset, uint32_t aLen, bool aEOF)
365 {
366 mFile->AssertOwnsLock();
367
368 MOZ_ASSERT(!aEOF, "Implement me! What to do with opened streams?");
369 MOZ_ASSERT(aOffset <= mDataSize);
370
371 // UpdateDataSize() is called only when we've written some data to the chunk
372 // and we never write data anymore once some error occurs.
373 MOZ_ASSERT(mState != ERROR);
374
375 LOG(("CacheFileChunk::UpdateDataSize() [this=%p, offset=%d, len=%d, EOF=%d]",
376 this, aOffset, aLen, aEOF));
377
378 mIsDirty = true;
379
380 int64_t fileSize = kChunkSize * mIndex + aOffset + aLen;
381 bool notify = false;
382
383 if (fileSize > mFile->mDataSize)
384 mFile->mDataSize = fileSize;
385
386 if (aOffset + aLen > mDataSize) {
387 mDataSize = aOffset + aLen;
388 notify = true;
389 }
390
391 if (mState == READY || mState == WRITING) {
392 MOZ_ASSERT(mValidityMap.Length() == 0);
393
394 if (notify)
395 NotifyUpdateListeners();
396
397 return;
398 }
399
400 // We're still waiting for data from the disk. This chunk cannot be used by
401 // input stream, so there must be no update listener. We also need to keep
402 // track of where the data is written so that we can correctly merge the new
403 // data with the old one.
404
405 MOZ_ASSERT(mUpdateListeners.Length() == 0);
406 MOZ_ASSERT(mState == READING);
407
408 ValidityPair pair(aOffset, aLen);
409
410 if (mValidityMap.Length() == 0) {
411 mValidityMap.AppendElement(pair);
412 return;
413 }
414
415
416 // Find out where to place this pair into the map, it can overlap with
417 // one preceding pair and all subsequent pairs.
418 uint32_t pos = 0;
419 for (pos = mValidityMap.Length() ; pos > 0 ; pos--) {
420 if (mValidityMap[pos-1].LessThan(pair)) {
421 if (mValidityMap[pos-1].Overlaps(pair)) {
422 // Merge with the preceding pair
423 mValidityMap[pos-1].Merge(pair);
424 pos--; // Point to the updated pair
425 }
426 else {
427 if (pos == mValidityMap.Length())
428 mValidityMap.AppendElement(pair);
429 else
430 mValidityMap.InsertElementAt(pos, pair);
431 }
432
433 break;
434 }
435 }
436
437 if (!pos)
438 mValidityMap.InsertElementAt(0, pair);
439
440 // Now pos points to merged or inserted pair, check whether it overlaps with
441 // subsequent pairs.
442 while (pos + 1 < mValidityMap.Length()) {
443 if (mValidityMap[pos].Overlaps(mValidityMap[pos + 1])) {
444 mValidityMap[pos].Merge(mValidityMap[pos + 1]);
445 mValidityMap.RemoveElementAt(pos + 1);
446 }
447 else {
448 break;
449 }
450 }
451 }
452
453 nsresult
454 CacheFileChunk::OnFileOpened(CacheFileHandle *aHandle, nsresult aResult)
455 {
456 MOZ_CRASH("CacheFileChunk::OnFileOpened should not be called!");
457 return NS_ERROR_UNEXPECTED;
458 }
459
460 nsresult
461 CacheFileChunk::OnDataWritten(CacheFileHandle *aHandle, const char *aBuf,
462 nsresult aResult)
463 {
464 LOG(("CacheFileChunk::OnDataWritten() [this=%p, handle=%p, result=0x%08x]",
465 this, aHandle, aResult));
466
467 nsCOMPtr<CacheFileChunkListener> listener;
468
469 {
470 CacheFileAutoLock lock(mFile);
471
472 MOZ_ASSERT(mState == WRITING);
473 MOZ_ASSERT(mListener);
474
475 if (NS_WARN_IF(NS_FAILED(aResult))) {
476 SetError(aResult);
477 } else {
478 mState = READY;
479 }
480
481 if (!mBuf) {
482 mBuf = mRWBuf;
483 mBufSize = mRWBufSize;
484 } else {
485 free(mRWBuf);
486 }
487
488 mRWBuf = nullptr;
489 mRWBufSize = 0;
490
491 DoMemoryReport(MemorySize());
492
493 mListener.swap(listener);
494 }
495
496 listener->OnChunkWritten(aResult, this);
497
498 return NS_OK;
499 }
500
501 nsresult
502 CacheFileChunk::OnDataRead(CacheFileHandle *aHandle, char *aBuf,
503 nsresult aResult)
504 {
505 LOG(("CacheFileChunk::OnDataRead() [this=%p, handle=%p, result=0x%08x]",
506 this, aHandle, aResult));
507
508 nsCOMPtr<CacheFileChunkListener> listener;
509
510 {
511 CacheFileAutoLock lock(mFile);
512
513 MOZ_ASSERT(mState == READING);
514 MOZ_ASSERT(mListener);
515
516 if (NS_SUCCEEDED(aResult)) {
517 CacheHash::Hash16_t hash = CacheHash::Hash16(mRWBuf, mRWBufSize);
518 if (hash != mReadHash) {
519 LOG(("CacheFileChunk::OnDataRead() - Hash mismatch! Hash of the data is"
520 " %hx, hash in metadata is %hx. [this=%p, idx=%d]",
521 hash, mReadHash, this, mIndex));
522 aResult = NS_ERROR_FILE_CORRUPTED;
523 }
524 else {
525 if (!mBuf) {
526 // Just swap the buffers if we don't have mBuf yet
527 MOZ_ASSERT(mDataSize == mRWBufSize);
528 mBuf = mRWBuf;
529 mBufSize = mRWBufSize;
530 mRWBuf = nullptr;
531 mRWBufSize = 0;
532 } else {
533 // Merge data with write buffer
534 if (mRWBufSize < mBufSize) {
535 mRWBuf = static_cast<char *>(moz_xrealloc(mRWBuf, mBufSize));
536 mRWBufSize = mBufSize;
537 }
538
539 for (uint32_t i = 0 ; i < mValidityMap.Length() ; i++) {
540 memcpy(mRWBuf + mValidityMap[i].Offset(),
541 mBuf + mValidityMap[i].Offset(), mValidityMap[i].Len());
542 }
543
544 free(mBuf);
545 mBuf = mRWBuf;
546 mBufSize = mRWBufSize;
547 mRWBuf = nullptr;
548 mRWBufSize = 0;
549
550 DoMemoryReport(MemorySize());
551 }
552 }
553 }
554
555 if (NS_FAILED(aResult)) {
556 aResult = mIndex ? NS_ERROR_FILE_CORRUPTED : NS_ERROR_FILE_NOT_FOUND;
557 SetError(aResult);
558 mDataSize = 0;
559 } else {
560 mState = READY;
561 }
562
563 mListener.swap(listener);
564 }
565
566 listener->OnChunkRead(aResult, this);
567
568 return NS_OK;
569 }
570
571 nsresult
572 CacheFileChunk::OnFileDoomed(CacheFileHandle *aHandle, nsresult aResult)
573 {
574 MOZ_CRASH("CacheFileChunk::OnFileDoomed should not be called!");
575 return NS_ERROR_UNEXPECTED;
576 }
577
578 nsresult
579 CacheFileChunk::OnEOFSet(CacheFileHandle *aHandle, nsresult aResult)
580 {
581 MOZ_CRASH("CacheFileChunk::OnEOFSet should not be called!");
582 return NS_ERROR_UNEXPECTED;
583 }
584
585 nsresult
586 CacheFileChunk::OnFileRenamed(CacheFileHandle *aHandle, nsresult aResult)
587 {
588 MOZ_CRASH("CacheFileChunk::OnFileRenamed should not be called!");
589 return NS_ERROR_UNEXPECTED;
590 }
591
592 bool
593 CacheFileChunk::IsReady() const
594 {
595 mFile->AssertOwnsLock();
596
597 return (NS_SUCCEEDED(mStatus) && (mState == READY || mState == WRITING));
598 }
599
600 bool
601 CacheFileChunk::IsDirty() const
602 {
603 mFile->AssertOwnsLock();
604
605 return mIsDirty;
606 }
607
608 nsresult
609 CacheFileChunk::GetStatus()
610 {
611 mFile->AssertOwnsLock();
612
613 return mStatus;
614 }
615
616 void
617 CacheFileChunk::SetError(nsresult aStatus)
618 {
619 if (NS_SUCCEEDED(mStatus)) {
620 MOZ_ASSERT(mState != ERROR);
621 mStatus = aStatus;
622 mState = ERROR;
623 } else {
624 MOZ_ASSERT(mState == ERROR);
625 }
626 }
627
628 char *
629 CacheFileChunk::BufForWriting() const
630 {
631 mFile->AssertOwnsLock();
632
633 MOZ_ASSERT(mBuf); // Writer should always first call EnsureBufSize()
634
635 MOZ_ASSERT((mState == READY && !mRWBuf) ||
636 (mState == WRITING && mRWBuf) ||
637 (mState == READING && mRWBuf));
638
639 return mBuf;
640 }
641
642 const char *
643 CacheFileChunk::BufForReading() const
644 {
645 mFile->AssertOwnsLock();
646
647 MOZ_ASSERT((mState == READY && mBuf && !mRWBuf) ||
648 (mState == WRITING && mRWBuf));
649
650 return mBuf ? mBuf : mRWBuf;
651 }
652
653 void
654 CacheFileChunk::EnsureBufSize(uint32_t aBufSize)
655 {
656 mFile->AssertOwnsLock();
657
658 // EnsureBufSize() is called only when we want to write some data to the chunk
659 // and we never write data anymore once some error occurs.
660 MOZ_ASSERT(mState != ERROR);
661
662 if (mBufSize >= aBufSize)
663 return;
664
665 bool copy = false;
666 if (!mBuf && mState == WRITING) {
667 // We need to duplicate the data that is being written on the background
668 // thread, so make sure that all the data fits into the new buffer.
669 copy = true;
670
671 if (mRWBufSize > aBufSize)
672 aBufSize = mRWBufSize;
673 }
674
675 // find smallest power of 2 greater than or equal to aBufSize
676 aBufSize--;
677 aBufSize |= aBufSize >> 1;
678 aBufSize |= aBufSize >> 2;
679 aBufSize |= aBufSize >> 4;
680 aBufSize |= aBufSize >> 8;
681 aBufSize |= aBufSize >> 16;
682 aBufSize++;
683
684 const uint32_t minBufSize = kMinBufSize;
685 const uint32_t maxBufSize = kChunkSize;
686 aBufSize = clamped(aBufSize, minBufSize, maxBufSize);
687
688 mBuf = static_cast<char *>(moz_xrealloc(mBuf, aBufSize));
689 mBufSize = aBufSize;
690
691 if (copy)
692 memcpy(mBuf, mRWBuf, mRWBufSize);
693
694 DoMemoryReport(MemorySize());
695 }
696
697 // Memory reporting
698
699 size_t
700 CacheFileChunk::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
701 {
702 size_t n = 0;
703 n += mallocSizeOf(mBuf);
704 n += mallocSizeOf(mRWBuf);
705 n += mValidityMap.SizeOfExcludingThis(mallocSizeOf);
706
707 return n;
708 }
709
710 size_t
711 CacheFileChunk::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
712 {
713 return mallocSizeOf(this) + SizeOfExcludingThis(mallocSizeOf);
714 }
715
716 } // net
717 } // mozilla

mercurial