|
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 |