Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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 "CacheFileChunk.h"
8 #include "CacheFile.h"
9 #include "nsThreadUtils.h"
10 #include "nsAlgorithm.h"
11 #include <algorithm>
13 namespace mozilla {
14 namespace net {
16 #define kMinBufSize 512
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 }
30 ~NotifyUpdateListenerEvent()
31 {
32 LOG(("NotifyUpdateListenerEvent::~NotifyUpdateListenerEvent() [this=%p]",
33 this));
34 MOZ_COUNT_DTOR(NotifyUpdateListenerEvent);
35 }
37 NS_IMETHOD Run()
38 {
39 LOG(("NotifyUpdateListenerEvent::Run() [this=%p]", this));
41 mCallback->OnChunkUpdated(mChunk);
42 return NS_OK;
43 }
45 protected:
46 nsCOMPtr<CacheFileChunkListener> mCallback;
47 nsRefPtr<CacheFileChunk> mChunk;
48 };
51 class ValidityPair {
52 public:
53 ValidityPair(uint32_t aOffset, uint32_t aLen)
54 : mOffset(aOffset), mLen(aLen)
55 {}
57 ValidityPair& operator=(const ValidityPair& aOther) {
58 mOffset = aOther.mOffset;
59 mLen = aOther.mLen;
60 return *this;
61 }
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;
68 return false;
69 }
71 bool LessThan(const ValidityPair& aOther) const {
72 if (mOffset < aOther.mOffset)
73 return true;
75 if (mOffset == aOther.mOffset && mLen < aOther.mLen)
76 return true;
78 return false;
79 }
81 void Merge(const ValidityPair& aOther) {
82 MOZ_ASSERT(Overlaps(aOther));
84 uint32_t offset = std::min(mOffset, aOther.mOffset);
85 uint32_t end = std::max(mOffset + mLen, aOther.mOffset + aOther.mLen);
87 mOffset = offset;
88 mLen = end - offset;
89 }
91 uint32_t Offset() { return mOffset; }
92 uint32_t Len() { return mLen; }
94 private:
95 uint32_t mOffset;
96 uint32_t mLen;
97 };
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");
108 if (0 == count) {
109 mRefCnt = 1;
110 delete (this);
111 return 0;
112 }
114 if (!mRemovingChunk && count == 1) {
115 mFile->RemoveChunk(this);
116 }
118 return count;
119 }
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
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 }
145 CacheFileChunk::~CacheFileChunk()
146 {
147 LOG(("CacheFileChunk::~CacheFileChunk() [this=%p]", this));
148 MOZ_COUNT_DTOR(CacheFileChunk);
150 if (mBuf) {
151 free(mBuf);
152 mBuf = nullptr;
153 mBufSize = 0;
154 }
156 if (mRWBuf) {
157 free(mRWBuf);
158 mRWBuf = nullptr;
159 mRWBufSize = 0;
160 }
161 }
163 void
164 CacheFileChunk::InitNew(CacheFileChunkListener *aCallback)
165 {
166 mFile->AssertOwnsLock();
168 LOG(("CacheFileChunk::InitNew() [this=%p, listener=%p]", this, aCallback));
170 MOZ_ASSERT(mState == INITIAL);
171 MOZ_ASSERT(!mBuf);
172 MOZ_ASSERT(!mRWBuf);
174 mBuf = static_cast<char *>(moz_xmalloc(kMinBufSize));
175 mBufSize = kMinBufSize;
176 mDataSize = 0;
177 mState = READY;
178 mIsDirty = true;
180 DoMemoryReport(MemorySize());
181 }
183 nsresult
184 CacheFileChunk::Read(CacheFileHandle *aHandle, uint32_t aLen,
185 CacheHash::Hash16_t aHash,
186 CacheFileChunkListener *aCallback)
187 {
188 mFile->AssertOwnsLock();
190 LOG(("CacheFileChunk::Read() [this=%p, handle=%p, len=%d, listener=%p]",
191 this, aHandle, aLen, aCallback));
193 MOZ_ASSERT(mState == INITIAL);
194 MOZ_ASSERT(!mBuf);
195 MOZ_ASSERT(!mRWBuf);
196 MOZ_ASSERT(aLen);
198 nsresult rv;
200 mRWBuf = static_cast<char *>(moz_xmalloc(aLen));
201 mRWBufSize = aLen;
203 DoMemoryReport(MemorySize());
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 }
217 return rv;
218 }
220 nsresult
221 CacheFileChunk::Write(CacheFileHandle *aHandle,
222 CacheFileChunkListener *aCallback)
223 {
224 mFile->AssertOwnsLock();
226 LOG(("CacheFileChunk::Write() [this=%p, handle=%p, listener=%p]",
227 this, aHandle, aCallback));
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
234 nsresult rv;
236 mRWBuf = mBuf;
237 mRWBufSize = mBufSize;
238 mBuf = nullptr;
239 mBufSize = 0;
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 }
251 return rv;
252 }
254 void
255 CacheFileChunk::WaitForUpdate(CacheFileChunkListener *aCallback)
256 {
257 mFile->AssertOwnsLock();
259 LOG(("CacheFileChunk::WaitForUpdate() [this=%p, listener=%p]",
260 this, aCallback));
262 MOZ_ASSERT(mFile->mOutput);
263 MOZ_ASSERT(IsReady());
265 #ifdef DEBUG
266 for (uint32_t i = 0 ; i < mUpdateListeners.Length() ; i++) {
267 MOZ_ASSERT(mUpdateListeners[i]->mCallback != aCallback);
268 }
269 #endif
271 ChunkListenerItem *item = new ChunkListenerItem();
272 item->mTarget = NS_GetCurrentThread();
273 item->mCallback = aCallback;
275 mUpdateListeners.AppendElement(item);
276 }
278 nsresult
279 CacheFileChunk::CancelWait(CacheFileChunkListener *aCallback)
280 {
281 mFile->AssertOwnsLock();
283 LOG(("CacheFileChunk::CancelWait() [this=%p, listener=%p]", this, aCallback));
285 MOZ_ASSERT(IsReady());
287 uint32_t i;
288 for (i = 0 ; i < mUpdateListeners.Length() ; i++) {
289 ChunkListenerItem *item = mUpdateListeners[i];
291 if (item->mCallback == aCallback) {
292 mUpdateListeners.RemoveElementAt(i);
293 delete item;
294 break;
295 }
296 }
298 #ifdef DEBUG
299 for ( ; i < mUpdateListeners.Length() ; i++) {
300 MOZ_ASSERT(mUpdateListeners[i]->mCallback != aCallback);
301 }
302 #endif
304 return NS_OK;
305 }
307 nsresult
308 CacheFileChunk::NotifyUpdateListeners()
309 {
310 mFile->AssertOwnsLock();
312 LOG(("CacheFileChunk::NotifyUpdateListeners() [this=%p]", this));
314 MOZ_ASSERT(IsReady());
316 nsresult rv, rv2;
318 rv = NS_OK;
319 for (uint32_t i = 0 ; i < mUpdateListeners.Length() ; i++) {
320 ChunkListenerItem *item = mUpdateListeners[i];
322 LOG(("CacheFileChunk::NotifyUpdateListeners() - Notifying listener %p "
323 "[this=%p]", item->mCallback.get(), this));
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 }
333 mUpdateListeners.Clear();
335 return rv;
336 }
338 uint32_t
339 CacheFileChunk::Index()
340 {
341 return mIndex;
342 }
344 CacheHash::Hash16_t
345 CacheFileChunk::Hash()
346 {
347 mFile->AssertOwnsLock();
349 MOZ_ASSERT(mBuf);
350 MOZ_ASSERT(!mListener);
351 MOZ_ASSERT(IsReady());
353 return CacheHash::Hash16(BufForReading(), mDataSize);
354 }
356 uint32_t
357 CacheFileChunk::DataSize()
358 {
359 mFile->AssertOwnsLock();
360 return mDataSize;
361 }
363 void
364 CacheFileChunk::UpdateDataSize(uint32_t aOffset, uint32_t aLen, bool aEOF)
365 {
366 mFile->AssertOwnsLock();
368 MOZ_ASSERT(!aEOF, "Implement me! What to do with opened streams?");
369 MOZ_ASSERT(aOffset <= mDataSize);
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);
375 LOG(("CacheFileChunk::UpdateDataSize() [this=%p, offset=%d, len=%d, EOF=%d]",
376 this, aOffset, aLen, aEOF));
378 mIsDirty = true;
380 int64_t fileSize = kChunkSize * mIndex + aOffset + aLen;
381 bool notify = false;
383 if (fileSize > mFile->mDataSize)
384 mFile->mDataSize = fileSize;
386 if (aOffset + aLen > mDataSize) {
387 mDataSize = aOffset + aLen;
388 notify = true;
389 }
391 if (mState == READY || mState == WRITING) {
392 MOZ_ASSERT(mValidityMap.Length() == 0);
394 if (notify)
395 NotifyUpdateListeners();
397 return;
398 }
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.
405 MOZ_ASSERT(mUpdateListeners.Length() == 0);
406 MOZ_ASSERT(mState == READING);
408 ValidityPair pair(aOffset, aLen);
410 if (mValidityMap.Length() == 0) {
411 mValidityMap.AppendElement(pair);
412 return;
413 }
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 }
433 break;
434 }
435 }
437 if (!pos)
438 mValidityMap.InsertElementAt(0, pair);
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 }
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 }
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));
467 nsCOMPtr<CacheFileChunkListener> listener;
469 {
470 CacheFileAutoLock lock(mFile);
472 MOZ_ASSERT(mState == WRITING);
473 MOZ_ASSERT(mListener);
475 if (NS_WARN_IF(NS_FAILED(aResult))) {
476 SetError(aResult);
477 } else {
478 mState = READY;
479 }
481 if (!mBuf) {
482 mBuf = mRWBuf;
483 mBufSize = mRWBufSize;
484 } else {
485 free(mRWBuf);
486 }
488 mRWBuf = nullptr;
489 mRWBufSize = 0;
491 DoMemoryReport(MemorySize());
493 mListener.swap(listener);
494 }
496 listener->OnChunkWritten(aResult, this);
498 return NS_OK;
499 }
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));
508 nsCOMPtr<CacheFileChunkListener> listener;
510 {
511 CacheFileAutoLock lock(mFile);
513 MOZ_ASSERT(mState == READING);
514 MOZ_ASSERT(mListener);
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 }
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 }
544 free(mBuf);
545 mBuf = mRWBuf;
546 mBufSize = mRWBufSize;
547 mRWBuf = nullptr;
548 mRWBufSize = 0;
550 DoMemoryReport(MemorySize());
551 }
552 }
553 }
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 }
563 mListener.swap(listener);
564 }
566 listener->OnChunkRead(aResult, this);
568 return NS_OK;
569 }
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 }
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 }
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 }
592 bool
593 CacheFileChunk::IsReady() const
594 {
595 mFile->AssertOwnsLock();
597 return (NS_SUCCEEDED(mStatus) && (mState == READY || mState == WRITING));
598 }
600 bool
601 CacheFileChunk::IsDirty() const
602 {
603 mFile->AssertOwnsLock();
605 return mIsDirty;
606 }
608 nsresult
609 CacheFileChunk::GetStatus()
610 {
611 mFile->AssertOwnsLock();
613 return mStatus;
614 }
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 }
628 char *
629 CacheFileChunk::BufForWriting() const
630 {
631 mFile->AssertOwnsLock();
633 MOZ_ASSERT(mBuf); // Writer should always first call EnsureBufSize()
635 MOZ_ASSERT((mState == READY && !mRWBuf) ||
636 (mState == WRITING && mRWBuf) ||
637 (mState == READING && mRWBuf));
639 return mBuf;
640 }
642 const char *
643 CacheFileChunk::BufForReading() const
644 {
645 mFile->AssertOwnsLock();
647 MOZ_ASSERT((mState == READY && mBuf && !mRWBuf) ||
648 (mState == WRITING && mRWBuf));
650 return mBuf ? mBuf : mRWBuf;
651 }
653 void
654 CacheFileChunk::EnsureBufSize(uint32_t aBufSize)
655 {
656 mFile->AssertOwnsLock();
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);
662 if (mBufSize >= aBufSize)
663 return;
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;
671 if (mRWBufSize > aBufSize)
672 aBufSize = mRWBufSize;
673 }
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++;
684 const uint32_t minBufSize = kMinBufSize;
685 const uint32_t maxBufSize = kChunkSize;
686 aBufSize = clamped(aBufSize, minBufSize, maxBufSize);
688 mBuf = static_cast<char *>(moz_xrealloc(mBuf, aBufSize));
689 mBufSize = aBufSize;
691 if (copy)
692 memcpy(mBuf, mRWBuf, mRWBufSize);
694 DoMemoryReport(MemorySize());
695 }
697 // Memory reporting
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);
707 return n;
708 }
710 size_t
711 CacheFileChunk::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
712 {
713 return mallocSizeOf(this) + SizeOfExcludingThis(mallocSizeOf);
714 }
716 } // net
717 } // mozilla