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 "CacheFile.h"
8 #include "CacheFileChunk.h"
9 #include "CacheFileInputStream.h"
10 #include "CacheFileOutputStream.h"
11 #include "CacheIndex.h"
12 #include "nsThreadUtils.h"
13 #include "mozilla/DebugOnly.h"
14 #include <algorithm>
15 #include "nsComponentManagerUtils.h"
16 #include "nsProxyRelease.h"
18 // When CACHE_CHUNKS is defined we always cache unused chunks in mCacheChunks.
19 // When it is not defined, we always release the chunks ASAP, i.e. we cache
20 // unused chunks only when:
21 // - CacheFile is memory-only
22 // - CacheFile is still waiting for the handle
24 //#define CACHE_CHUNKS
26 namespace mozilla {
27 namespace net {
29 class NotifyCacheFileListenerEvent : public nsRunnable {
30 public:
31 NotifyCacheFileListenerEvent(CacheFileListener *aCallback,
32 nsresult aResult,
33 bool aIsNew)
34 : mCallback(aCallback)
35 , mRV(aResult)
36 , mIsNew(aIsNew)
37 {
38 LOG(("NotifyCacheFileListenerEvent::NotifyCacheFileListenerEvent() "
39 "[this=%p]", this));
40 MOZ_COUNT_CTOR(NotifyCacheFileListenerEvent);
41 }
43 ~NotifyCacheFileListenerEvent()
44 {
45 LOG(("NotifyCacheFileListenerEvent::~NotifyCacheFileListenerEvent() "
46 "[this=%p]", this));
47 MOZ_COUNT_DTOR(NotifyCacheFileListenerEvent);
48 }
50 NS_IMETHOD Run()
51 {
52 LOG(("NotifyCacheFileListenerEvent::Run() [this=%p]", this));
54 mCallback->OnFileReady(mRV, mIsNew);
55 return NS_OK;
56 }
58 protected:
59 nsCOMPtr<CacheFileListener> mCallback;
60 nsresult mRV;
61 bool mIsNew;
62 };
64 class NotifyChunkListenerEvent : public nsRunnable {
65 public:
66 NotifyChunkListenerEvent(CacheFileChunkListener *aCallback,
67 nsresult aResult,
68 uint32_t aChunkIdx,
69 CacheFileChunk *aChunk)
70 : mCallback(aCallback)
71 , mRV(aResult)
72 , mChunkIdx(aChunkIdx)
73 , mChunk(aChunk)
74 {
75 LOG(("NotifyChunkListenerEvent::NotifyChunkListenerEvent() [this=%p]",
76 this));
77 MOZ_COUNT_CTOR(NotifyChunkListenerEvent);
78 }
80 ~NotifyChunkListenerEvent()
81 {
82 LOG(("NotifyChunkListenerEvent::~NotifyChunkListenerEvent() [this=%p]",
83 this));
84 MOZ_COUNT_DTOR(NotifyChunkListenerEvent);
85 }
87 NS_IMETHOD Run()
88 {
89 LOG(("NotifyChunkListenerEvent::Run() [this=%p]", this));
91 mCallback->OnChunkAvailable(mRV, mChunkIdx, mChunk);
92 return NS_OK;
93 }
95 protected:
96 nsCOMPtr<CacheFileChunkListener> mCallback;
97 nsresult mRV;
98 uint32_t mChunkIdx;
99 nsRefPtr<CacheFileChunk> mChunk;
100 };
103 class DoomFileHelper : public CacheFileIOListener
104 {
105 public:
106 NS_DECL_THREADSAFE_ISUPPORTS
108 DoomFileHelper(CacheFileListener *aListener)
109 : mListener(aListener)
110 {
111 MOZ_COUNT_CTOR(DoomFileHelper);
112 }
115 NS_IMETHOD OnFileOpened(CacheFileHandle *aHandle, nsresult aResult)
116 {
117 MOZ_CRASH("DoomFileHelper::OnFileOpened should not be called!");
118 return NS_ERROR_UNEXPECTED;
119 }
121 NS_IMETHOD OnDataWritten(CacheFileHandle *aHandle, const char *aBuf,
122 nsresult aResult)
123 {
124 MOZ_CRASH("DoomFileHelper::OnDataWritten should not be called!");
125 return NS_ERROR_UNEXPECTED;
126 }
128 NS_IMETHOD OnDataRead(CacheFileHandle *aHandle, char *aBuf, nsresult aResult)
129 {
130 MOZ_CRASH("DoomFileHelper::OnDataRead should not be called!");
131 return NS_ERROR_UNEXPECTED;
132 }
134 NS_IMETHOD OnFileDoomed(CacheFileHandle *aHandle, nsresult aResult)
135 {
136 if (mListener)
137 mListener->OnFileDoomed(aResult);
138 return NS_OK;
139 }
141 NS_IMETHOD OnEOFSet(CacheFileHandle *aHandle, nsresult aResult)
142 {
143 MOZ_CRASH("DoomFileHelper::OnEOFSet should not be called!");
144 return NS_ERROR_UNEXPECTED;
145 }
147 NS_IMETHOD OnFileRenamed(CacheFileHandle *aHandle, nsresult aResult)
148 {
149 MOZ_CRASH("DoomFileHelper::OnFileRenamed should not be called!");
150 return NS_ERROR_UNEXPECTED;
151 }
153 private:
154 virtual ~DoomFileHelper()
155 {
156 MOZ_COUNT_DTOR(DoomFileHelper);
157 }
159 nsCOMPtr<CacheFileListener> mListener;
160 };
162 NS_IMPL_ISUPPORTS(DoomFileHelper, CacheFileIOListener)
165 NS_IMPL_ADDREF(CacheFile)
166 NS_IMPL_RELEASE(CacheFile)
167 NS_INTERFACE_MAP_BEGIN(CacheFile)
168 NS_INTERFACE_MAP_ENTRY(mozilla::net::CacheFileChunkListener)
169 NS_INTERFACE_MAP_ENTRY(mozilla::net::CacheFileIOListener)
170 NS_INTERFACE_MAP_ENTRY(mozilla::net::CacheFileMetadataListener)
171 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports,
172 mozilla::net::CacheFileChunkListener)
173 NS_INTERFACE_MAP_END_THREADSAFE
175 CacheFile::CacheFile()
176 : mLock("CacheFile.mLock")
177 , mOpeningFile(false)
178 , mReady(false)
179 , mMemoryOnly(false)
180 , mOpenAsMemoryOnly(false)
181 , mDataAccessed(false)
182 , mDataIsDirty(false)
183 , mWritingMetadata(false)
184 , mStatus(NS_OK)
185 , mDataSize(-1)
186 , mOutput(nullptr)
187 {
188 LOG(("CacheFile::CacheFile() [this=%p]", this));
189 }
191 CacheFile::~CacheFile()
192 {
193 LOG(("CacheFile::~CacheFile() [this=%p]", this));
195 MutexAutoLock lock(mLock);
196 if (!mMemoryOnly && mReady) {
197 // mReady flag indicates we have metadata plus in a valid state.
198 WriteMetadataIfNeededLocked(true);
199 }
200 }
202 nsresult
203 CacheFile::Init(const nsACString &aKey,
204 bool aCreateNew,
205 bool aMemoryOnly,
206 bool aPriority,
207 CacheFileListener *aCallback)
208 {
209 MOZ_ASSERT(!mListener);
210 MOZ_ASSERT(!mHandle);
212 nsresult rv;
214 mKey = aKey;
215 mOpenAsMemoryOnly = mMemoryOnly = aMemoryOnly;
217 LOG(("CacheFile::Init() [this=%p, key=%s, createNew=%d, memoryOnly=%d, "
218 "listener=%p]", this, mKey.get(), aCreateNew, aMemoryOnly, aCallback));
220 if (mMemoryOnly) {
221 MOZ_ASSERT(!aCallback);
223 mMetadata = new CacheFileMetadata(mOpenAsMemoryOnly, mKey);
224 mReady = true;
225 mDataSize = mMetadata->Offset();
226 return NS_OK;
227 }
228 else {
229 uint32_t flags;
230 if (aCreateNew) {
231 MOZ_ASSERT(!aCallback);
232 flags = CacheFileIOManager::CREATE_NEW;
234 // make sure we can use this entry immediately
235 mMetadata = new CacheFileMetadata(mOpenAsMemoryOnly, mKey);
236 mReady = true;
237 mDataSize = mMetadata->Offset();
238 }
239 else {
240 flags = CacheFileIOManager::CREATE;
242 // Have a look into index and change to CREATE_NEW when we are sure
243 // that the entry does not exist.
244 CacheIndex::EntryStatus status;
245 rv = CacheIndex::HasEntry(mKey, &status);
246 if (status == CacheIndex::DOES_NOT_EXIST) {
247 LOG(("CacheFile::Init() - Forcing CREATE_NEW flag since we don't have"
248 " this entry according to index"));
249 flags = CacheFileIOManager::CREATE_NEW;
251 // make sure we can use this entry immediately
252 mMetadata = new CacheFileMetadata(mOpenAsMemoryOnly, mKey);
253 mReady = true;
254 mDataSize = mMetadata->Offset();
256 // Notify callback now and don't store it in mListener, no further
257 // operation can change the result.
258 nsRefPtr<NotifyCacheFileListenerEvent> ev;
259 ev = new NotifyCacheFileListenerEvent(aCallback, NS_OK, true);
260 rv = NS_DispatchToCurrentThread(ev);
261 NS_ENSURE_SUCCESS(rv, rv);
263 aCallback = nullptr;
264 }
265 }
267 if (aPriority)
268 flags |= CacheFileIOManager::PRIORITY;
270 mOpeningFile = true;
271 mListener = aCallback;
272 rv = CacheFileIOManager::OpenFile(mKey, flags, true, this);
273 if (NS_FAILED(rv)) {
274 mListener = nullptr;
275 mOpeningFile = false;
277 if (aCreateNew) {
278 NS_WARNING("Forcing memory-only entry since OpenFile failed");
279 LOG(("CacheFile::Init() - CacheFileIOManager::OpenFile() failed "
280 "synchronously. We can continue in memory-only mode since "
281 "aCreateNew == true. [this=%p]", this));
283 mMemoryOnly = true;
284 }
285 else if (rv == NS_ERROR_NOT_INITIALIZED) {
286 NS_WARNING("Forcing memory-only entry since CacheIOManager isn't "
287 "initialized.");
288 LOG(("CacheFile::Init() - CacheFileIOManager isn't initialized, "
289 "initializing entry as memory-only. [this=%p]", this));
291 mMemoryOnly = true;
292 mMetadata = new CacheFileMetadata(mOpenAsMemoryOnly, mKey);
293 mReady = true;
294 mDataSize = mMetadata->Offset();
296 nsRefPtr<NotifyCacheFileListenerEvent> ev;
297 ev = new NotifyCacheFileListenerEvent(aCallback, NS_OK, true);
298 rv = NS_DispatchToCurrentThread(ev);
299 NS_ENSURE_SUCCESS(rv, rv);
300 }
301 else {
302 NS_ENSURE_SUCCESS(rv, rv);
303 }
304 }
305 }
307 return NS_OK;
308 }
310 nsresult
311 CacheFile::OnChunkRead(nsresult aResult, CacheFileChunk *aChunk)
312 {
313 CacheFileAutoLock lock(this);
315 nsresult rv;
317 uint32_t index = aChunk->Index();
319 LOG(("CacheFile::OnChunkRead() [this=%p, rv=0x%08x, chunk=%p, idx=%d]",
320 this, aResult, aChunk, index));
322 if (NS_FAILED(aResult)) {
323 SetError(aResult);
324 CacheFileIOManager::DoomFile(mHandle, nullptr);
325 }
327 if (HaveChunkListeners(index)) {
328 rv = NotifyChunkListeners(index, aResult, aChunk);
329 NS_ENSURE_SUCCESS(rv, rv);
330 }
332 return NS_OK;
333 }
335 nsresult
336 CacheFile::OnChunkWritten(nsresult aResult, CacheFileChunk *aChunk)
337 {
338 CacheFileAutoLock lock(this);
340 nsresult rv;
342 LOG(("CacheFile::OnChunkWritten() [this=%p, rv=0x%08x, chunk=%p, idx=%d]",
343 this, aResult, aChunk, aChunk->Index()));
345 MOZ_ASSERT(!mMemoryOnly);
346 MOZ_ASSERT(!mOpeningFile);
347 MOZ_ASSERT(mHandle);
349 if (NS_FAILED(aResult)) {
350 SetError(aResult);
351 CacheFileIOManager::DoomFile(mHandle, nullptr);
352 }
354 if (NS_SUCCEEDED(aResult) && !aChunk->IsDirty()) {
355 // update hash value in metadata
356 mMetadata->SetHash(aChunk->Index(), aChunk->Hash());
357 }
359 // notify listeners if there is any
360 if (HaveChunkListeners(aChunk->Index())) {
361 // don't release the chunk since there are some listeners queued
362 rv = NotifyChunkListeners(aChunk->Index(), aResult, aChunk);
363 if (NS_SUCCEEDED(rv)) {
364 MOZ_ASSERT(aChunk->mRefCnt != 2);
365 return NS_OK;
366 }
367 }
369 if (aChunk->mRefCnt != 2) {
370 LOG(("CacheFile::OnChunkWritten() - Chunk is still used [this=%p, chunk=%p,"
371 " refcnt=%d]", this, aChunk, aChunk->mRefCnt.get()));
373 return NS_OK;
374 }
376 #ifdef CACHE_CHUNKS
377 if (NS_SUCCEEDED(aResult)) {
378 LOG(("CacheFile::OnChunkWritten() - Caching unused chunk [this=%p, "
379 "chunk=%p]", this, aChunk));
380 } else {
381 LOG(("CacheFile::OnChunkWritten() - Removing failed chunk [this=%p, "
382 "chunk=%p]", this, aChunk));
383 }
384 #else
385 LOG(("CacheFile::OnChunkWritten() - Releasing %s chunk [this=%p, chunk=%p]",
386 NS_SUCCEEDED(aResult) ? "unused" : "failed", this, aChunk));
387 #endif
389 RemoveChunkInternal(aChunk,
390 #ifdef CACHE_CHUNKS
391 NS_SUCCEEDED(aResult));
392 #else
393 false);
394 #endif
396 WriteMetadataIfNeededLocked();
398 return NS_OK;
399 }
401 nsresult
402 CacheFile::OnChunkAvailable(nsresult aResult, uint32_t aChunkIdx,
403 CacheFileChunk *aChunk)
404 {
405 MOZ_CRASH("CacheFile::OnChunkAvailable should not be called!");
406 return NS_ERROR_UNEXPECTED;
407 }
409 nsresult
410 CacheFile::OnChunkUpdated(CacheFileChunk *aChunk)
411 {
412 MOZ_CRASH("CacheFile::OnChunkUpdated should not be called!");
413 return NS_ERROR_UNEXPECTED;
414 }
416 nsresult
417 CacheFile::OnFileOpened(CacheFileHandle *aHandle, nsresult aResult)
418 {
419 nsresult rv;
421 // Using an 'auto' class to perform doom or fail the listener
422 // outside the CacheFile's lock.
423 class AutoFailDoomListener
424 {
425 public:
426 AutoFailDoomListener(CacheFileHandle *aHandle)
427 : mHandle(aHandle)
428 , mAlreadyDoomed(false)
429 {}
430 ~AutoFailDoomListener()
431 {
432 if (!mListener)
433 return;
435 if (mHandle) {
436 if (mAlreadyDoomed) {
437 mListener->OnFileDoomed(mHandle, NS_OK);
438 } else {
439 CacheFileIOManager::DoomFile(mHandle, mListener);
440 }
441 } else {
442 mListener->OnFileDoomed(nullptr, NS_ERROR_NOT_AVAILABLE);
443 }
444 }
446 CacheFileHandle* mHandle;
447 nsCOMPtr<CacheFileIOListener> mListener;
448 bool mAlreadyDoomed;
449 } autoDoom(aHandle);
451 nsCOMPtr<CacheFileListener> listener;
452 bool isNew = false;
453 nsresult retval = NS_OK;
455 {
456 CacheFileAutoLock lock(this);
458 MOZ_ASSERT(mOpeningFile);
459 MOZ_ASSERT((NS_SUCCEEDED(aResult) && aHandle) ||
460 (NS_FAILED(aResult) && !aHandle));
461 MOZ_ASSERT((mListener && !mMetadata) || // !createNew
462 (!mListener && mMetadata)); // createNew
463 MOZ_ASSERT(!mMemoryOnly || mMetadata); // memory-only was set on new entry
465 LOG(("CacheFile::OnFileOpened() [this=%p, rv=0x%08x, handle=%p]",
466 this, aResult, aHandle));
468 mOpeningFile = false;
470 autoDoom.mListener.swap(mDoomAfterOpenListener);
472 if (mMemoryOnly) {
473 // We can be here only in case the entry was initilized as createNew and
474 // SetMemoryOnly() was called.
476 // Just don't store the handle into mHandle and exit
477 autoDoom.mAlreadyDoomed = true;
478 return NS_OK;
479 }
480 else if (NS_FAILED(aResult)) {
481 if (mMetadata) {
482 // This entry was initialized as createNew, just switch to memory-only
483 // mode.
484 NS_WARNING("Forcing memory-only entry since OpenFile failed");
485 LOG(("CacheFile::OnFileOpened() - CacheFileIOManager::OpenFile() "
486 "failed asynchronously. We can continue in memory-only mode since "
487 "aCreateNew == true. [this=%p]", this));
489 mMemoryOnly = true;
490 return NS_OK;
491 }
492 else if (aResult == NS_ERROR_FILE_INVALID_PATH) {
493 // CacheFileIOManager doesn't have mCacheDirectory, switch to
494 // memory-only mode.
495 NS_WARNING("Forcing memory-only entry since CacheFileIOManager doesn't "
496 "have mCacheDirectory.");
497 LOG(("CacheFile::OnFileOpened() - CacheFileIOManager doesn't have "
498 "mCacheDirectory, initializing entry as memory-only. [this=%p]",
499 this));
501 mMemoryOnly = true;
502 mMetadata = new CacheFileMetadata(mOpenAsMemoryOnly, mKey);
503 mReady = true;
504 mDataSize = mMetadata->Offset();
506 isNew = true;
507 retval = NS_OK;
508 }
509 else {
510 // CacheFileIOManager::OpenFile() failed for another reason.
511 isNew = false;
512 retval = aResult;
513 }
515 mListener.swap(listener);
516 }
517 else {
518 mHandle = aHandle;
520 if (mMetadata) {
521 InitIndexEntry();
523 // The entry was initialized as createNew, don't try to read metadata.
524 mMetadata->SetHandle(mHandle);
526 // Write all cached chunks, otherwise they may stay unwritten.
527 mCachedChunks.Enumerate(&CacheFile::WriteAllCachedChunks, this);
529 return NS_OK;
530 }
531 }
532 }
534 if (listener) {
535 listener->OnFileReady(retval, isNew);
536 return NS_OK;
537 }
539 MOZ_ASSERT(NS_SUCCEEDED(aResult));
540 MOZ_ASSERT(!mMetadata);
541 MOZ_ASSERT(mListener);
543 mMetadata = new CacheFileMetadata(mHandle, mKey);
545 rv = mMetadata->ReadMetadata(this);
546 if (NS_FAILED(rv)) {
547 mListener.swap(listener);
548 listener->OnFileReady(rv, false);
549 }
551 return NS_OK;
552 }
554 nsresult
555 CacheFile::OnDataWritten(CacheFileHandle *aHandle, const char *aBuf,
556 nsresult aResult)
557 {
558 MOZ_CRASH("CacheFile::OnDataWritten should not be called!");
559 return NS_ERROR_UNEXPECTED;
560 }
562 nsresult
563 CacheFile::OnDataRead(CacheFileHandle *aHandle, char *aBuf, nsresult aResult)
564 {
565 MOZ_CRASH("CacheFile::OnDataRead should not be called!");
566 return NS_ERROR_UNEXPECTED;
567 }
569 nsresult
570 CacheFile::OnMetadataRead(nsresult aResult)
571 {
572 MOZ_ASSERT(mListener);
574 LOG(("CacheFile::OnMetadataRead() [this=%p, rv=0x%08x]", this, aResult));
576 bool isNew = false;
577 if (NS_SUCCEEDED(aResult)) {
578 mReady = true;
579 mDataSize = mMetadata->Offset();
580 if (mDataSize == 0 && mMetadata->ElementsSize() == 0) {
581 isNew = true;
582 mMetadata->MarkDirty();
583 }
585 InitIndexEntry();
586 }
588 nsCOMPtr<CacheFileListener> listener;
589 mListener.swap(listener);
590 listener->OnFileReady(aResult, isNew);
591 return NS_OK;
592 }
594 nsresult
595 CacheFile::OnMetadataWritten(nsresult aResult)
596 {
597 CacheFileAutoLock lock(this);
599 LOG(("CacheFile::OnMetadataWritten() [this=%p, rv=0x%08x]", this, aResult));
601 MOZ_ASSERT(mWritingMetadata);
602 mWritingMetadata = false;
604 MOZ_ASSERT(!mMemoryOnly);
605 MOZ_ASSERT(!mOpeningFile);
607 if (NS_FAILED(aResult)) {
608 // TODO close streams with an error ???
609 }
611 if (mOutput || mInputs.Length() || mChunks.Count())
612 return NS_OK;
614 if (IsDirty())
615 WriteMetadataIfNeededLocked();
617 if (!mWritingMetadata) {
618 LOG(("CacheFile::OnMetadataWritten() - Releasing file handle [this=%p]",
619 this));
620 CacheFileIOManager::ReleaseNSPRHandle(mHandle);
621 }
623 return NS_OK;
624 }
626 nsresult
627 CacheFile::OnFileDoomed(CacheFileHandle *aHandle, nsresult aResult)
628 {
629 nsCOMPtr<CacheFileListener> listener;
631 {
632 CacheFileAutoLock lock(this);
634 MOZ_ASSERT(mListener);
636 LOG(("CacheFile::OnFileDoomed() [this=%p, rv=0x%08x, handle=%p]",
637 this, aResult, aHandle));
639 mListener.swap(listener);
640 }
642 listener->OnFileDoomed(aResult);
643 return NS_OK;
644 }
646 nsresult
647 CacheFile::OnEOFSet(CacheFileHandle *aHandle, nsresult aResult)
648 {
649 MOZ_CRASH("CacheFile::OnEOFSet should not be called!");
650 return NS_ERROR_UNEXPECTED;
651 }
653 nsresult
654 CacheFile::OnFileRenamed(CacheFileHandle *aHandle, nsresult aResult)
655 {
656 MOZ_CRASH("CacheFile::OnFileRenamed should not be called!");
657 return NS_ERROR_UNEXPECTED;
658 }
660 nsresult
661 CacheFile::OpenInputStream(nsIInputStream **_retval)
662 {
663 CacheFileAutoLock lock(this);
665 MOZ_ASSERT(mHandle || mMemoryOnly || mOpeningFile);
667 if (!mReady) {
668 LOG(("CacheFile::OpenInputStream() - CacheFile is not ready [this=%p]",
669 this));
671 return NS_ERROR_NOT_AVAILABLE;
672 }
674 CacheFileInputStream *input = new CacheFileInputStream(this);
676 LOG(("CacheFile::OpenInputStream() - Creating new input stream %p [this=%p]",
677 input, this));
679 mInputs.AppendElement(input);
680 NS_ADDREF(input);
682 mDataAccessed = true;
683 NS_ADDREF(*_retval = input);
684 return NS_OK;
685 }
687 nsresult
688 CacheFile::OpenOutputStream(CacheOutputCloseListener *aCloseListener, nsIOutputStream **_retval)
689 {
690 CacheFileAutoLock lock(this);
692 MOZ_ASSERT(mHandle || mMemoryOnly || mOpeningFile);
694 if (!mReady) {
695 LOG(("CacheFile::OpenOutputStream() - CacheFile is not ready [this=%p]",
696 this));
698 return NS_ERROR_NOT_AVAILABLE;
699 }
701 if (mOutput) {
702 LOG(("CacheFile::OpenOutputStream() - We already have output stream %p "
703 "[this=%p]", mOutput, this));
705 return NS_ERROR_NOT_AVAILABLE;
706 }
708 mOutput = new CacheFileOutputStream(this, aCloseListener);
710 LOG(("CacheFile::OpenOutputStream() - Creating new output stream %p "
711 "[this=%p]", mOutput, this));
713 mDataAccessed = true;
714 NS_ADDREF(*_retval = mOutput);
715 return NS_OK;
716 }
718 nsresult
719 CacheFile::SetMemoryOnly()
720 {
721 LOG(("CacheFile::SetMemoryOnly() mMemoryOnly=%d [this=%p]",
722 mMemoryOnly, this));
724 if (mMemoryOnly)
725 return NS_OK;
727 MOZ_ASSERT(mReady);
729 if (!mReady) {
730 LOG(("CacheFile::SetMemoryOnly() - CacheFile is not ready [this=%p]",
731 this));
733 return NS_ERROR_NOT_AVAILABLE;
734 }
736 if (mDataAccessed) {
737 LOG(("CacheFile::SetMemoryOnly() - Data was already accessed [this=%p]", this));
738 return NS_ERROR_NOT_AVAILABLE;
739 }
741 // TODO what to do when this isn't a new entry and has an existing metadata???
742 mMemoryOnly = true;
743 return NS_OK;
744 }
746 nsresult
747 CacheFile::Doom(CacheFileListener *aCallback)
748 {
749 CacheFileAutoLock lock(this);
751 MOZ_ASSERT(mHandle || mMemoryOnly || mOpeningFile);
753 LOG(("CacheFile::Doom() [this=%p, listener=%p]", this, aCallback));
755 nsresult rv = NS_OK;
757 if (mMemoryOnly) {
758 return NS_ERROR_FILE_NOT_FOUND;
759 }
761 if (mHandle && mHandle->IsDoomed()) {
762 return NS_ERROR_FILE_NOT_FOUND;
763 }
765 nsCOMPtr<CacheFileIOListener> listener;
766 if (aCallback || !mHandle) {
767 listener = new DoomFileHelper(aCallback);
768 }
769 if (mHandle) {
770 rv = CacheFileIOManager::DoomFile(mHandle, listener);
771 } else if (mOpeningFile) {
772 mDoomAfterOpenListener = listener;
773 }
775 return rv;
776 }
778 nsresult
779 CacheFile::ThrowMemoryCachedData()
780 {
781 CacheFileAutoLock lock(this);
783 LOG(("CacheFile::ThrowMemoryCachedData() [this=%p]", this));
785 if (mMemoryOnly) {
786 // This method should not be called when the CacheFile was initialized as
787 // memory-only, but it can be called when CacheFile end up as memory-only
788 // due to e.g. IO failure since CacheEntry doesn't know it.
789 LOG(("CacheFile::ThrowMemoryCachedData() - Ignoring request because the "
790 "entry is memory-only. [this=%p]", this));
792 return NS_ERROR_NOT_AVAILABLE;
793 }
795 if (mOpeningFile) {
796 // mayhemer, note: we shouldn't get here, since CacheEntry prevents loading
797 // entries from being purged.
799 LOG(("CacheFile::ThrowMemoryCachedData() - Ignoring request because the "
800 "entry is still opening the file [this=%p]", this));
802 return NS_ERROR_ABORT;
803 }
805 #ifdef CACHE_CHUNKS
806 mCachedChunks.Clear();
807 #else
808 // If we don't cache all chunks, mCachedChunks must be empty.
809 MOZ_ASSERT(mCachedChunks.Count() == 0);
810 #endif
812 return NS_OK;
813 }
815 nsresult
816 CacheFile::GetElement(const char *aKey, char **_retval)
817 {
818 CacheFileAutoLock lock(this);
819 MOZ_ASSERT(mMetadata);
820 NS_ENSURE_TRUE(mMetadata, NS_ERROR_UNEXPECTED);
822 const char *value;
823 value = mMetadata->GetElement(aKey);
824 if (!value)
825 return NS_ERROR_NOT_AVAILABLE;
827 *_retval = NS_strdup(value);
828 return NS_OK;
829 }
831 nsresult
832 CacheFile::SetElement(const char *aKey, const char *aValue)
833 {
834 CacheFileAutoLock lock(this);
835 MOZ_ASSERT(mMetadata);
836 NS_ENSURE_TRUE(mMetadata, NS_ERROR_UNEXPECTED);
838 PostWriteTimer();
839 return mMetadata->SetElement(aKey, aValue);
840 }
842 nsresult
843 CacheFile::ElementsSize(uint32_t *_retval)
844 {
845 CacheFileAutoLock lock(this);
847 if (!mMetadata)
848 return NS_ERROR_NOT_AVAILABLE;
850 *_retval = mMetadata->ElementsSize();
851 return NS_OK;
852 }
854 nsresult
855 CacheFile::SetExpirationTime(uint32_t aExpirationTime)
856 {
857 CacheFileAutoLock lock(this);
858 MOZ_ASSERT(mMetadata);
859 NS_ENSURE_TRUE(mMetadata, NS_ERROR_UNEXPECTED);
861 PostWriteTimer();
863 if (mHandle && !mHandle->IsDoomed())
864 CacheFileIOManager::UpdateIndexEntry(mHandle, nullptr, &aExpirationTime);
866 return mMetadata->SetExpirationTime(aExpirationTime);
867 }
869 nsresult
870 CacheFile::GetExpirationTime(uint32_t *_retval)
871 {
872 CacheFileAutoLock lock(this);
873 MOZ_ASSERT(mMetadata);
874 NS_ENSURE_TRUE(mMetadata, NS_ERROR_UNEXPECTED);
876 return mMetadata->GetExpirationTime(_retval);
877 }
879 nsresult
880 CacheFile::SetLastModified(uint32_t aLastModified)
881 {
882 CacheFileAutoLock lock(this);
883 MOZ_ASSERT(mMetadata);
884 NS_ENSURE_TRUE(mMetadata, NS_ERROR_UNEXPECTED);
886 PostWriteTimer();
887 return mMetadata->SetLastModified(aLastModified);
888 }
890 nsresult
891 CacheFile::GetLastModified(uint32_t *_retval)
892 {
893 CacheFileAutoLock lock(this);
894 MOZ_ASSERT(mMetadata);
895 NS_ENSURE_TRUE(mMetadata, NS_ERROR_UNEXPECTED);
897 return mMetadata->GetLastModified(_retval);
898 }
900 nsresult
901 CacheFile::SetFrecency(uint32_t aFrecency)
902 {
903 CacheFileAutoLock lock(this);
904 MOZ_ASSERT(mMetadata);
905 NS_ENSURE_TRUE(mMetadata, NS_ERROR_UNEXPECTED);
907 PostWriteTimer();
909 if (mHandle && !mHandle->IsDoomed())
910 CacheFileIOManager::UpdateIndexEntry(mHandle, &aFrecency, nullptr);
912 return mMetadata->SetFrecency(aFrecency);
913 }
915 nsresult
916 CacheFile::GetFrecency(uint32_t *_retval)
917 {
918 CacheFileAutoLock lock(this);
919 MOZ_ASSERT(mMetadata);
920 NS_ENSURE_TRUE(mMetadata, NS_ERROR_UNEXPECTED);
922 return mMetadata->GetFrecency(_retval);
923 }
925 nsresult
926 CacheFile::GetLastFetched(uint32_t *_retval)
927 {
928 CacheFileAutoLock lock(this);
929 MOZ_ASSERT(mMetadata);
930 NS_ENSURE_TRUE(mMetadata, NS_ERROR_UNEXPECTED);
932 return mMetadata->GetLastFetched(_retval);
933 }
935 nsresult
936 CacheFile::GetFetchCount(uint32_t *_retval)
937 {
938 CacheFileAutoLock lock(this);
939 MOZ_ASSERT(mMetadata);
940 NS_ENSURE_TRUE(mMetadata, NS_ERROR_UNEXPECTED);
942 return mMetadata->GetFetchCount(_retval);
943 }
945 void
946 CacheFile::Lock()
947 {
948 mLock.Lock();
949 }
951 void
952 CacheFile::Unlock()
953 {
954 nsTArray<nsISupports*> objs;
955 objs.SwapElements(mObjsToRelease);
957 mLock.Unlock();
959 for (uint32_t i = 0; i < objs.Length(); i++)
960 objs[i]->Release();
961 }
963 void
964 CacheFile::AssertOwnsLock() const
965 {
966 mLock.AssertCurrentThreadOwns();
967 }
969 void
970 CacheFile::ReleaseOutsideLock(nsISupports *aObject)
971 {
972 AssertOwnsLock();
974 mObjsToRelease.AppendElement(aObject);
975 }
977 nsresult
978 CacheFile::GetChunk(uint32_t aIndex, bool aWriter,
979 CacheFileChunkListener *aCallback, CacheFileChunk **_retval)
980 {
981 CacheFileAutoLock lock(this);
982 return GetChunkLocked(aIndex, aWriter, aCallback, _retval);
983 }
985 nsresult
986 CacheFile::GetChunkLocked(uint32_t aIndex, bool aWriter,
987 CacheFileChunkListener *aCallback,
988 CacheFileChunk **_retval)
989 {
990 AssertOwnsLock();
992 LOG(("CacheFile::GetChunkLocked() [this=%p, idx=%d, writer=%d, listener=%p]",
993 this, aIndex, aWriter, aCallback));
995 MOZ_ASSERT(mReady);
996 MOZ_ASSERT(mHandle || mMemoryOnly || mOpeningFile);
997 MOZ_ASSERT((aWriter && !aCallback) || (!aWriter && aCallback));
999 nsresult rv;
1001 nsRefPtr<CacheFileChunk> chunk;
1002 if (mChunks.Get(aIndex, getter_AddRefs(chunk))) {
1003 LOG(("CacheFile::GetChunkLocked() - Found chunk %p in mChunks [this=%p]",
1004 chunk.get(), this));
1006 // We might get failed chunk between releasing the lock in
1007 // CacheFileChunk::OnDataWritten/Read and CacheFile::OnChunkWritten/Read
1008 rv = chunk->GetStatus();
1009 if (NS_FAILED(rv)) {
1010 SetError(rv);
1011 LOG(("CacheFile::GetChunkLocked() - Found failed chunk in mChunks "
1012 "[this=%p]", this));
1013 return rv;
1014 }
1016 if (chunk->IsReady() || aWriter) {
1017 chunk.swap(*_retval);
1018 }
1019 else {
1020 rv = QueueChunkListener(aIndex, aCallback);
1021 NS_ENSURE_SUCCESS(rv, rv);
1022 }
1024 return NS_OK;
1025 }
1027 if (mCachedChunks.Get(aIndex, getter_AddRefs(chunk))) {
1028 #ifndef CACHE_CHUNKS
1029 // We don't cache all chunks, so we must not have handle and we must be
1030 // either waiting for the handle, or this is memory-only entry.
1031 MOZ_ASSERT(!mHandle && (mMemoryOnly || mOpeningFile));
1032 #endif
1033 LOG(("CacheFile::GetChunkLocked() - Reusing cached chunk %p [this=%p]",
1034 chunk.get(), this));
1036 mChunks.Put(aIndex, chunk);
1037 mCachedChunks.Remove(aIndex);
1038 chunk->mFile = this;
1039 chunk->mRemovingChunk = false;
1041 MOZ_ASSERT(chunk->IsReady());
1043 chunk.swap(*_retval);
1044 return NS_OK;
1045 }
1047 int64_t off = aIndex * kChunkSize;
1049 if (off < mDataSize) {
1050 // We cannot be here if this is memory only entry since the chunk must exist
1051 MOZ_ASSERT(!mMemoryOnly);
1052 if (mMemoryOnly) {
1053 // If this ever really happen it is better to fail rather than crashing on
1054 // a null handle.
1055 LOG(("CacheFile::GetChunkLocked() - Unexpected state! Offset < mDataSize "
1056 "for memory-only entry. [this=%p, off=%lld, mDataSize=%lld]",
1057 this, off, mDataSize));
1059 return NS_ERROR_UNEXPECTED;
1060 }
1062 chunk = new CacheFileChunk(this, aIndex);
1063 mChunks.Put(aIndex, chunk);
1065 LOG(("CacheFile::GetChunkLocked() - Reading newly created chunk %p from "
1066 "the disk [this=%p]", chunk.get(), this));
1068 // Read the chunk from the disk
1069 rv = chunk->Read(mHandle, std::min(static_cast<uint32_t>(mDataSize - off),
1070 static_cast<uint32_t>(kChunkSize)),
1071 mMetadata->GetHash(aIndex), this);
1072 if (NS_WARN_IF(NS_FAILED(rv))) {
1073 RemoveChunkInternal(chunk, false);
1074 return rv;
1075 }
1077 if (aWriter) {
1078 chunk.swap(*_retval);
1079 }
1080 else {
1081 rv = QueueChunkListener(aIndex, aCallback);
1082 NS_ENSURE_SUCCESS(rv, rv);
1083 }
1085 return NS_OK;
1086 }
1087 else if (off == mDataSize) {
1088 if (aWriter) {
1089 // this listener is going to write to the chunk
1090 chunk = new CacheFileChunk(this, aIndex);
1091 mChunks.Put(aIndex, chunk);
1093 LOG(("CacheFile::GetChunkLocked() - Created new empty chunk %p [this=%p]",
1094 chunk.get(), this));
1096 chunk->InitNew(this);
1097 mMetadata->SetHash(aIndex, chunk->Hash());
1099 if (HaveChunkListeners(aIndex)) {
1100 rv = NotifyChunkListeners(aIndex, NS_OK, chunk);
1101 NS_ENSURE_SUCCESS(rv, rv);
1102 }
1104 chunk.swap(*_retval);
1105 return NS_OK;
1106 }
1107 }
1108 else {
1109 if (aWriter) {
1110 // this chunk was requested by writer, but we need to fill the gap first
1112 // Fill with zero the last chunk if it is incomplete
1113 if (mDataSize % kChunkSize) {
1114 rv = PadChunkWithZeroes(mDataSize / kChunkSize);
1115 NS_ENSURE_SUCCESS(rv, rv);
1117 MOZ_ASSERT(!(mDataSize % kChunkSize));
1118 }
1120 uint32_t startChunk = mDataSize / kChunkSize;
1122 if (mMemoryOnly) {
1123 // We need to create all missing CacheFileChunks if this is memory-only
1124 // entry
1125 for (uint32_t i = startChunk ; i < aIndex ; i++) {
1126 rv = PadChunkWithZeroes(i);
1127 NS_ENSURE_SUCCESS(rv, rv);
1128 }
1129 }
1130 else {
1131 // We don't need to create CacheFileChunk for other empty chunks unless
1132 // there is some input stream waiting for this chunk.
1134 if (startChunk != aIndex) {
1135 // Make sure the file contains zeroes at the end of the file
1136 rv = CacheFileIOManager::TruncateSeekSetEOF(mHandle,
1137 startChunk * kChunkSize,
1138 aIndex * kChunkSize,
1139 nullptr);
1140 NS_ENSURE_SUCCESS(rv, rv);
1141 }
1143 for (uint32_t i = startChunk ; i < aIndex ; i++) {
1144 if (HaveChunkListeners(i)) {
1145 rv = PadChunkWithZeroes(i);
1146 NS_ENSURE_SUCCESS(rv, rv);
1147 }
1148 else {
1149 mMetadata->SetHash(i, kEmptyChunkHash);
1150 mDataSize = (i + 1) * kChunkSize;
1151 }
1152 }
1153 }
1155 MOZ_ASSERT(mDataSize == off);
1156 rv = GetChunkLocked(aIndex, true, nullptr, getter_AddRefs(chunk));
1157 NS_ENSURE_SUCCESS(rv, rv);
1159 chunk.swap(*_retval);
1160 return NS_OK;
1161 }
1162 }
1164 if (mOutput) {
1165 // the chunk doesn't exist but mOutput may create it
1166 rv = QueueChunkListener(aIndex, aCallback);
1167 NS_ENSURE_SUCCESS(rv, rv);
1168 }
1169 else {
1170 return NS_ERROR_NOT_AVAILABLE;
1171 }
1173 return NS_OK;
1174 }
1176 nsresult
1177 CacheFile::RemoveChunk(CacheFileChunk *aChunk)
1178 {
1179 nsresult rv;
1181 // Avoid lock reentrancy by increasing the RefCnt
1182 nsRefPtr<CacheFileChunk> chunk = aChunk;
1184 {
1185 CacheFileAutoLock lock(this);
1187 LOG(("CacheFile::RemoveChunk() [this=%p, chunk=%p, idx=%d]",
1188 this, aChunk, aChunk->Index()));
1190 MOZ_ASSERT(mReady);
1191 MOZ_ASSERT((mHandle && !mMemoryOnly && !mOpeningFile) ||
1192 (!mHandle && mMemoryOnly && !mOpeningFile) ||
1193 (!mHandle && !mMemoryOnly && mOpeningFile));
1195 if (aChunk->mRefCnt != 2) {
1196 LOG(("CacheFile::RemoveChunk() - Chunk is still used [this=%p, chunk=%p, "
1197 "refcnt=%d]", this, aChunk, aChunk->mRefCnt.get()));
1199 // somebody got the reference before the lock was acquired
1200 return NS_OK;
1201 }
1203 #ifdef DEBUG
1204 {
1205 // We can be here iff the chunk is in the hash table
1206 nsRefPtr<CacheFileChunk> chunkCheck;
1207 mChunks.Get(chunk->Index(), getter_AddRefs(chunkCheck));
1208 MOZ_ASSERT(chunkCheck == chunk);
1210 // We also shouldn't have any queued listener for this chunk
1211 ChunkListeners *listeners;
1212 mChunkListeners.Get(chunk->Index(), &listeners);
1213 MOZ_ASSERT(!listeners);
1214 }
1215 #endif
1217 if (NS_FAILED(mStatus)) {
1218 // Don't write any chunk to disk since this entry will be doomed
1219 LOG(("CacheFile::RemoveChunk() - Removing chunk because of status "
1220 "[this=%p, chunk=%p, mStatus=0x%08x]", this, chunk.get(), mStatus));
1222 RemoveChunkInternal(chunk, false);
1223 return mStatus;
1224 }
1226 if (chunk->IsDirty() && !mMemoryOnly && !mOpeningFile) {
1227 LOG(("CacheFile::RemoveChunk() - Writing dirty chunk to the disk "
1228 "[this=%p]", this));
1230 mDataIsDirty = true;
1232 rv = chunk->Write(mHandle, this);
1233 if (NS_FAILED(rv)) {
1234 LOG(("CacheFile::RemoveChunk() - CacheFileChunk::Write() failed "
1235 "synchronously. Removing it. [this=%p, chunk=%p, rv=0x%08x]",
1236 this, chunk.get(), rv));
1238 RemoveChunkInternal(chunk, false);
1240 SetError(rv);
1241 CacheFileIOManager::DoomFile(mHandle, nullptr);
1242 return rv;
1243 }
1244 else {
1245 // Chunk will be removed in OnChunkWritten if it is still unused
1247 // chunk needs to be released under the lock to be able to rely on
1248 // CacheFileChunk::mRefCnt in CacheFile::OnChunkWritten()
1249 chunk = nullptr;
1250 return NS_OK;
1251 }
1252 }
1254 #ifdef CACHE_CHUNKS
1255 LOG(("CacheFile::RemoveChunk() - Caching unused chunk [this=%p, chunk=%p]",
1256 this, chunk.get()));
1257 #else
1258 if (mMemoryOnly || mOpeningFile) {
1259 LOG(("CacheFile::RemoveChunk() - Caching unused chunk [this=%p, chunk=%p,"
1260 " reason=%s]", this, chunk.get(),
1261 mMemoryOnly ? "memory-only" : "opening-file"));
1262 } else {
1263 LOG(("CacheFile::RemoveChunk() - Releasing unused chunk [this=%p, "
1264 "chunk=%p]", this, chunk.get()));
1265 }
1266 #endif
1268 RemoveChunkInternal(chunk,
1269 #ifdef CACHE_CHUNKS
1270 true);
1271 #else
1272 // Cache the chunk only when we have a reason to do so
1273 mMemoryOnly || mOpeningFile);
1274 #endif
1276 if (!mMemoryOnly)
1277 WriteMetadataIfNeededLocked();
1278 }
1280 return NS_OK;
1281 }
1283 void
1284 CacheFile::RemoveChunkInternal(CacheFileChunk *aChunk, bool aCacheChunk)
1285 {
1286 aChunk->mRemovingChunk = true;
1287 ReleaseOutsideLock(static_cast<CacheFileChunkListener *>(
1288 aChunk->mFile.forget().take()));
1290 if (aCacheChunk) {
1291 mCachedChunks.Put(aChunk->Index(), aChunk);
1292 }
1294 mChunks.Remove(aChunk->Index());
1295 }
1297 nsresult
1298 CacheFile::RemoveInput(CacheFileInputStream *aInput)
1299 {
1300 CacheFileAutoLock lock(this);
1302 LOG(("CacheFile::RemoveInput() [this=%p, input=%p]", this, aInput));
1304 DebugOnly<bool> found;
1305 found = mInputs.RemoveElement(aInput);
1306 MOZ_ASSERT(found);
1308 ReleaseOutsideLock(static_cast<nsIInputStream*>(aInput));
1310 if (!mMemoryOnly)
1311 WriteMetadataIfNeededLocked();
1313 return NS_OK;
1314 }
1316 nsresult
1317 CacheFile::RemoveOutput(CacheFileOutputStream *aOutput)
1318 {
1319 AssertOwnsLock();
1321 LOG(("CacheFile::RemoveOutput() [this=%p, output=%p]", this, aOutput));
1323 if (mOutput != aOutput) {
1324 LOG(("CacheFile::RemoveOutput() - This output was already removed, ignoring"
1325 " call [this=%p]", this));
1326 return NS_OK;
1327 }
1329 mOutput = nullptr;
1331 // Cancel all queued chunk and update listeners that cannot be satisfied
1332 NotifyListenersAboutOutputRemoval();
1334 if (!mMemoryOnly)
1335 WriteMetadataIfNeededLocked();
1337 // Notify close listener as the last action
1338 aOutput->NotifyCloseListener();
1340 return NS_OK;
1341 }
1343 nsresult
1344 CacheFile::NotifyChunkListener(CacheFileChunkListener *aCallback,
1345 nsIEventTarget *aTarget,
1346 nsresult aResult,
1347 uint32_t aChunkIdx,
1348 CacheFileChunk *aChunk)
1349 {
1350 LOG(("CacheFile::NotifyChunkListener() [this=%p, listener=%p, target=%p, "
1351 "rv=0x%08x, idx=%d, chunk=%p]", this, aCallback, aTarget, aResult,
1352 aChunkIdx, aChunk));
1354 nsresult rv;
1355 nsRefPtr<NotifyChunkListenerEvent> ev;
1356 ev = new NotifyChunkListenerEvent(aCallback, aResult, aChunkIdx, aChunk);
1357 if (aTarget)
1358 rv = aTarget->Dispatch(ev, NS_DISPATCH_NORMAL);
1359 else
1360 rv = NS_DispatchToCurrentThread(ev);
1361 NS_ENSURE_SUCCESS(rv, rv);
1363 return NS_OK;
1364 }
1366 nsresult
1367 CacheFile::QueueChunkListener(uint32_t aIndex,
1368 CacheFileChunkListener *aCallback)
1369 {
1370 LOG(("CacheFile::QueueChunkListener() [this=%p, idx=%d, listener=%p]",
1371 this, aIndex, aCallback));
1373 AssertOwnsLock();
1375 MOZ_ASSERT(aCallback);
1377 ChunkListenerItem *item = new ChunkListenerItem();
1378 item->mTarget = NS_GetCurrentThread();
1379 item->mCallback = aCallback;
1381 ChunkListeners *listeners;
1382 if (!mChunkListeners.Get(aIndex, &listeners)) {
1383 listeners = new ChunkListeners();
1384 mChunkListeners.Put(aIndex, listeners);
1385 }
1387 listeners->mItems.AppendElement(item);
1388 return NS_OK;
1389 }
1391 nsresult
1392 CacheFile::NotifyChunkListeners(uint32_t aIndex, nsresult aResult,
1393 CacheFileChunk *aChunk)
1394 {
1395 LOG(("CacheFile::NotifyChunkListeners() [this=%p, idx=%d, rv=0x%08x, "
1396 "chunk=%p]", this, aIndex, aResult, aChunk));
1398 AssertOwnsLock();
1400 nsresult rv, rv2;
1402 ChunkListeners *listeners;
1403 mChunkListeners.Get(aIndex, &listeners);
1404 MOZ_ASSERT(listeners);
1406 rv = NS_OK;
1407 for (uint32_t i = 0 ; i < listeners->mItems.Length() ; i++) {
1408 ChunkListenerItem *item = listeners->mItems[i];
1409 rv2 = NotifyChunkListener(item->mCallback, item->mTarget, aResult, aIndex,
1410 aChunk);
1411 if (NS_FAILED(rv2) && NS_SUCCEEDED(rv))
1412 rv = rv2;
1413 delete item;
1414 }
1416 mChunkListeners.Remove(aIndex);
1418 return rv;
1419 }
1421 bool
1422 CacheFile::HaveChunkListeners(uint32_t aIndex)
1423 {
1424 ChunkListeners *listeners;
1425 mChunkListeners.Get(aIndex, &listeners);
1426 return !!listeners;
1427 }
1429 void
1430 CacheFile::NotifyListenersAboutOutputRemoval()
1431 {
1432 LOG(("CacheFile::NotifyListenersAboutOutputRemoval() [this=%p]", this));
1434 AssertOwnsLock();
1436 // First fail all chunk listeners that wait for non-existent chunk
1437 mChunkListeners.Enumerate(&CacheFile::FailListenersIfNonExistentChunk,
1438 this);
1440 // Fail all update listeners
1441 mChunks.Enumerate(&CacheFile::FailUpdateListeners, this);
1442 }
1444 bool
1445 CacheFile::DataSize(int64_t* aSize)
1446 {
1447 CacheFileAutoLock lock(this);
1449 if (mOutput)
1450 return false;
1452 *aSize = mDataSize;
1453 return true;
1454 }
1456 bool
1457 CacheFile::IsDoomed()
1458 {
1459 CacheFileAutoLock lock(this);
1461 if (!mHandle)
1462 return false;
1464 return mHandle->IsDoomed();
1465 }
1467 bool
1468 CacheFile::IsWriteInProgress()
1469 {
1470 // Returns true when there is a potentially unfinished write operation.
1471 // Not using lock for performance reasons. mMetadata is never released
1472 // during life time of CacheFile.
1473 return
1474 mDataIsDirty ||
1475 (mMetadata && mMetadata->IsDirty()) ||
1476 mWritingMetadata ||
1477 mOpeningFile ||
1478 mOutput ||
1479 mChunks.Count();
1480 }
1482 bool
1483 CacheFile::IsDirty()
1484 {
1485 return mDataIsDirty || mMetadata->IsDirty();
1486 }
1488 void
1489 CacheFile::WriteMetadataIfNeeded()
1490 {
1491 LOG(("CacheFile::WriteMetadataIfNeeded() [this=%p]", this));
1493 CacheFileAutoLock lock(this);
1495 if (!mMemoryOnly)
1496 WriteMetadataIfNeededLocked();
1497 }
1499 void
1500 CacheFile::WriteMetadataIfNeededLocked(bool aFireAndForget)
1501 {
1502 // When aFireAndForget is set to true, we are called from dtor.
1503 // |this| must not be referenced after this method returns!
1505 LOG(("CacheFile::WriteMetadataIfNeededLocked() [this=%p]", this));
1507 nsresult rv;
1509 AssertOwnsLock();
1510 MOZ_ASSERT(!mMemoryOnly);
1512 if (!mMetadata) {
1513 MOZ_CRASH("Must have metadata here");
1514 return;
1515 }
1517 if (!aFireAndForget) {
1518 // if aFireAndForget is set, we are called from dtor. Write
1519 // scheduler hard-refers CacheFile otherwise, so we cannot be here.
1520 CacheFileIOManager::UnscheduleMetadataWrite(this);
1521 }
1523 if (NS_FAILED(mStatus))
1524 return;
1526 if (!IsDirty() || mOutput || mInputs.Length() || mChunks.Count() ||
1527 mWritingMetadata || mOpeningFile)
1528 return;
1530 LOG(("CacheFile::WriteMetadataIfNeededLocked() - Writing metadata [this=%p]",
1531 this));
1533 rv = mMetadata->WriteMetadata(mDataSize, aFireAndForget ? nullptr : this);
1534 if (NS_SUCCEEDED(rv)) {
1535 mWritingMetadata = true;
1536 mDataIsDirty = false;
1537 } else {
1538 LOG(("CacheFile::WriteMetadataIfNeededLocked() - Writing synchronously "
1539 "failed [this=%p]", this));
1540 // TODO: close streams with error
1541 SetError(rv);
1542 }
1543 }
1545 void
1546 CacheFile::PostWriteTimer()
1547 {
1548 LOG(("CacheFile::PostWriteTimer() [this=%p]", this));
1550 CacheFileIOManager::ScheduleMetadataWrite(this);
1551 }
1553 PLDHashOperator
1554 CacheFile::WriteAllCachedChunks(const uint32_t& aIdx,
1555 nsRefPtr<CacheFileChunk>& aChunk,
1556 void* aClosure)
1557 {
1558 CacheFile *file = static_cast<CacheFile*>(aClosure);
1560 LOG(("CacheFile::WriteAllCachedChunks() [this=%p, idx=%d, chunk=%p]",
1561 file, aIdx, aChunk.get()));
1563 file->mChunks.Put(aIdx, aChunk);
1564 aChunk->mFile = file;
1565 aChunk->mRemovingChunk = false;
1567 MOZ_ASSERT(aChunk->IsReady());
1569 NS_ADDREF(aChunk);
1570 file->ReleaseOutsideLock(aChunk);
1572 return PL_DHASH_REMOVE;
1573 }
1575 PLDHashOperator
1576 CacheFile::FailListenersIfNonExistentChunk(
1577 const uint32_t& aIdx,
1578 nsAutoPtr<ChunkListeners>& aListeners,
1579 void* aClosure)
1580 {
1581 CacheFile *file = static_cast<CacheFile*>(aClosure);
1583 LOG(("CacheFile::FailListenersIfNonExistentChunk() [this=%p, idx=%d]",
1584 file, aIdx));
1586 nsRefPtr<CacheFileChunk> chunk;
1587 file->mChunks.Get(aIdx, getter_AddRefs(chunk));
1588 if (chunk) {
1589 MOZ_ASSERT(!chunk->IsReady());
1590 return PL_DHASH_NEXT;
1591 }
1593 for (uint32_t i = 0 ; i < aListeners->mItems.Length() ; i++) {
1594 ChunkListenerItem *item = aListeners->mItems[i];
1595 file->NotifyChunkListener(item->mCallback, item->mTarget,
1596 NS_ERROR_NOT_AVAILABLE, aIdx, nullptr);
1597 delete item;
1598 }
1600 return PL_DHASH_REMOVE;
1601 }
1603 PLDHashOperator
1604 CacheFile::FailUpdateListeners(
1605 const uint32_t& aIdx,
1606 nsRefPtr<CacheFileChunk>& aChunk,
1607 void* aClosure)
1608 {
1609 #ifdef PR_LOGGING
1610 CacheFile *file = static_cast<CacheFile*>(aClosure);
1611 #endif
1613 LOG(("CacheFile::FailUpdateListeners() [this=%p, idx=%d]",
1614 file, aIdx));
1616 if (aChunk->IsReady()) {
1617 aChunk->NotifyUpdateListeners();
1618 }
1620 return PL_DHASH_NEXT;
1621 }
1623 nsresult
1624 CacheFile::PadChunkWithZeroes(uint32_t aChunkIdx)
1625 {
1626 AssertOwnsLock();
1628 // This method is used to pad last incomplete chunk with zeroes or create
1629 // a new chunk full of zeroes
1630 MOZ_ASSERT(mDataSize / kChunkSize == aChunkIdx);
1632 nsresult rv;
1633 nsRefPtr<CacheFileChunk> chunk;
1634 rv = GetChunkLocked(aChunkIdx, true, nullptr, getter_AddRefs(chunk));
1635 NS_ENSURE_SUCCESS(rv, rv);
1637 LOG(("CacheFile::PadChunkWithZeroes() - Zeroing hole in chunk %d, range %d-%d"
1638 " [this=%p]", aChunkIdx, chunk->DataSize(), kChunkSize - 1, this));
1640 chunk->EnsureBufSize(kChunkSize);
1641 memset(chunk->BufForWriting() + chunk->DataSize(), 0, kChunkSize - chunk->DataSize());
1643 chunk->UpdateDataSize(chunk->DataSize(), kChunkSize - chunk->DataSize(),
1644 false);
1646 ReleaseOutsideLock(chunk.forget().take());
1648 return NS_OK;
1649 }
1651 void
1652 CacheFile::SetError(nsresult aStatus)
1653 {
1654 if (NS_SUCCEEDED(mStatus)) {
1655 mStatus = aStatus;
1656 }
1657 }
1659 nsresult
1660 CacheFile::InitIndexEntry()
1661 {
1662 MOZ_ASSERT(mHandle);
1664 if (mHandle->IsDoomed())
1665 return NS_OK;
1667 nsresult rv;
1669 rv = CacheFileIOManager::InitIndexEntry(mHandle,
1670 mMetadata->AppId(),
1671 mMetadata->IsAnonymous(),
1672 mMetadata->IsInBrowser());
1673 NS_ENSURE_SUCCESS(rv, rv);
1675 uint32_t expTime;
1676 mMetadata->GetExpirationTime(&expTime);
1678 uint32_t frecency;
1679 mMetadata->GetFrecency(&frecency);
1681 rv = CacheFileIOManager::UpdateIndexEntry(mHandle, &frecency, &expTime);
1682 NS_ENSURE_SUCCESS(rv, rv);
1684 return NS_OK;
1685 }
1687 // Memory reporting
1689 namespace { // anon
1691 size_t
1692 CollectChunkSize(uint32_t const & aIdx,
1693 nsRefPtr<mozilla::net::CacheFileChunk> const & aChunk,
1694 mozilla::MallocSizeOf mallocSizeOf, void* aClosure)
1695 {
1696 return aChunk->SizeOfIncludingThis(mallocSizeOf);
1697 }
1699 } // anon
1701 size_t
1702 CacheFile::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
1703 {
1704 CacheFileAutoLock lock(const_cast<CacheFile*>(this));
1706 size_t n = 0;
1707 n += mKey.SizeOfExcludingThisIfUnshared(mallocSizeOf);
1708 n += mChunks.SizeOfExcludingThis(CollectChunkSize, mallocSizeOf);
1709 n += mCachedChunks.SizeOfExcludingThis(CollectChunkSize, mallocSizeOf);
1710 if (mMetadata) {
1711 n += mMetadata->SizeOfIncludingThis(mallocSizeOf);
1712 }
1714 // Input streams are not elsewhere reported.
1715 n += mInputs.SizeOfExcludingThis(mallocSizeOf);
1716 for (uint32_t i = 0; i < mInputs.Length(); ++i) {
1717 n += mInputs[i]->SizeOfIncludingThis(mallocSizeOf);
1718 }
1720 // Output streams are not elsewhere reported.
1721 if (mOutput) {
1722 n += mOutput->SizeOfIncludingThis(mallocSizeOf);
1723 }
1725 // The listeners are usually classes reported just above.
1726 n += mChunkListeners.SizeOfExcludingThis(nullptr, mallocSizeOf);
1727 n += mObjsToRelease.SizeOfExcludingThis(mallocSizeOf);
1729 // mHandle reported directly from CacheFileIOManager.
1731 return n;
1732 }
1734 size_t
1735 CacheFile::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
1736 {
1737 return mallocSizeOf(this) + SizeOfExcludingThis(mallocSizeOf);
1738 }
1740 } // net
1741 } // mozilla