netwerk/cache2/CacheFile.cpp

changeset 2
7e26c7da4463
equal deleted inserted replaced
-1:000000000000 0:6cebd4d1aa77
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 "CacheFile.h"
7
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"
17
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
23
24 //#define CACHE_CHUNKS
25
26 namespace mozilla {
27 namespace net {
28
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 }
42
43 ~NotifyCacheFileListenerEvent()
44 {
45 LOG(("NotifyCacheFileListenerEvent::~NotifyCacheFileListenerEvent() "
46 "[this=%p]", this));
47 MOZ_COUNT_DTOR(NotifyCacheFileListenerEvent);
48 }
49
50 NS_IMETHOD Run()
51 {
52 LOG(("NotifyCacheFileListenerEvent::Run() [this=%p]", this));
53
54 mCallback->OnFileReady(mRV, mIsNew);
55 return NS_OK;
56 }
57
58 protected:
59 nsCOMPtr<CacheFileListener> mCallback;
60 nsresult mRV;
61 bool mIsNew;
62 };
63
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 }
79
80 ~NotifyChunkListenerEvent()
81 {
82 LOG(("NotifyChunkListenerEvent::~NotifyChunkListenerEvent() [this=%p]",
83 this));
84 MOZ_COUNT_DTOR(NotifyChunkListenerEvent);
85 }
86
87 NS_IMETHOD Run()
88 {
89 LOG(("NotifyChunkListenerEvent::Run() [this=%p]", this));
90
91 mCallback->OnChunkAvailable(mRV, mChunkIdx, mChunk);
92 return NS_OK;
93 }
94
95 protected:
96 nsCOMPtr<CacheFileChunkListener> mCallback;
97 nsresult mRV;
98 uint32_t mChunkIdx;
99 nsRefPtr<CacheFileChunk> mChunk;
100 };
101
102
103 class DoomFileHelper : public CacheFileIOListener
104 {
105 public:
106 NS_DECL_THREADSAFE_ISUPPORTS
107
108 DoomFileHelper(CacheFileListener *aListener)
109 : mListener(aListener)
110 {
111 MOZ_COUNT_CTOR(DoomFileHelper);
112 }
113
114
115 NS_IMETHOD OnFileOpened(CacheFileHandle *aHandle, nsresult aResult)
116 {
117 MOZ_CRASH("DoomFileHelper::OnFileOpened should not be called!");
118 return NS_ERROR_UNEXPECTED;
119 }
120
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 }
127
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 }
133
134 NS_IMETHOD OnFileDoomed(CacheFileHandle *aHandle, nsresult aResult)
135 {
136 if (mListener)
137 mListener->OnFileDoomed(aResult);
138 return NS_OK;
139 }
140
141 NS_IMETHOD OnEOFSet(CacheFileHandle *aHandle, nsresult aResult)
142 {
143 MOZ_CRASH("DoomFileHelper::OnEOFSet should not be called!");
144 return NS_ERROR_UNEXPECTED;
145 }
146
147 NS_IMETHOD OnFileRenamed(CacheFileHandle *aHandle, nsresult aResult)
148 {
149 MOZ_CRASH("DoomFileHelper::OnFileRenamed should not be called!");
150 return NS_ERROR_UNEXPECTED;
151 }
152
153 private:
154 virtual ~DoomFileHelper()
155 {
156 MOZ_COUNT_DTOR(DoomFileHelper);
157 }
158
159 nsCOMPtr<CacheFileListener> mListener;
160 };
161
162 NS_IMPL_ISUPPORTS(DoomFileHelper, CacheFileIOListener)
163
164
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
174
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 }
190
191 CacheFile::~CacheFile()
192 {
193 LOG(("CacheFile::~CacheFile() [this=%p]", this));
194
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 }
201
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);
211
212 nsresult rv;
213
214 mKey = aKey;
215 mOpenAsMemoryOnly = mMemoryOnly = aMemoryOnly;
216
217 LOG(("CacheFile::Init() [this=%p, key=%s, createNew=%d, memoryOnly=%d, "
218 "listener=%p]", this, mKey.get(), aCreateNew, aMemoryOnly, aCallback));
219
220 if (mMemoryOnly) {
221 MOZ_ASSERT(!aCallback);
222
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;
233
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;
241
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;
250
251 // make sure we can use this entry immediately
252 mMetadata = new CacheFileMetadata(mOpenAsMemoryOnly, mKey);
253 mReady = true;
254 mDataSize = mMetadata->Offset();
255
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);
262
263 aCallback = nullptr;
264 }
265 }
266
267 if (aPriority)
268 flags |= CacheFileIOManager::PRIORITY;
269
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;
276
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));
282
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));
290
291 mMemoryOnly = true;
292 mMetadata = new CacheFileMetadata(mOpenAsMemoryOnly, mKey);
293 mReady = true;
294 mDataSize = mMetadata->Offset();
295
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 }
306
307 return NS_OK;
308 }
309
310 nsresult
311 CacheFile::OnChunkRead(nsresult aResult, CacheFileChunk *aChunk)
312 {
313 CacheFileAutoLock lock(this);
314
315 nsresult rv;
316
317 uint32_t index = aChunk->Index();
318
319 LOG(("CacheFile::OnChunkRead() [this=%p, rv=0x%08x, chunk=%p, idx=%d]",
320 this, aResult, aChunk, index));
321
322 if (NS_FAILED(aResult)) {
323 SetError(aResult);
324 CacheFileIOManager::DoomFile(mHandle, nullptr);
325 }
326
327 if (HaveChunkListeners(index)) {
328 rv = NotifyChunkListeners(index, aResult, aChunk);
329 NS_ENSURE_SUCCESS(rv, rv);
330 }
331
332 return NS_OK;
333 }
334
335 nsresult
336 CacheFile::OnChunkWritten(nsresult aResult, CacheFileChunk *aChunk)
337 {
338 CacheFileAutoLock lock(this);
339
340 nsresult rv;
341
342 LOG(("CacheFile::OnChunkWritten() [this=%p, rv=0x%08x, chunk=%p, idx=%d]",
343 this, aResult, aChunk, aChunk->Index()));
344
345 MOZ_ASSERT(!mMemoryOnly);
346 MOZ_ASSERT(!mOpeningFile);
347 MOZ_ASSERT(mHandle);
348
349 if (NS_FAILED(aResult)) {
350 SetError(aResult);
351 CacheFileIOManager::DoomFile(mHandle, nullptr);
352 }
353
354 if (NS_SUCCEEDED(aResult) && !aChunk->IsDirty()) {
355 // update hash value in metadata
356 mMetadata->SetHash(aChunk->Index(), aChunk->Hash());
357 }
358
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 }
368
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()));
372
373 return NS_OK;
374 }
375
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
388
389 RemoveChunkInternal(aChunk,
390 #ifdef CACHE_CHUNKS
391 NS_SUCCEEDED(aResult));
392 #else
393 false);
394 #endif
395
396 WriteMetadataIfNeededLocked();
397
398 return NS_OK;
399 }
400
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 }
408
409 nsresult
410 CacheFile::OnChunkUpdated(CacheFileChunk *aChunk)
411 {
412 MOZ_CRASH("CacheFile::OnChunkUpdated should not be called!");
413 return NS_ERROR_UNEXPECTED;
414 }
415
416 nsresult
417 CacheFile::OnFileOpened(CacheFileHandle *aHandle, nsresult aResult)
418 {
419 nsresult rv;
420
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;
434
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 }
445
446 CacheFileHandle* mHandle;
447 nsCOMPtr<CacheFileIOListener> mListener;
448 bool mAlreadyDoomed;
449 } autoDoom(aHandle);
450
451 nsCOMPtr<CacheFileListener> listener;
452 bool isNew = false;
453 nsresult retval = NS_OK;
454
455 {
456 CacheFileAutoLock lock(this);
457
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
464
465 LOG(("CacheFile::OnFileOpened() [this=%p, rv=0x%08x, handle=%p]",
466 this, aResult, aHandle));
467
468 mOpeningFile = false;
469
470 autoDoom.mListener.swap(mDoomAfterOpenListener);
471
472 if (mMemoryOnly) {
473 // We can be here only in case the entry was initilized as createNew and
474 // SetMemoryOnly() was called.
475
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));
488
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));
500
501 mMemoryOnly = true;
502 mMetadata = new CacheFileMetadata(mOpenAsMemoryOnly, mKey);
503 mReady = true;
504 mDataSize = mMetadata->Offset();
505
506 isNew = true;
507 retval = NS_OK;
508 }
509 else {
510 // CacheFileIOManager::OpenFile() failed for another reason.
511 isNew = false;
512 retval = aResult;
513 }
514
515 mListener.swap(listener);
516 }
517 else {
518 mHandle = aHandle;
519
520 if (mMetadata) {
521 InitIndexEntry();
522
523 // The entry was initialized as createNew, don't try to read metadata.
524 mMetadata->SetHandle(mHandle);
525
526 // Write all cached chunks, otherwise they may stay unwritten.
527 mCachedChunks.Enumerate(&CacheFile::WriteAllCachedChunks, this);
528
529 return NS_OK;
530 }
531 }
532 }
533
534 if (listener) {
535 listener->OnFileReady(retval, isNew);
536 return NS_OK;
537 }
538
539 MOZ_ASSERT(NS_SUCCEEDED(aResult));
540 MOZ_ASSERT(!mMetadata);
541 MOZ_ASSERT(mListener);
542
543 mMetadata = new CacheFileMetadata(mHandle, mKey);
544
545 rv = mMetadata->ReadMetadata(this);
546 if (NS_FAILED(rv)) {
547 mListener.swap(listener);
548 listener->OnFileReady(rv, false);
549 }
550
551 return NS_OK;
552 }
553
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 }
561
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 }
568
569 nsresult
570 CacheFile::OnMetadataRead(nsresult aResult)
571 {
572 MOZ_ASSERT(mListener);
573
574 LOG(("CacheFile::OnMetadataRead() [this=%p, rv=0x%08x]", this, aResult));
575
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 }
584
585 InitIndexEntry();
586 }
587
588 nsCOMPtr<CacheFileListener> listener;
589 mListener.swap(listener);
590 listener->OnFileReady(aResult, isNew);
591 return NS_OK;
592 }
593
594 nsresult
595 CacheFile::OnMetadataWritten(nsresult aResult)
596 {
597 CacheFileAutoLock lock(this);
598
599 LOG(("CacheFile::OnMetadataWritten() [this=%p, rv=0x%08x]", this, aResult));
600
601 MOZ_ASSERT(mWritingMetadata);
602 mWritingMetadata = false;
603
604 MOZ_ASSERT(!mMemoryOnly);
605 MOZ_ASSERT(!mOpeningFile);
606
607 if (NS_FAILED(aResult)) {
608 // TODO close streams with an error ???
609 }
610
611 if (mOutput || mInputs.Length() || mChunks.Count())
612 return NS_OK;
613
614 if (IsDirty())
615 WriteMetadataIfNeededLocked();
616
617 if (!mWritingMetadata) {
618 LOG(("CacheFile::OnMetadataWritten() - Releasing file handle [this=%p]",
619 this));
620 CacheFileIOManager::ReleaseNSPRHandle(mHandle);
621 }
622
623 return NS_OK;
624 }
625
626 nsresult
627 CacheFile::OnFileDoomed(CacheFileHandle *aHandle, nsresult aResult)
628 {
629 nsCOMPtr<CacheFileListener> listener;
630
631 {
632 CacheFileAutoLock lock(this);
633
634 MOZ_ASSERT(mListener);
635
636 LOG(("CacheFile::OnFileDoomed() [this=%p, rv=0x%08x, handle=%p]",
637 this, aResult, aHandle));
638
639 mListener.swap(listener);
640 }
641
642 listener->OnFileDoomed(aResult);
643 return NS_OK;
644 }
645
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 }
652
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 }
659
660 nsresult
661 CacheFile::OpenInputStream(nsIInputStream **_retval)
662 {
663 CacheFileAutoLock lock(this);
664
665 MOZ_ASSERT(mHandle || mMemoryOnly || mOpeningFile);
666
667 if (!mReady) {
668 LOG(("CacheFile::OpenInputStream() - CacheFile is not ready [this=%p]",
669 this));
670
671 return NS_ERROR_NOT_AVAILABLE;
672 }
673
674 CacheFileInputStream *input = new CacheFileInputStream(this);
675
676 LOG(("CacheFile::OpenInputStream() - Creating new input stream %p [this=%p]",
677 input, this));
678
679 mInputs.AppendElement(input);
680 NS_ADDREF(input);
681
682 mDataAccessed = true;
683 NS_ADDREF(*_retval = input);
684 return NS_OK;
685 }
686
687 nsresult
688 CacheFile::OpenOutputStream(CacheOutputCloseListener *aCloseListener, nsIOutputStream **_retval)
689 {
690 CacheFileAutoLock lock(this);
691
692 MOZ_ASSERT(mHandle || mMemoryOnly || mOpeningFile);
693
694 if (!mReady) {
695 LOG(("CacheFile::OpenOutputStream() - CacheFile is not ready [this=%p]",
696 this));
697
698 return NS_ERROR_NOT_AVAILABLE;
699 }
700
701 if (mOutput) {
702 LOG(("CacheFile::OpenOutputStream() - We already have output stream %p "
703 "[this=%p]", mOutput, this));
704
705 return NS_ERROR_NOT_AVAILABLE;
706 }
707
708 mOutput = new CacheFileOutputStream(this, aCloseListener);
709
710 LOG(("CacheFile::OpenOutputStream() - Creating new output stream %p "
711 "[this=%p]", mOutput, this));
712
713 mDataAccessed = true;
714 NS_ADDREF(*_retval = mOutput);
715 return NS_OK;
716 }
717
718 nsresult
719 CacheFile::SetMemoryOnly()
720 {
721 LOG(("CacheFile::SetMemoryOnly() mMemoryOnly=%d [this=%p]",
722 mMemoryOnly, this));
723
724 if (mMemoryOnly)
725 return NS_OK;
726
727 MOZ_ASSERT(mReady);
728
729 if (!mReady) {
730 LOG(("CacheFile::SetMemoryOnly() - CacheFile is not ready [this=%p]",
731 this));
732
733 return NS_ERROR_NOT_AVAILABLE;
734 }
735
736 if (mDataAccessed) {
737 LOG(("CacheFile::SetMemoryOnly() - Data was already accessed [this=%p]", this));
738 return NS_ERROR_NOT_AVAILABLE;
739 }
740
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 }
745
746 nsresult
747 CacheFile::Doom(CacheFileListener *aCallback)
748 {
749 CacheFileAutoLock lock(this);
750
751 MOZ_ASSERT(mHandle || mMemoryOnly || mOpeningFile);
752
753 LOG(("CacheFile::Doom() [this=%p, listener=%p]", this, aCallback));
754
755 nsresult rv = NS_OK;
756
757 if (mMemoryOnly) {
758 return NS_ERROR_FILE_NOT_FOUND;
759 }
760
761 if (mHandle && mHandle->IsDoomed()) {
762 return NS_ERROR_FILE_NOT_FOUND;
763 }
764
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 }
774
775 return rv;
776 }
777
778 nsresult
779 CacheFile::ThrowMemoryCachedData()
780 {
781 CacheFileAutoLock lock(this);
782
783 LOG(("CacheFile::ThrowMemoryCachedData() [this=%p]", this));
784
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));
791
792 return NS_ERROR_NOT_AVAILABLE;
793 }
794
795 if (mOpeningFile) {
796 // mayhemer, note: we shouldn't get here, since CacheEntry prevents loading
797 // entries from being purged.
798
799 LOG(("CacheFile::ThrowMemoryCachedData() - Ignoring request because the "
800 "entry is still opening the file [this=%p]", this));
801
802 return NS_ERROR_ABORT;
803 }
804
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
811
812 return NS_OK;
813 }
814
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);
821
822 const char *value;
823 value = mMetadata->GetElement(aKey);
824 if (!value)
825 return NS_ERROR_NOT_AVAILABLE;
826
827 *_retval = NS_strdup(value);
828 return NS_OK;
829 }
830
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);
837
838 PostWriteTimer();
839 return mMetadata->SetElement(aKey, aValue);
840 }
841
842 nsresult
843 CacheFile::ElementsSize(uint32_t *_retval)
844 {
845 CacheFileAutoLock lock(this);
846
847 if (!mMetadata)
848 return NS_ERROR_NOT_AVAILABLE;
849
850 *_retval = mMetadata->ElementsSize();
851 return NS_OK;
852 }
853
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);
860
861 PostWriteTimer();
862
863 if (mHandle && !mHandle->IsDoomed())
864 CacheFileIOManager::UpdateIndexEntry(mHandle, nullptr, &aExpirationTime);
865
866 return mMetadata->SetExpirationTime(aExpirationTime);
867 }
868
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);
875
876 return mMetadata->GetExpirationTime(_retval);
877 }
878
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);
885
886 PostWriteTimer();
887 return mMetadata->SetLastModified(aLastModified);
888 }
889
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);
896
897 return mMetadata->GetLastModified(_retval);
898 }
899
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);
906
907 PostWriteTimer();
908
909 if (mHandle && !mHandle->IsDoomed())
910 CacheFileIOManager::UpdateIndexEntry(mHandle, &aFrecency, nullptr);
911
912 return mMetadata->SetFrecency(aFrecency);
913 }
914
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);
921
922 return mMetadata->GetFrecency(_retval);
923 }
924
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);
931
932 return mMetadata->GetLastFetched(_retval);
933 }
934
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);
941
942 return mMetadata->GetFetchCount(_retval);
943 }
944
945 void
946 CacheFile::Lock()
947 {
948 mLock.Lock();
949 }
950
951 void
952 CacheFile::Unlock()
953 {
954 nsTArray<nsISupports*> objs;
955 objs.SwapElements(mObjsToRelease);
956
957 mLock.Unlock();
958
959 for (uint32_t i = 0; i < objs.Length(); i++)
960 objs[i]->Release();
961 }
962
963 void
964 CacheFile::AssertOwnsLock() const
965 {
966 mLock.AssertCurrentThreadOwns();
967 }
968
969 void
970 CacheFile::ReleaseOutsideLock(nsISupports *aObject)
971 {
972 AssertOwnsLock();
973
974 mObjsToRelease.AppendElement(aObject);
975 }
976
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 }
984
985 nsresult
986 CacheFile::GetChunkLocked(uint32_t aIndex, bool aWriter,
987 CacheFileChunkListener *aCallback,
988 CacheFileChunk **_retval)
989 {
990 AssertOwnsLock();
991
992 LOG(("CacheFile::GetChunkLocked() [this=%p, idx=%d, writer=%d, listener=%p]",
993 this, aIndex, aWriter, aCallback));
994
995 MOZ_ASSERT(mReady);
996 MOZ_ASSERT(mHandle || mMemoryOnly || mOpeningFile);
997 MOZ_ASSERT((aWriter && !aCallback) || (!aWriter && aCallback));
998
999 nsresult rv;
1000
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));
1005
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 }
1015
1016 if (chunk->IsReady() || aWriter) {
1017 chunk.swap(*_retval);
1018 }
1019 else {
1020 rv = QueueChunkListener(aIndex, aCallback);
1021 NS_ENSURE_SUCCESS(rv, rv);
1022 }
1023
1024 return NS_OK;
1025 }
1026
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));
1035
1036 mChunks.Put(aIndex, chunk);
1037 mCachedChunks.Remove(aIndex);
1038 chunk->mFile = this;
1039 chunk->mRemovingChunk = false;
1040
1041 MOZ_ASSERT(chunk->IsReady());
1042
1043 chunk.swap(*_retval);
1044 return NS_OK;
1045 }
1046
1047 int64_t off = aIndex * kChunkSize;
1048
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));
1058
1059 return NS_ERROR_UNEXPECTED;
1060 }
1061
1062 chunk = new CacheFileChunk(this, aIndex);
1063 mChunks.Put(aIndex, chunk);
1064
1065 LOG(("CacheFile::GetChunkLocked() - Reading newly created chunk %p from "
1066 "the disk [this=%p]", chunk.get(), this));
1067
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 }
1076
1077 if (aWriter) {
1078 chunk.swap(*_retval);
1079 }
1080 else {
1081 rv = QueueChunkListener(aIndex, aCallback);
1082 NS_ENSURE_SUCCESS(rv, rv);
1083 }
1084
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);
1092
1093 LOG(("CacheFile::GetChunkLocked() - Created new empty chunk %p [this=%p]",
1094 chunk.get(), this));
1095
1096 chunk->InitNew(this);
1097 mMetadata->SetHash(aIndex, chunk->Hash());
1098
1099 if (HaveChunkListeners(aIndex)) {
1100 rv = NotifyChunkListeners(aIndex, NS_OK, chunk);
1101 NS_ENSURE_SUCCESS(rv, rv);
1102 }
1103
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
1111
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);
1116
1117 MOZ_ASSERT(!(mDataSize % kChunkSize));
1118 }
1119
1120 uint32_t startChunk = mDataSize / kChunkSize;
1121
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.
1133
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 }
1142
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 }
1154
1155 MOZ_ASSERT(mDataSize == off);
1156 rv = GetChunkLocked(aIndex, true, nullptr, getter_AddRefs(chunk));
1157 NS_ENSURE_SUCCESS(rv, rv);
1158
1159 chunk.swap(*_retval);
1160 return NS_OK;
1161 }
1162 }
1163
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 }
1172
1173 return NS_OK;
1174 }
1175
1176 nsresult
1177 CacheFile::RemoveChunk(CacheFileChunk *aChunk)
1178 {
1179 nsresult rv;
1180
1181 // Avoid lock reentrancy by increasing the RefCnt
1182 nsRefPtr<CacheFileChunk> chunk = aChunk;
1183
1184 {
1185 CacheFileAutoLock lock(this);
1186
1187 LOG(("CacheFile::RemoveChunk() [this=%p, chunk=%p, idx=%d]",
1188 this, aChunk, aChunk->Index()));
1189
1190 MOZ_ASSERT(mReady);
1191 MOZ_ASSERT((mHandle && !mMemoryOnly && !mOpeningFile) ||
1192 (!mHandle && mMemoryOnly && !mOpeningFile) ||
1193 (!mHandle && !mMemoryOnly && mOpeningFile));
1194
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()));
1198
1199 // somebody got the reference before the lock was acquired
1200 return NS_OK;
1201 }
1202
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);
1209
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
1216
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));
1221
1222 RemoveChunkInternal(chunk, false);
1223 return mStatus;
1224 }
1225
1226 if (chunk->IsDirty() && !mMemoryOnly && !mOpeningFile) {
1227 LOG(("CacheFile::RemoveChunk() - Writing dirty chunk to the disk "
1228 "[this=%p]", this));
1229
1230 mDataIsDirty = true;
1231
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));
1237
1238 RemoveChunkInternal(chunk, false);
1239
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
1246
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 }
1253
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
1267
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
1275
1276 if (!mMemoryOnly)
1277 WriteMetadataIfNeededLocked();
1278 }
1279
1280 return NS_OK;
1281 }
1282
1283 void
1284 CacheFile::RemoveChunkInternal(CacheFileChunk *aChunk, bool aCacheChunk)
1285 {
1286 aChunk->mRemovingChunk = true;
1287 ReleaseOutsideLock(static_cast<CacheFileChunkListener *>(
1288 aChunk->mFile.forget().take()));
1289
1290 if (aCacheChunk) {
1291 mCachedChunks.Put(aChunk->Index(), aChunk);
1292 }
1293
1294 mChunks.Remove(aChunk->Index());
1295 }
1296
1297 nsresult
1298 CacheFile::RemoveInput(CacheFileInputStream *aInput)
1299 {
1300 CacheFileAutoLock lock(this);
1301
1302 LOG(("CacheFile::RemoveInput() [this=%p, input=%p]", this, aInput));
1303
1304 DebugOnly<bool> found;
1305 found = mInputs.RemoveElement(aInput);
1306 MOZ_ASSERT(found);
1307
1308 ReleaseOutsideLock(static_cast<nsIInputStream*>(aInput));
1309
1310 if (!mMemoryOnly)
1311 WriteMetadataIfNeededLocked();
1312
1313 return NS_OK;
1314 }
1315
1316 nsresult
1317 CacheFile::RemoveOutput(CacheFileOutputStream *aOutput)
1318 {
1319 AssertOwnsLock();
1320
1321 LOG(("CacheFile::RemoveOutput() [this=%p, output=%p]", this, aOutput));
1322
1323 if (mOutput != aOutput) {
1324 LOG(("CacheFile::RemoveOutput() - This output was already removed, ignoring"
1325 " call [this=%p]", this));
1326 return NS_OK;
1327 }
1328
1329 mOutput = nullptr;
1330
1331 // Cancel all queued chunk and update listeners that cannot be satisfied
1332 NotifyListenersAboutOutputRemoval();
1333
1334 if (!mMemoryOnly)
1335 WriteMetadataIfNeededLocked();
1336
1337 // Notify close listener as the last action
1338 aOutput->NotifyCloseListener();
1339
1340 return NS_OK;
1341 }
1342
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));
1353
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);
1362
1363 return NS_OK;
1364 }
1365
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));
1372
1373 AssertOwnsLock();
1374
1375 MOZ_ASSERT(aCallback);
1376
1377 ChunkListenerItem *item = new ChunkListenerItem();
1378 item->mTarget = NS_GetCurrentThread();
1379 item->mCallback = aCallback;
1380
1381 ChunkListeners *listeners;
1382 if (!mChunkListeners.Get(aIndex, &listeners)) {
1383 listeners = new ChunkListeners();
1384 mChunkListeners.Put(aIndex, listeners);
1385 }
1386
1387 listeners->mItems.AppendElement(item);
1388 return NS_OK;
1389 }
1390
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));
1397
1398 AssertOwnsLock();
1399
1400 nsresult rv, rv2;
1401
1402 ChunkListeners *listeners;
1403 mChunkListeners.Get(aIndex, &listeners);
1404 MOZ_ASSERT(listeners);
1405
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 }
1415
1416 mChunkListeners.Remove(aIndex);
1417
1418 return rv;
1419 }
1420
1421 bool
1422 CacheFile::HaveChunkListeners(uint32_t aIndex)
1423 {
1424 ChunkListeners *listeners;
1425 mChunkListeners.Get(aIndex, &listeners);
1426 return !!listeners;
1427 }
1428
1429 void
1430 CacheFile::NotifyListenersAboutOutputRemoval()
1431 {
1432 LOG(("CacheFile::NotifyListenersAboutOutputRemoval() [this=%p]", this));
1433
1434 AssertOwnsLock();
1435
1436 // First fail all chunk listeners that wait for non-existent chunk
1437 mChunkListeners.Enumerate(&CacheFile::FailListenersIfNonExistentChunk,
1438 this);
1439
1440 // Fail all update listeners
1441 mChunks.Enumerate(&CacheFile::FailUpdateListeners, this);
1442 }
1443
1444 bool
1445 CacheFile::DataSize(int64_t* aSize)
1446 {
1447 CacheFileAutoLock lock(this);
1448
1449 if (mOutput)
1450 return false;
1451
1452 *aSize = mDataSize;
1453 return true;
1454 }
1455
1456 bool
1457 CacheFile::IsDoomed()
1458 {
1459 CacheFileAutoLock lock(this);
1460
1461 if (!mHandle)
1462 return false;
1463
1464 return mHandle->IsDoomed();
1465 }
1466
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 }
1481
1482 bool
1483 CacheFile::IsDirty()
1484 {
1485 return mDataIsDirty || mMetadata->IsDirty();
1486 }
1487
1488 void
1489 CacheFile::WriteMetadataIfNeeded()
1490 {
1491 LOG(("CacheFile::WriteMetadataIfNeeded() [this=%p]", this));
1492
1493 CacheFileAutoLock lock(this);
1494
1495 if (!mMemoryOnly)
1496 WriteMetadataIfNeededLocked();
1497 }
1498
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!
1504
1505 LOG(("CacheFile::WriteMetadataIfNeededLocked() [this=%p]", this));
1506
1507 nsresult rv;
1508
1509 AssertOwnsLock();
1510 MOZ_ASSERT(!mMemoryOnly);
1511
1512 if (!mMetadata) {
1513 MOZ_CRASH("Must have metadata here");
1514 return;
1515 }
1516
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 }
1522
1523 if (NS_FAILED(mStatus))
1524 return;
1525
1526 if (!IsDirty() || mOutput || mInputs.Length() || mChunks.Count() ||
1527 mWritingMetadata || mOpeningFile)
1528 return;
1529
1530 LOG(("CacheFile::WriteMetadataIfNeededLocked() - Writing metadata [this=%p]",
1531 this));
1532
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 }
1544
1545 void
1546 CacheFile::PostWriteTimer()
1547 {
1548 LOG(("CacheFile::PostWriteTimer() [this=%p]", this));
1549
1550 CacheFileIOManager::ScheduleMetadataWrite(this);
1551 }
1552
1553 PLDHashOperator
1554 CacheFile::WriteAllCachedChunks(const uint32_t& aIdx,
1555 nsRefPtr<CacheFileChunk>& aChunk,
1556 void* aClosure)
1557 {
1558 CacheFile *file = static_cast<CacheFile*>(aClosure);
1559
1560 LOG(("CacheFile::WriteAllCachedChunks() [this=%p, idx=%d, chunk=%p]",
1561 file, aIdx, aChunk.get()));
1562
1563 file->mChunks.Put(aIdx, aChunk);
1564 aChunk->mFile = file;
1565 aChunk->mRemovingChunk = false;
1566
1567 MOZ_ASSERT(aChunk->IsReady());
1568
1569 NS_ADDREF(aChunk);
1570 file->ReleaseOutsideLock(aChunk);
1571
1572 return PL_DHASH_REMOVE;
1573 }
1574
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);
1582
1583 LOG(("CacheFile::FailListenersIfNonExistentChunk() [this=%p, idx=%d]",
1584 file, aIdx));
1585
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 }
1592
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 }
1599
1600 return PL_DHASH_REMOVE;
1601 }
1602
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
1612
1613 LOG(("CacheFile::FailUpdateListeners() [this=%p, idx=%d]",
1614 file, aIdx));
1615
1616 if (aChunk->IsReady()) {
1617 aChunk->NotifyUpdateListeners();
1618 }
1619
1620 return PL_DHASH_NEXT;
1621 }
1622
1623 nsresult
1624 CacheFile::PadChunkWithZeroes(uint32_t aChunkIdx)
1625 {
1626 AssertOwnsLock();
1627
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);
1631
1632 nsresult rv;
1633 nsRefPtr<CacheFileChunk> chunk;
1634 rv = GetChunkLocked(aChunkIdx, true, nullptr, getter_AddRefs(chunk));
1635 NS_ENSURE_SUCCESS(rv, rv);
1636
1637 LOG(("CacheFile::PadChunkWithZeroes() - Zeroing hole in chunk %d, range %d-%d"
1638 " [this=%p]", aChunkIdx, chunk->DataSize(), kChunkSize - 1, this));
1639
1640 chunk->EnsureBufSize(kChunkSize);
1641 memset(chunk->BufForWriting() + chunk->DataSize(), 0, kChunkSize - chunk->DataSize());
1642
1643 chunk->UpdateDataSize(chunk->DataSize(), kChunkSize - chunk->DataSize(),
1644 false);
1645
1646 ReleaseOutsideLock(chunk.forget().take());
1647
1648 return NS_OK;
1649 }
1650
1651 void
1652 CacheFile::SetError(nsresult aStatus)
1653 {
1654 if (NS_SUCCEEDED(mStatus)) {
1655 mStatus = aStatus;
1656 }
1657 }
1658
1659 nsresult
1660 CacheFile::InitIndexEntry()
1661 {
1662 MOZ_ASSERT(mHandle);
1663
1664 if (mHandle->IsDoomed())
1665 return NS_OK;
1666
1667 nsresult rv;
1668
1669 rv = CacheFileIOManager::InitIndexEntry(mHandle,
1670 mMetadata->AppId(),
1671 mMetadata->IsAnonymous(),
1672 mMetadata->IsInBrowser());
1673 NS_ENSURE_SUCCESS(rv, rv);
1674
1675 uint32_t expTime;
1676 mMetadata->GetExpirationTime(&expTime);
1677
1678 uint32_t frecency;
1679 mMetadata->GetFrecency(&frecency);
1680
1681 rv = CacheFileIOManager::UpdateIndexEntry(mHandle, &frecency, &expTime);
1682 NS_ENSURE_SUCCESS(rv, rv);
1683
1684 return NS_OK;
1685 }
1686
1687 // Memory reporting
1688
1689 namespace { // anon
1690
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 }
1698
1699 } // anon
1700
1701 size_t
1702 CacheFile::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
1703 {
1704 CacheFileAutoLock lock(const_cast<CacheFile*>(this));
1705
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 }
1713
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 }
1719
1720 // Output streams are not elsewhere reported.
1721 if (mOutput) {
1722 n += mOutput->SizeOfIncludingThis(mallocSizeOf);
1723 }
1724
1725 // The listeners are usually classes reported just above.
1726 n += mChunkListeners.SizeOfExcludingThis(nullptr, mallocSizeOf);
1727 n += mObjsToRelease.SizeOfExcludingThis(mallocSizeOf);
1728
1729 // mHandle reported directly from CacheFileIOManager.
1730
1731 return n;
1732 }
1733
1734 size_t
1735 CacheFile::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
1736 {
1737 return mallocSizeOf(this) + SizeOfExcludingThis(mallocSizeOf);
1738 }
1739
1740 } // net
1741 } // mozilla

mercurial