netwerk/cache2/CacheFileIOManager.cpp

branch
TOR_BUG_9701
changeset 10
ac0c01689b40
equal deleted inserted replaced
-1:000000000000 0:6b954ef19707
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 "CacheFileIOManager.h"
7
8 #include "../cache/nsCacheUtils.h"
9 #include "CacheHashUtils.h"
10 #include "CacheStorageService.h"
11 #include "CacheIndex.h"
12 #include "CacheFileUtils.h"
13 #include "nsThreadUtils.h"
14 #include "CacheFile.h"
15 #include "CacheObserver.h"
16 #include "nsIFile.h"
17 #include "CacheFileContextEvictor.h"
18 #include "nsITimer.h"
19 #include "nsISimpleEnumerator.h"
20 #include "nsIDirectoryEnumerator.h"
21 #include "nsIObserverService.h"
22 #include "nsISizeOf.h"
23 #include "mozilla/Telemetry.h"
24 #include "mozilla/DebugOnly.h"
25 #include "mozilla/Services.h"
26 #include "nsDirectoryServiceUtils.h"
27 #include "nsAppDirectoryServiceDefs.h"
28 #include "private/pprio.h"
29 #include "mozilla/VisualEventTracer.h"
30 #include "mozilla/Preferences.h"
31
32 // include files for ftruncate (or equivalent)
33 #if defined(XP_UNIX)
34 #include <unistd.h>
35 #elif defined(XP_WIN)
36 #include <windows.h>
37 #undef CreateFile
38 #undef CREATE_NEW
39 #else
40 // XXX add necessary include file for ftruncate (or equivalent)
41 #endif
42
43
44 namespace mozilla {
45 namespace net {
46
47 #define kOpenHandlesLimit 64
48 #define kMetadataWriteDelay 5000
49 #define kRemoveTrashStartDelay 60000 // in milliseconds
50 #define kSmartSizeUpdateInterval 60000 // in milliseconds
51
52 #ifdef ANDROID
53 const uint32_t kMaxCacheSizeKB = 200*1024; // 200 MB
54 #else
55 const uint32_t kMaxCacheSizeKB = 350*1024; // 350 MB
56 #endif
57
58 bool
59 CacheFileHandle::DispatchRelease()
60 {
61 if (CacheFileIOManager::IsOnIOThreadOrCeased()) {
62 return false;
63 }
64
65 nsCOMPtr<nsIEventTarget> ioTarget = CacheFileIOManager::IOTarget();
66 if (!ioTarget) {
67 return false;
68 }
69
70 nsRefPtr<nsRunnableMethod<CacheFileHandle, MozExternalRefCountType, false> > event =
71 NS_NewNonOwningRunnableMethod(this, &CacheFileHandle::Release);
72 nsresult rv = ioTarget->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL);
73 if (NS_FAILED(rv)) {
74 return false;
75 }
76
77 return true;
78 }
79
80 NS_IMPL_ADDREF(CacheFileHandle)
81 NS_IMETHODIMP_(MozExternalRefCountType)
82 CacheFileHandle::Release()
83 {
84 nsrefcnt count = mRefCnt - 1;
85 if (DispatchRelease()) {
86 // Redispatched to the IO thread.
87 return count;
88 }
89
90 MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
91
92 LOG(("CacheFileHandle::Release() [this=%p, refcnt=%d]", this, mRefCnt.get()));
93 NS_PRECONDITION(0 != mRefCnt, "dup release");
94 count = --mRefCnt;
95 NS_LOG_RELEASE(this, count, "CacheFileHandle");
96
97 if (0 == count) {
98 mRefCnt = 1;
99 delete (this);
100 return 0;
101 }
102
103 return count;
104 }
105
106 NS_INTERFACE_MAP_BEGIN(CacheFileHandle)
107 NS_INTERFACE_MAP_ENTRY(nsISupports)
108 NS_INTERFACE_MAP_END_THREADSAFE
109
110 CacheFileHandle::CacheFileHandle(const SHA1Sum::Hash *aHash, bool aPriority)
111 : mHash(aHash)
112 , mIsDoomed(false)
113 , mPriority(aPriority)
114 , mClosed(false)
115 , mInvalid(false)
116 , mFileExists(false)
117 , mFileSize(-1)
118 , mFD(nullptr)
119 {
120 LOG(("CacheFileHandle::CacheFileHandle() [this=%p, hash=%08x%08x%08x%08x%08x]"
121 , this, LOGSHA1(aHash)));
122 }
123
124 CacheFileHandle::CacheFileHandle(const nsACString &aKey, bool aPriority)
125 : mHash(nullptr)
126 , mIsDoomed(false)
127 , mPriority(aPriority)
128 , mClosed(false)
129 , mInvalid(false)
130 , mFileExists(false)
131 , mFileSize(-1)
132 , mFD(nullptr)
133 , mKey(aKey)
134 {
135 LOG(("CacheFileHandle::CacheFileHandle() [this=%p, key=%s]", this,
136 PromiseFlatCString(aKey).get()));
137 }
138
139 CacheFileHandle::~CacheFileHandle()
140 {
141 LOG(("CacheFileHandle::~CacheFileHandle() [this=%p]", this));
142
143 MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
144
145 nsRefPtr<CacheFileIOManager> ioMan = CacheFileIOManager::gInstance;
146 if (ioMan) {
147 ioMan->CloseHandleInternal(this);
148 }
149 }
150
151 void
152 CacheFileHandle::Log()
153 {
154 nsAutoCString leafName;
155 if (mFile) {
156 mFile->GetNativeLeafName(leafName);
157 }
158
159 if (!mHash) {
160 // special file
161 LOG(("CacheFileHandle::Log() [this=%p, hash=nullptr, isDoomed=%d, "
162 "priority=%d, closed=%d, invalid=%d, "
163 "fileExists=%d, fileSize=%lld, leafName=%s, key=%s]",
164 this, mIsDoomed, mPriority, mClosed, mInvalid,
165 mFileExists, mFileSize, leafName.get(), mKey.get()));
166 } else {
167 LOG(("CacheFileHandle::Log() [this=%p, hash=%08x%08x%08x%08x%08x, "
168 "isDoomed=%d, priority=%d, closed=%d, invalid=%d, "
169 "fileExists=%d, fileSize=%lld, leafName=%s, key=%s]",
170 this, LOGSHA1(mHash), mIsDoomed, mPriority, mClosed,
171 mInvalid, mFileExists, mFileSize, leafName.get(), mKey.get()));
172 }
173 }
174
175 uint32_t
176 CacheFileHandle::FileSizeInK() const
177 {
178 MOZ_ASSERT(mFileSize != -1);
179 uint64_t size64 = mFileSize;
180
181 size64 += 0x3FF;
182 size64 >>= 10;
183
184 uint32_t size;
185 if (size64 >> 32) {
186 NS_WARNING("CacheFileHandle::FileSizeInK() - FileSize is too large, "
187 "truncating to PR_UINT32_MAX");
188 size = PR_UINT32_MAX;
189 } else {
190 size = static_cast<uint32_t>(size64);
191 }
192
193 return size;
194 }
195
196 // Memory reporting
197
198 size_t
199 CacheFileHandle::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
200 {
201 size_t n = 0;
202 nsCOMPtr<nsISizeOf> sizeOf;
203
204 sizeOf = do_QueryInterface(mFile);
205 if (sizeOf) {
206 n += sizeOf->SizeOfIncludingThis(mallocSizeOf);
207 }
208
209 n += mallocSizeOf(mFD);
210 n += mKey.SizeOfExcludingThisIfUnshared(mallocSizeOf);
211 return n;
212 }
213
214 size_t
215 CacheFileHandle::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
216 {
217 return mallocSizeOf(this) + SizeOfExcludingThis(mallocSizeOf);
218 }
219
220 /******************************************************************************
221 * CacheFileHandles::HandleHashKey
222 *****************************************************************************/
223
224 void
225 CacheFileHandles::HandleHashKey::AddHandle(CacheFileHandle* aHandle)
226 {
227 MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
228
229 mHandles.InsertElementAt(0, aHandle);
230 }
231
232 void
233 CacheFileHandles::HandleHashKey::RemoveHandle(CacheFileHandle* aHandle)
234 {
235 MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
236
237 DebugOnly<bool> found;
238 found = mHandles.RemoveElement(aHandle);
239 MOZ_ASSERT(found);
240 }
241
242 already_AddRefed<CacheFileHandle>
243 CacheFileHandles::HandleHashKey::GetNewestHandle()
244 {
245 MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
246
247 nsRefPtr<CacheFileHandle> handle;
248 if (mHandles.Length()) {
249 handle = mHandles[0];
250 }
251
252 return handle.forget();
253 }
254
255 void
256 CacheFileHandles::HandleHashKey::GetHandles(nsTArray<nsRefPtr<CacheFileHandle> > &aResult)
257 {
258 MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
259
260 for (uint32_t i = 0; i < mHandles.Length(); ++i) {
261 CacheFileHandle* handle = mHandles[i];
262 aResult.AppendElement(handle);
263 }
264 }
265
266 #ifdef DEBUG
267
268 void
269 CacheFileHandles::HandleHashKey::AssertHandlesState()
270 {
271 for (uint32_t i = 0; i < mHandles.Length(); ++i) {
272 CacheFileHandle* handle = mHandles[i];
273 MOZ_ASSERT(handle->IsDoomed());
274 }
275 }
276
277 #endif
278
279 size_t
280 CacheFileHandles::HandleHashKey::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
281 {
282 MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
283
284 size_t n = 0;
285 n += mallocSizeOf(mHash);
286 for (uint32_t i = 0; i < mHandles.Length(); ++i) {
287 n += mHandles[i]->SizeOfIncludingThis(mallocSizeOf);
288 }
289
290 return n;
291 }
292
293 /******************************************************************************
294 * CacheFileHandles
295 *****************************************************************************/
296
297 CacheFileHandles::CacheFileHandles()
298 {
299 LOG(("CacheFileHandles::CacheFileHandles() [this=%p]", this));
300 MOZ_COUNT_CTOR(CacheFileHandles);
301 }
302
303 CacheFileHandles::~CacheFileHandles()
304 {
305 LOG(("CacheFileHandles::~CacheFileHandles() [this=%p]", this));
306 MOZ_COUNT_DTOR(CacheFileHandles);
307 }
308
309 nsresult
310 CacheFileHandles::GetHandle(const SHA1Sum::Hash *aHash,
311 bool aReturnDoomed,
312 CacheFileHandle **_retval)
313 {
314 MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
315 MOZ_ASSERT(aHash);
316
317 #ifdef DEBUG_HANDLES
318 LOG(("CacheFileHandles::GetHandle() [hash=%08x%08x%08x%08x%08x]",
319 LOGSHA1(aHash)));
320 #endif
321
322 // find hash entry for key
323 HandleHashKey *entry = mTable.GetEntry(*aHash);
324 if (!entry) {
325 LOG(("CacheFileHandles::GetHandle() hash=%08x%08x%08x%08x%08x "
326 "no handle entries found", LOGSHA1(aHash)));
327 return NS_ERROR_NOT_AVAILABLE;
328 }
329
330 #ifdef DEBUG_HANDLES
331 Log(entry);
332 #endif
333
334 // Check if the entry is doomed
335 nsRefPtr<CacheFileHandle> handle = entry->GetNewestHandle();
336 if (!handle) {
337 LOG(("CacheFileHandles::GetHandle() hash=%08x%08x%08x%08x%08x "
338 "no handle found %p, entry %p", LOGSHA1(aHash), handle.get(), entry));
339 return NS_ERROR_NOT_AVAILABLE;
340 }
341
342 if (handle->IsDoomed()) {
343 LOG(("CacheFileHandles::GetHandle() hash=%08x%08x%08x%08x%08x "
344 "found doomed handle %p, entry %p", LOGSHA1(aHash), handle.get(), entry));
345
346 // If the consumer doesn't want doomed handles, exit with NOT_AVAIL.
347 if (!aReturnDoomed) {
348 return NS_ERROR_NOT_AVAILABLE;
349 }
350 } else {
351 LOG(("CacheFileHandles::GetHandle() hash=%08x%08x%08x%08x%08x "
352 "found handle %p, entry %p", LOGSHA1(aHash), handle.get(), entry));
353 }
354
355 handle.forget(_retval);
356 return NS_OK;
357 }
358
359
360 nsresult
361 CacheFileHandles::NewHandle(const SHA1Sum::Hash *aHash,
362 bool aPriority,
363 CacheFileHandle **_retval)
364 {
365 MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
366 MOZ_ASSERT(aHash);
367
368 #ifdef DEBUG_HANDLES
369 LOG(("CacheFileHandles::NewHandle() [hash=%08x%08x%08x%08x%08x]", LOGSHA1(aHash)));
370 #endif
371
372 // find hash entry for key
373 HandleHashKey *entry = mTable.PutEntry(*aHash);
374
375 #ifdef DEBUG_HANDLES
376 Log(entry);
377 #endif
378
379 #ifdef DEBUG
380 entry->AssertHandlesState();
381 #endif
382
383 nsRefPtr<CacheFileHandle> handle = new CacheFileHandle(entry->Hash(), aPriority);
384 entry->AddHandle(handle);
385
386 LOG(("CacheFileHandles::NewHandle() hash=%08x%08x%08x%08x%08x "
387 "created new handle %p, entry=%p", LOGSHA1(aHash), handle.get(), entry));
388
389 handle.forget(_retval);
390 return NS_OK;
391 }
392
393 void
394 CacheFileHandles::RemoveHandle(CacheFileHandle *aHandle)
395 {
396 MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
397 MOZ_ASSERT(aHandle);
398
399 if (!aHandle) {
400 return;
401 }
402
403 #ifdef DEBUG_HANDLES
404 LOG(("CacheFileHandles::RemoveHandle() [handle=%p, hash=%08x%08x%08x%08x%08x]"
405 , aHandle, LOGSHA1(aHandle->Hash())));
406 #endif
407
408 // find hash entry for key
409 HandleHashKey *entry = mTable.GetEntry(*aHandle->Hash());
410 if (!entry) {
411 MOZ_ASSERT(CacheFileIOManager::IsShutdown(),
412 "Should find entry when removing a handle before shutdown");
413
414 LOG(("CacheFileHandles::RemoveHandle() hash=%08x%08x%08x%08x%08x "
415 "no entries found", LOGSHA1(aHandle->Hash())));
416 return;
417 }
418
419 #ifdef DEBUG_HANDLES
420 Log(entry);
421 #endif
422
423 LOG(("CacheFileHandles::RemoveHandle() hash=%08x%08x%08x%08x%08x "
424 "removing handle %p", LOGSHA1(entry->Hash()), aHandle));
425 entry->RemoveHandle(aHandle);
426
427 if (entry->IsEmpty()) {
428 LOG(("CacheFileHandles::RemoveHandle() hash=%08x%08x%08x%08x%08x "
429 "list is empty, removing entry %p", LOGSHA1(entry->Hash()), entry));
430 mTable.RemoveEntry(*entry->Hash());
431 }
432 }
433
434 static PLDHashOperator
435 GetAllHandlesEnum(CacheFileHandles::HandleHashKey* aEntry, void *aClosure)
436 {
437 nsTArray<nsRefPtr<CacheFileHandle> > *array =
438 static_cast<nsTArray<nsRefPtr<CacheFileHandle> > *>(aClosure);
439
440 aEntry->GetHandles(*array);
441 return PL_DHASH_NEXT;
442 }
443
444 void
445 CacheFileHandles::GetAllHandles(nsTArray<nsRefPtr<CacheFileHandle> > *_retval)
446 {
447 MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
448 mTable.EnumerateEntries(&GetAllHandlesEnum, _retval);
449 }
450
451 static PLDHashOperator
452 GetActiveHandlesEnum(CacheFileHandles::HandleHashKey* aEntry, void *aClosure)
453 {
454 nsTArray<nsRefPtr<CacheFileHandle> > *array =
455 static_cast<nsTArray<nsRefPtr<CacheFileHandle> > *>(aClosure);
456
457 nsRefPtr<CacheFileHandle> handle = aEntry->GetNewestHandle();
458 MOZ_ASSERT(handle);
459
460 if (!handle->IsDoomed()) {
461 array->AppendElement(handle);
462 }
463
464 return PL_DHASH_NEXT;
465 }
466
467 void
468 CacheFileHandles::GetActiveHandles(
469 nsTArray<nsRefPtr<CacheFileHandle> > *_retval)
470 {
471 MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
472 mTable.EnumerateEntries(&GetActiveHandlesEnum, _retval);
473 }
474
475 void
476 CacheFileHandles::ClearAll()
477 {
478 MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
479 mTable.Clear();
480 }
481
482 uint32_t
483 CacheFileHandles::HandleCount()
484 {
485 return mTable.Count();
486 }
487
488 #ifdef DEBUG_HANDLES
489 void
490 CacheFileHandles::Log(CacheFileHandlesEntry *entry)
491 {
492 LOG(("CacheFileHandles::Log() BEGIN [entry=%p]", entry));
493
494 nsTArray<nsRefPtr<CacheFileHandle> > array;
495 aEntry->GetHandles(array);
496
497 for (uint32_t i = 0; i < array.Length(); ++i) {
498 CacheFileHandle *handle = array[i];
499 handle->Log();
500 }
501
502 LOG(("CacheFileHandles::Log() END [entry=%p]", entry));
503 }
504 #endif
505
506 // Memory reporting
507
508 namespace { // anon
509
510 size_t
511 CollectHandlesMemory(CacheFileHandles::HandleHashKey* key,
512 mozilla::MallocSizeOf mallocSizeOf,
513 void *arg)
514 {
515 return key->SizeOfExcludingThis(mallocSizeOf);
516 }
517
518 } // anon
519
520 size_t
521 CacheFileHandles::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
522 {
523 MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
524
525 return mTable.SizeOfExcludingThis(&CollectHandlesMemory, mallocSizeOf);
526 }
527
528 // Events
529
530 class ShutdownEvent : public nsRunnable {
531 public:
532 ShutdownEvent(mozilla::Mutex *aLock, mozilla::CondVar *aCondVar)
533 : mLock(aLock)
534 , mCondVar(aCondVar)
535 {
536 MOZ_COUNT_CTOR(ShutdownEvent);
537 }
538
539 ~ShutdownEvent()
540 {
541 MOZ_COUNT_DTOR(ShutdownEvent);
542 }
543
544 NS_IMETHOD Run()
545 {
546 MutexAutoLock lock(*mLock);
547
548 CacheFileIOManager::gInstance->ShutdownInternal();
549
550 mCondVar->Notify();
551 return NS_OK;
552 }
553
554 protected:
555 mozilla::Mutex *mLock;
556 mozilla::CondVar *mCondVar;
557 };
558
559 class OpenFileEvent : public nsRunnable {
560 public:
561 OpenFileEvent(const nsACString &aKey,
562 uint32_t aFlags, bool aResultOnAnyThread,
563 CacheFileIOListener *aCallback)
564 : mFlags(aFlags)
565 , mResultOnAnyThread(aResultOnAnyThread)
566 , mCallback(aCallback)
567 , mRV(NS_ERROR_FAILURE)
568 , mKey(aKey)
569 {
570 MOZ_COUNT_CTOR(OpenFileEvent);
571
572 if (!aResultOnAnyThread) {
573 mTarget = static_cast<nsIEventTarget*>(NS_GetCurrentThread());
574 MOZ_ASSERT(mTarget);
575 }
576
577 mIOMan = CacheFileIOManager::gInstance;
578
579 MOZ_EVENT_TRACER_NAME_OBJECT(static_cast<nsIRunnable*>(this), aKey.BeginReading());
580 MOZ_EVENT_TRACER_WAIT(static_cast<nsIRunnable*>(this), "net::cache::open-background");
581 }
582
583 ~OpenFileEvent()
584 {
585 MOZ_COUNT_DTOR(OpenFileEvent);
586 }
587
588 NS_IMETHOD Run()
589 {
590 if (mResultOnAnyThread || mTarget) {
591 mRV = NS_OK;
592
593 if (!(mFlags & CacheFileIOManager::SPECIAL_FILE)) {
594 SHA1Sum sum;
595 sum.update(mKey.BeginReading(), mKey.Length());
596 sum.finish(mHash);
597 }
598
599 MOZ_EVENT_TRACER_EXEC(static_cast<nsIRunnable*>(this),
600 "net::cache::open-background");
601 if (NS_SUCCEEDED(mRV)) {
602 if (!mIOMan) {
603 mRV = NS_ERROR_NOT_INITIALIZED;
604 } else {
605 if (mFlags & CacheFileIOManager::SPECIAL_FILE) {
606 mRV = mIOMan->OpenSpecialFileInternal(mKey, mFlags,
607 getter_AddRefs(mHandle));
608 } else {
609 mRV = mIOMan->OpenFileInternal(&mHash, mKey, mFlags,
610 getter_AddRefs(mHandle));
611 }
612 mIOMan = nullptr;
613 if (mHandle) {
614 MOZ_EVENT_TRACER_NAME_OBJECT(mHandle.get(), mKey.get());
615 if (mHandle->Key().IsEmpty()) {
616 mHandle->Key() = mKey;
617 }
618 }
619 }
620 }
621 MOZ_EVENT_TRACER_DONE(static_cast<nsIRunnable*>(this), "net::cache::open-background");
622
623 MOZ_EVENT_TRACER_WAIT(static_cast<nsIRunnable*>(this), "net::cache::open-result");
624
625 if (mTarget) {
626 nsCOMPtr<nsIEventTarget> target;
627 mTarget.swap(target);
628 return target->Dispatch(this, nsIEventTarget::DISPATCH_NORMAL);
629 }
630 }
631
632 if (!mTarget) {
633 MOZ_EVENT_TRACER_EXEC(static_cast<nsIRunnable*>(this), "net::cache::open-result");
634 mCallback->OnFileOpened(mHandle, mRV);
635 MOZ_EVENT_TRACER_DONE(static_cast<nsIRunnable*>(this), "net::cache::open-result");
636 }
637
638 return NS_OK;
639 }
640
641 protected:
642 SHA1Sum::Hash mHash;
643 uint32_t mFlags;
644 bool mResultOnAnyThread;
645 nsCOMPtr<CacheFileIOListener> mCallback;
646 nsCOMPtr<nsIEventTarget> mTarget;
647 nsRefPtr<CacheFileIOManager> mIOMan;
648 nsRefPtr<CacheFileHandle> mHandle;
649 nsresult mRV;
650 nsCString mKey;
651 };
652
653 class ReadEvent : public nsRunnable {
654 public:
655 ReadEvent(CacheFileHandle *aHandle, int64_t aOffset, char *aBuf,
656 int32_t aCount, bool aResultOnAnyThread, CacheFileIOListener *aCallback)
657 : mHandle(aHandle)
658 , mOffset(aOffset)
659 , mBuf(aBuf)
660 , mCount(aCount)
661 , mResultOnAnyThread(aResultOnAnyThread)
662 , mCallback(aCallback)
663 , mRV(NS_ERROR_FAILURE)
664 {
665 MOZ_COUNT_CTOR(ReadEvent);
666
667 if (!aResultOnAnyThread) {
668 mTarget = static_cast<nsIEventTarget*>(NS_GetCurrentThread());
669 }
670
671 MOZ_EVENT_TRACER_NAME_OBJECT(static_cast<nsIRunnable*>(this), aHandle->Key().get());
672 MOZ_EVENT_TRACER_WAIT(static_cast<nsIRunnable*>(this), "net::cache::read-background");
673 }
674
675 ~ReadEvent()
676 {
677 MOZ_COUNT_DTOR(ReadEvent);
678 }
679
680 NS_IMETHOD Run()
681 {
682 if (mResultOnAnyThread || mTarget) {
683 MOZ_EVENT_TRACER_EXEC(static_cast<nsIRunnable*>(this), "net::cache::read-background");
684 if (mHandle->IsClosed()) {
685 mRV = NS_ERROR_NOT_INITIALIZED;
686 } else {
687 mRV = CacheFileIOManager::gInstance->ReadInternal(
688 mHandle, mOffset, mBuf, mCount);
689 }
690 MOZ_EVENT_TRACER_DONE(static_cast<nsIRunnable*>(this), "net::cache::read-background");
691
692 MOZ_EVENT_TRACER_WAIT(static_cast<nsIRunnable*>(this), "net::cache::read-result");
693
694 if (mTarget) {
695 nsCOMPtr<nsIEventTarget> target;
696 mTarget.swap(target);
697 return target->Dispatch(this, nsIEventTarget::DISPATCH_NORMAL);
698 }
699 }
700
701 if (!mTarget && mCallback) {
702 MOZ_EVENT_TRACER_EXEC(static_cast<nsIRunnable*>(this), "net::cache::read-result");
703 mCallback->OnDataRead(mHandle, mBuf, mRV);
704 MOZ_EVENT_TRACER_DONE(static_cast<nsIRunnable*>(this), "net::cache::read-result");
705 }
706
707 return NS_OK;
708 }
709
710 protected:
711 nsRefPtr<CacheFileHandle> mHandle;
712 int64_t mOffset;
713 char *mBuf;
714 int32_t mCount;
715 bool mResultOnAnyThread;
716 nsCOMPtr<CacheFileIOListener> mCallback;
717 nsCOMPtr<nsIEventTarget> mTarget;
718 nsresult mRV;
719 };
720
721 class WriteEvent : public nsRunnable {
722 public:
723 WriteEvent(CacheFileHandle *aHandle, int64_t aOffset, const char *aBuf,
724 int32_t aCount, bool aValidate, CacheFileIOListener *aCallback)
725 : mHandle(aHandle)
726 , mOffset(aOffset)
727 , mBuf(aBuf)
728 , mCount(aCount)
729 , mValidate(aValidate)
730 , mCallback(aCallback)
731 , mRV(NS_ERROR_FAILURE)
732 {
733 MOZ_COUNT_CTOR(WriteEvent);
734 mTarget = static_cast<nsIEventTarget*>(NS_GetCurrentThread());
735
736 MOZ_EVENT_TRACER_NAME_OBJECT(static_cast<nsIRunnable*>(this), aHandle->Key().get());
737 MOZ_EVENT_TRACER_WAIT(static_cast<nsIRunnable*>(this), "net::cache::write-background");
738 }
739
740 ~WriteEvent()
741 {
742 MOZ_COUNT_DTOR(WriteEvent);
743
744 if (!mCallback && mBuf) {
745 free(const_cast<char *>(mBuf));
746 }
747 }
748
749 NS_IMETHOD Run()
750 {
751 if (mTarget) {
752 MOZ_EVENT_TRACER_EXEC(static_cast<nsIRunnable*>(this), "net::cache::write-background");
753 if (mHandle->IsClosed()) {
754 mRV = NS_ERROR_NOT_INITIALIZED;
755 } else {
756 mRV = CacheFileIOManager::gInstance->WriteInternal(
757 mHandle, mOffset, mBuf, mCount, mValidate);
758 }
759 MOZ_EVENT_TRACER_DONE(static_cast<nsIRunnable*>(this), "net::cache::write-background");
760
761 MOZ_EVENT_TRACER_WAIT(static_cast<nsIRunnable*>(this), "net::cache::write-result");
762 nsCOMPtr<nsIEventTarget> target;
763 mTarget.swap(target);
764 target->Dispatch(this, nsIEventTarget::DISPATCH_NORMAL);
765 } else {
766 MOZ_EVENT_TRACER_EXEC(static_cast<nsIRunnable*>(this), "net::cache::write-result");
767 if (mCallback) {
768 mCallback->OnDataWritten(mHandle, mBuf, mRV);
769 } else {
770 free(const_cast<char *>(mBuf));
771 mBuf = nullptr;
772 }
773 MOZ_EVENT_TRACER_DONE(static_cast<nsIRunnable*>(this), "net::cache::write-result");
774 }
775 return NS_OK;
776 }
777
778 protected:
779 nsRefPtr<CacheFileHandle> mHandle;
780 int64_t mOffset;
781 const char *mBuf;
782 int32_t mCount;
783 bool mValidate;
784 nsCOMPtr<CacheFileIOListener> mCallback;
785 nsCOMPtr<nsIEventTarget> mTarget;
786 nsresult mRV;
787 };
788
789 class DoomFileEvent : public nsRunnable {
790 public:
791 DoomFileEvent(CacheFileHandle *aHandle,
792 CacheFileIOListener *aCallback)
793 : mCallback(aCallback)
794 , mHandle(aHandle)
795 , mRV(NS_ERROR_FAILURE)
796 {
797 MOZ_COUNT_CTOR(DoomFileEvent);
798 mTarget = static_cast<nsIEventTarget*>(NS_GetCurrentThread());
799
800 MOZ_EVENT_TRACER_NAME_OBJECT(static_cast<nsIRunnable*>(this), aHandle->Key().get());
801 MOZ_EVENT_TRACER_WAIT(static_cast<nsIRunnable*>(this), "net::cache::doom-background");
802 }
803
804 ~DoomFileEvent()
805 {
806 MOZ_COUNT_DTOR(DoomFileEvent);
807 }
808
809 NS_IMETHOD Run()
810 {
811 if (mTarget) {
812 MOZ_EVENT_TRACER_EXEC(static_cast<nsIRunnable*>(this), "net::cache::doom-background");
813 if (mHandle->IsClosed()) {
814 mRV = NS_ERROR_NOT_INITIALIZED;
815 } else {
816 mRV = CacheFileIOManager::gInstance->DoomFileInternal(mHandle);
817 }
818 MOZ_EVENT_TRACER_DONE(static_cast<nsIRunnable*>(this), "net::cache::doom-background");
819
820 MOZ_EVENT_TRACER_WAIT(static_cast<nsIRunnable*>(this), "net::cache::doom-result");
821 nsCOMPtr<nsIEventTarget> target;
822 mTarget.swap(target);
823 target->Dispatch(this, nsIEventTarget::DISPATCH_NORMAL);
824 } else {
825 MOZ_EVENT_TRACER_EXEC(static_cast<nsIRunnable*>(this), "net::cache::doom-result");
826 if (mCallback) {
827 mCallback->OnFileDoomed(mHandle, mRV);
828 }
829 MOZ_EVENT_TRACER_DONE(static_cast<nsIRunnable*>(this), "net::cache::doom-result");
830 }
831 return NS_OK;
832 }
833
834 protected:
835 nsCOMPtr<CacheFileIOListener> mCallback;
836 nsCOMPtr<nsIEventTarget> mTarget;
837 nsRefPtr<CacheFileHandle> mHandle;
838 nsresult mRV;
839 };
840
841 class DoomFileByKeyEvent : public nsRunnable {
842 public:
843 DoomFileByKeyEvent(const nsACString &aKey,
844 CacheFileIOListener *aCallback)
845 : mCallback(aCallback)
846 , mRV(NS_ERROR_FAILURE)
847 {
848 MOZ_COUNT_CTOR(DoomFileByKeyEvent);
849
850 SHA1Sum sum;
851 sum.update(aKey.BeginReading(), aKey.Length());
852 sum.finish(mHash);
853
854 mTarget = static_cast<nsIEventTarget*>(NS_GetCurrentThread());
855 mIOMan = CacheFileIOManager::gInstance;
856 MOZ_ASSERT(mTarget);
857 }
858
859 ~DoomFileByKeyEvent()
860 {
861 MOZ_COUNT_DTOR(DoomFileByKeyEvent);
862 }
863
864 NS_IMETHOD Run()
865 {
866 if (mTarget) {
867 if (!mIOMan) {
868 mRV = NS_ERROR_NOT_INITIALIZED;
869 } else {
870 mRV = mIOMan->DoomFileByKeyInternal(&mHash);
871 mIOMan = nullptr;
872 }
873
874 nsCOMPtr<nsIEventTarget> target;
875 mTarget.swap(target);
876 target->Dispatch(this, nsIEventTarget::DISPATCH_NORMAL);
877 } else {
878 if (mCallback) {
879 mCallback->OnFileDoomed(nullptr, mRV);
880 }
881 }
882 return NS_OK;
883 }
884
885 protected:
886 SHA1Sum::Hash mHash;
887 nsCOMPtr<CacheFileIOListener> mCallback;
888 nsCOMPtr<nsIEventTarget> mTarget;
889 nsRefPtr<CacheFileIOManager> mIOMan;
890 nsresult mRV;
891 };
892
893 class ReleaseNSPRHandleEvent : public nsRunnable {
894 public:
895 ReleaseNSPRHandleEvent(CacheFileHandle *aHandle)
896 : mHandle(aHandle)
897 {
898 MOZ_COUNT_CTOR(ReleaseNSPRHandleEvent);
899 }
900
901 ~ReleaseNSPRHandleEvent()
902 {
903 MOZ_COUNT_DTOR(ReleaseNSPRHandleEvent);
904 }
905
906 NS_IMETHOD Run()
907 {
908 if (mHandle->mFD && !mHandle->IsClosed()) {
909 CacheFileIOManager::gInstance->ReleaseNSPRHandleInternal(mHandle);
910 }
911
912 return NS_OK;
913 }
914
915 protected:
916 nsRefPtr<CacheFileHandle> mHandle;
917 };
918
919 class TruncateSeekSetEOFEvent : public nsRunnable {
920 public:
921 TruncateSeekSetEOFEvent(CacheFileHandle *aHandle, int64_t aTruncatePos,
922 int64_t aEOFPos, CacheFileIOListener *aCallback)
923 : mHandle(aHandle)
924 , mTruncatePos(aTruncatePos)
925 , mEOFPos(aEOFPos)
926 , mCallback(aCallback)
927 , mRV(NS_ERROR_FAILURE)
928 {
929 MOZ_COUNT_CTOR(TruncateSeekSetEOFEvent);
930 mTarget = static_cast<nsIEventTarget*>(NS_GetCurrentThread());
931 }
932
933 ~TruncateSeekSetEOFEvent()
934 {
935 MOZ_COUNT_DTOR(TruncateSeekSetEOFEvent);
936 }
937
938 NS_IMETHOD Run()
939 {
940 if (mTarget) {
941 if (mHandle->IsClosed()) {
942 mRV = NS_ERROR_NOT_INITIALIZED;
943 } else {
944 mRV = CacheFileIOManager::gInstance->TruncateSeekSetEOFInternal(
945 mHandle, mTruncatePos, mEOFPos);
946 }
947
948 nsCOMPtr<nsIEventTarget> target;
949 mTarget.swap(target);
950 target->Dispatch(this, nsIEventTarget::DISPATCH_NORMAL);
951 } else {
952 if (mCallback) {
953 mCallback->OnEOFSet(mHandle, mRV);
954 }
955 }
956 return NS_OK;
957 }
958
959 protected:
960 nsRefPtr<CacheFileHandle> mHandle;
961 int64_t mTruncatePos;
962 int64_t mEOFPos;
963 nsCOMPtr<CacheFileIOListener> mCallback;
964 nsCOMPtr<nsIEventTarget> mTarget;
965 nsresult mRV;
966 };
967
968 class RenameFileEvent : public nsRunnable {
969 public:
970 RenameFileEvent(CacheFileHandle *aHandle, const nsACString &aNewName,
971 CacheFileIOListener *aCallback)
972 : mHandle(aHandle)
973 , mNewName(aNewName)
974 , mCallback(aCallback)
975 , mRV(NS_ERROR_FAILURE)
976 {
977 MOZ_COUNT_CTOR(RenameFileEvent);
978 mTarget = static_cast<nsIEventTarget*>(NS_GetCurrentThread());
979 }
980
981 ~RenameFileEvent()
982 {
983 MOZ_COUNT_DTOR(RenameFileEvent);
984 }
985
986 NS_IMETHOD Run()
987 {
988 if (mTarget) {
989 if (mHandle->IsClosed()) {
990 mRV = NS_ERROR_NOT_INITIALIZED;
991 } else {
992 mRV = CacheFileIOManager::gInstance->RenameFileInternal(mHandle,
993 mNewName);
994 }
995
996 nsCOMPtr<nsIEventTarget> target;
997 mTarget.swap(target);
998 target->Dispatch(this, nsIEventTarget::DISPATCH_NORMAL);
999 } else {
1000 if (mCallback) {
1001 mCallback->OnFileRenamed(mHandle, mRV);
1002 }
1003 }
1004 return NS_OK;
1005 }
1006
1007 protected:
1008 nsRefPtr<CacheFileHandle> mHandle;
1009 nsCString mNewName;
1010 nsCOMPtr<CacheFileIOListener> mCallback;
1011 nsCOMPtr<nsIEventTarget> mTarget;
1012 nsresult mRV;
1013 };
1014
1015 class InitIndexEntryEvent : public nsRunnable {
1016 public:
1017 InitIndexEntryEvent(CacheFileHandle *aHandle, uint32_t aAppId,
1018 bool aAnonymous, bool aInBrowser)
1019 : mHandle(aHandle)
1020 , mAppId(aAppId)
1021 , mAnonymous(aAnonymous)
1022 , mInBrowser(aInBrowser)
1023 {
1024 MOZ_COUNT_CTOR(InitIndexEntryEvent);
1025 }
1026
1027 ~InitIndexEntryEvent()
1028 {
1029 MOZ_COUNT_DTOR(InitIndexEntryEvent);
1030 }
1031
1032 NS_IMETHOD Run()
1033 {
1034 if (mHandle->IsClosed() || mHandle->IsDoomed()) {
1035 return NS_OK;
1036 }
1037
1038 CacheIndex::InitEntry(mHandle->Hash(), mAppId, mAnonymous, mInBrowser);
1039
1040 // We cannot set the filesize before we init the entry. If we're opening
1041 // an existing entry file, frecency and expiration time will be set after
1042 // parsing the entry file, but we must set the filesize here since nobody is
1043 // going to set it if there is no write to the file.
1044 uint32_t sizeInK = mHandle->FileSizeInK();
1045 CacheIndex::UpdateEntry(mHandle->Hash(), nullptr, nullptr, &sizeInK);
1046
1047 return NS_OK;
1048 }
1049
1050 protected:
1051 nsRefPtr<CacheFileHandle> mHandle;
1052 uint32_t mAppId;
1053 bool mAnonymous;
1054 bool mInBrowser;
1055 };
1056
1057 class UpdateIndexEntryEvent : public nsRunnable {
1058 public:
1059 UpdateIndexEntryEvent(CacheFileHandle *aHandle, const uint32_t *aFrecency,
1060 const uint32_t *aExpirationTime)
1061 : mHandle(aHandle)
1062 , mHasFrecency(false)
1063 , mHasExpirationTime(false)
1064 {
1065 MOZ_COUNT_CTOR(UpdateIndexEntryEvent);
1066 if (aFrecency) {
1067 mHasFrecency = true;
1068 mFrecency = *aFrecency;
1069 }
1070 if (aExpirationTime) {
1071 mHasExpirationTime = true;
1072 mExpirationTime = *aExpirationTime;
1073 }
1074 }
1075
1076 ~UpdateIndexEntryEvent()
1077 {
1078 MOZ_COUNT_DTOR(UpdateIndexEntryEvent);
1079 }
1080
1081 NS_IMETHOD Run()
1082 {
1083 if (mHandle->IsClosed() || mHandle->IsDoomed()) {
1084 return NS_OK;
1085 }
1086
1087 CacheIndex::UpdateEntry(mHandle->Hash(),
1088 mHasFrecency ? &mFrecency : nullptr,
1089 mHasExpirationTime ? &mExpirationTime : nullptr,
1090 nullptr);
1091 return NS_OK;
1092 }
1093
1094 protected:
1095 nsRefPtr<CacheFileHandle> mHandle;
1096 bool mHasFrecency;
1097 bool mHasExpirationTime;
1098 uint32_t mFrecency;
1099 uint32_t mExpirationTime;
1100 };
1101
1102 class MetadataWriteScheduleEvent : public nsRunnable
1103 {
1104 public:
1105 enum EMode {
1106 SCHEDULE,
1107 UNSCHEDULE,
1108 SHUTDOWN
1109 } mMode;
1110
1111 nsRefPtr<CacheFile> mFile;
1112 nsRefPtr<CacheFileIOManager> mIOMan;
1113
1114 MetadataWriteScheduleEvent(CacheFileIOManager * aManager,
1115 CacheFile * aFile,
1116 EMode aMode)
1117 : mMode(aMode)
1118 , mFile(aFile)
1119 , mIOMan(aManager)
1120 { }
1121
1122 virtual ~MetadataWriteScheduleEvent() { }
1123
1124 NS_IMETHOD Run()
1125 {
1126 nsRefPtr<CacheFileIOManager> ioMan = CacheFileIOManager::gInstance;
1127 if (!ioMan) {
1128 NS_WARNING("CacheFileIOManager already gone in MetadataWriteScheduleEvent::Run()");
1129 return NS_OK;
1130 }
1131
1132 switch (mMode)
1133 {
1134 case SCHEDULE:
1135 ioMan->ScheduleMetadataWriteInternal(mFile);
1136 break;
1137 case UNSCHEDULE:
1138 ioMan->UnscheduleMetadataWriteInternal(mFile);
1139 break;
1140 case SHUTDOWN:
1141 ioMan->ShutdownMetadataWriteSchedulingInternal();
1142 break;
1143 }
1144 return NS_OK;
1145 }
1146 };
1147
1148 CacheFileIOManager * CacheFileIOManager::gInstance = nullptr;
1149
1150 NS_IMPL_ISUPPORTS(CacheFileIOManager, nsITimerCallback)
1151
1152 CacheFileIOManager::CacheFileIOManager()
1153 : mShuttingDown(false)
1154 , mTreeCreated(false)
1155 , mOverLimitEvicting(false)
1156 , mRemovingTrashDirs(false)
1157 {
1158 LOG(("CacheFileIOManager::CacheFileIOManager [this=%p]", this));
1159 MOZ_COUNT_CTOR(CacheFileIOManager);
1160 MOZ_ASSERT(!gInstance, "multiple CacheFileIOManager instances!");
1161 }
1162
1163 CacheFileIOManager::~CacheFileIOManager()
1164 {
1165 LOG(("CacheFileIOManager::~CacheFileIOManager [this=%p]", this));
1166 MOZ_COUNT_DTOR(CacheFileIOManager);
1167 }
1168
1169 // static
1170 nsresult
1171 CacheFileIOManager::Init()
1172 {
1173 LOG(("CacheFileIOManager::Init()"));
1174
1175 MOZ_ASSERT(NS_IsMainThread());
1176
1177 if (gInstance) {
1178 return NS_ERROR_ALREADY_INITIALIZED;
1179 }
1180
1181 nsRefPtr<CacheFileIOManager> ioMan = new CacheFileIOManager();
1182
1183 nsresult rv = ioMan->InitInternal();
1184 NS_ENSURE_SUCCESS(rv, rv);
1185
1186 ioMan.swap(gInstance);
1187 return NS_OK;
1188 }
1189
1190 nsresult
1191 CacheFileIOManager::InitInternal()
1192 {
1193 nsresult rv;
1194
1195 mIOThread = new CacheIOThread();
1196
1197 rv = mIOThread->Init();
1198 MOZ_ASSERT(NS_SUCCEEDED(rv), "Can't create background thread");
1199 NS_ENSURE_SUCCESS(rv, rv);
1200
1201 mStartTime = TimeStamp::NowLoRes();
1202
1203 return NS_OK;
1204 }
1205
1206 // static
1207 nsresult
1208 CacheFileIOManager::Shutdown()
1209 {
1210 LOG(("CacheFileIOManager::Shutdown() [gInstance=%p]", gInstance));
1211
1212 MOZ_ASSERT(NS_IsMainThread());
1213
1214 if (!gInstance) {
1215 return NS_ERROR_NOT_INITIALIZED;
1216 }
1217
1218 Telemetry::AutoTimer<Telemetry::NETWORK_DISK_CACHE_SHUTDOWN_V2> shutdownTimer;
1219
1220 CacheIndex::PreShutdown();
1221
1222 ShutdownMetadataWriteScheduling();
1223
1224 {
1225 mozilla::Mutex lock("CacheFileIOManager::Shutdown() lock");
1226 mozilla::CondVar condVar(lock, "CacheFileIOManager::Shutdown() condVar");
1227
1228 MutexAutoLock autoLock(lock);
1229 nsRefPtr<ShutdownEvent> ev = new ShutdownEvent(&lock, &condVar);
1230 DebugOnly<nsresult> rv;
1231 rv = gInstance->mIOThread->Dispatch(ev, CacheIOThread::CLOSE);
1232 MOZ_ASSERT(NS_SUCCEEDED(rv));
1233 condVar.Wait();
1234 }
1235
1236 MOZ_ASSERT(gInstance->mHandles.HandleCount() == 0);
1237 MOZ_ASSERT(gInstance->mHandlesByLastUsed.Length() == 0);
1238
1239 if (gInstance->mIOThread) {
1240 gInstance->mIOThread->Shutdown();
1241 }
1242
1243 CacheIndex::Shutdown();
1244
1245 if (CacheObserver::ClearCacheOnShutdown()) {
1246 gInstance->SyncRemoveAllCacheFiles();
1247 }
1248
1249 nsRefPtr<CacheFileIOManager> ioMan;
1250 ioMan.swap(gInstance);
1251
1252 return NS_OK;
1253 }
1254
1255 nsresult
1256 CacheFileIOManager::ShutdownInternal()
1257 {
1258 LOG(("CacheFileIOManager::ShutdownInternal() [this=%p]", this));
1259
1260 MOZ_ASSERT(mIOThread->IsCurrentThread());
1261
1262 // No new handles can be created after this flag is set
1263 mShuttingDown = true;
1264
1265 // close all handles and delete all associated files
1266 nsTArray<nsRefPtr<CacheFileHandle> > handles;
1267 mHandles.GetAllHandles(&handles);
1268 handles.AppendElements(mSpecialHandles);
1269
1270 for (uint32_t i=0 ; i<handles.Length() ; i++) {
1271 CacheFileHandle *h = handles[i];
1272 h->mClosed = true;
1273
1274 h->Log();
1275
1276 // Close file handle
1277 if (h->mFD) {
1278 ReleaseNSPRHandleInternal(h);
1279 }
1280
1281 // Remove file if entry is doomed or invalid
1282 if (h->mFileExists && (h->mIsDoomed || h->mInvalid)) {
1283 LOG(("CacheFileIOManager::ShutdownInternal() - Removing file from disk"));
1284 h->mFile->Remove(false);
1285 }
1286
1287 if (!h->IsSpecialFile() && !h->mIsDoomed &&
1288 (h->mInvalid || !h->mFileExists)) {
1289 CacheIndex::RemoveEntry(h->Hash());
1290 }
1291
1292 // Remove the handle from mHandles/mSpecialHandles
1293 if (h->IsSpecialFile()) {
1294 mSpecialHandles.RemoveElement(h);
1295 } else {
1296 mHandles.RemoveHandle(h);
1297 }
1298 }
1299
1300 // Assert the table is empty. When we are here, no new handles can be added
1301 // and handles will no longer remove them self from this table and we don't
1302 // want to keep invalid handles here. Also, there is no lookup after this
1303 // point to happen.
1304 MOZ_ASSERT(mHandles.HandleCount() == 0);
1305
1306 // Release trash directory enumerator
1307 if (mTrashDirEnumerator) {
1308 mTrashDirEnumerator->Close();
1309 mTrashDirEnumerator = nullptr;
1310 }
1311
1312 return NS_OK;
1313 }
1314
1315 // static
1316 nsresult
1317 CacheFileIOManager::OnProfile()
1318 {
1319 LOG(("CacheFileIOManager::OnProfile() [gInstance=%p]", gInstance));
1320
1321 nsRefPtr<CacheFileIOManager> ioMan = gInstance;
1322 if (!ioMan) {
1323 // CacheFileIOManager::Init() failed, probably could not create the IO
1324 // thread, just go with it...
1325 return NS_ERROR_NOT_INITIALIZED;
1326 }
1327
1328 nsresult rv;
1329
1330 nsCOMPtr<nsIFile> directory;
1331
1332 CacheObserver::ParentDirOverride(getter_AddRefs(directory));
1333
1334 #if defined(MOZ_WIDGET_ANDROID)
1335 char* cachePath = getenv("CACHE_DIRECTORY");
1336 if (!directory && cachePath && *cachePath) {
1337 rv = NS_NewNativeLocalFile(nsDependentCString(cachePath),
1338 true, getter_AddRefs(directory));
1339 }
1340 #endif
1341
1342 if (!directory) {
1343 rv = NS_GetSpecialDirectory(NS_APP_CACHE_PARENT_DIR,
1344 getter_AddRefs(directory));
1345 }
1346
1347 if (!directory) {
1348 rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR,
1349 getter_AddRefs(directory));
1350 }
1351
1352 if (directory) {
1353 rv = directory->Append(NS_LITERAL_STRING("cache2"));
1354 NS_ENSURE_SUCCESS(rv, rv);
1355 }
1356
1357 // All functions return a clone.
1358 ioMan->mCacheDirectory.swap(directory);
1359
1360 if (ioMan->mCacheDirectory) {
1361 CacheIndex::Init(ioMan->mCacheDirectory);
1362 }
1363
1364 return NS_OK;
1365 }
1366
1367 // static
1368 already_AddRefed<nsIEventTarget>
1369 CacheFileIOManager::IOTarget()
1370 {
1371 nsCOMPtr<nsIEventTarget> target;
1372 if (gInstance && gInstance->mIOThread) {
1373 target = gInstance->mIOThread->Target();
1374 }
1375
1376 return target.forget();
1377 }
1378
1379 // static
1380 already_AddRefed<CacheIOThread>
1381 CacheFileIOManager::IOThread()
1382 {
1383 nsRefPtr<CacheIOThread> thread;
1384 if (gInstance) {
1385 thread = gInstance->mIOThread;
1386 }
1387
1388 return thread.forget();
1389 }
1390
1391 // static
1392 bool
1393 CacheFileIOManager::IsOnIOThread()
1394 {
1395 nsRefPtr<CacheFileIOManager> ioMan = gInstance;
1396 if (ioMan && ioMan->mIOThread) {
1397 return ioMan->mIOThread->IsCurrentThread();
1398 }
1399
1400 return false;
1401 }
1402
1403 // static
1404 bool
1405 CacheFileIOManager::IsOnIOThreadOrCeased()
1406 {
1407 nsRefPtr<CacheFileIOManager> ioMan = gInstance;
1408 if (ioMan && ioMan->mIOThread) {
1409 return ioMan->mIOThread->IsCurrentThread();
1410 }
1411
1412 // Ceased...
1413 return true;
1414 }
1415
1416 // static
1417 bool
1418 CacheFileIOManager::IsShutdown()
1419 {
1420 if (!gInstance) {
1421 return true;
1422 }
1423 return gInstance->mShuttingDown;
1424 }
1425
1426 // static
1427 nsresult
1428 CacheFileIOManager::ScheduleMetadataWrite(CacheFile * aFile)
1429 {
1430 nsRefPtr<CacheFileIOManager> ioMan = gInstance;
1431 NS_ENSURE_TRUE(ioMan, NS_ERROR_NOT_INITIALIZED);
1432
1433 NS_ENSURE_TRUE(!ioMan->mShuttingDown, NS_ERROR_NOT_INITIALIZED);
1434
1435 nsRefPtr<MetadataWriteScheduleEvent> event = new MetadataWriteScheduleEvent(
1436 ioMan, aFile, MetadataWriteScheduleEvent::SCHEDULE);
1437 nsCOMPtr<nsIEventTarget> target = ioMan->IOTarget();
1438 NS_ENSURE_TRUE(target, NS_ERROR_UNEXPECTED);
1439 return target->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL);
1440 }
1441
1442 nsresult
1443 CacheFileIOManager::ScheduleMetadataWriteInternal(CacheFile * aFile)
1444 {
1445 MOZ_ASSERT(IsOnIOThreadOrCeased());
1446
1447 nsresult rv;
1448
1449 if (!mMetadataWritesTimer) {
1450 mMetadataWritesTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
1451 NS_ENSURE_SUCCESS(rv, rv);
1452
1453 rv = mMetadataWritesTimer->InitWithCallback(
1454 this, kMetadataWriteDelay, nsITimer::TYPE_ONE_SHOT);
1455 NS_ENSURE_SUCCESS(rv, rv);
1456 }
1457
1458 if (mScheduledMetadataWrites.IndexOf(aFile) !=
1459 mScheduledMetadataWrites.NoIndex) {
1460 return NS_OK;
1461 }
1462
1463 mScheduledMetadataWrites.AppendElement(aFile);
1464
1465 return NS_OK;
1466 }
1467
1468 // static
1469 nsresult
1470 CacheFileIOManager::UnscheduleMetadataWrite(CacheFile * aFile)
1471 {
1472 nsRefPtr<CacheFileIOManager> ioMan = gInstance;
1473 NS_ENSURE_TRUE(ioMan, NS_ERROR_NOT_INITIALIZED);
1474
1475 NS_ENSURE_TRUE(!ioMan->mShuttingDown, NS_ERROR_NOT_INITIALIZED);
1476
1477 nsRefPtr<MetadataWriteScheduleEvent> event = new MetadataWriteScheduleEvent(
1478 ioMan, aFile, MetadataWriteScheduleEvent::UNSCHEDULE);
1479 nsCOMPtr<nsIEventTarget> target = ioMan->IOTarget();
1480 NS_ENSURE_TRUE(target, NS_ERROR_UNEXPECTED);
1481 return target->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL);
1482 }
1483
1484 nsresult
1485 CacheFileIOManager::UnscheduleMetadataWriteInternal(CacheFile * aFile)
1486 {
1487 MOZ_ASSERT(IsOnIOThreadOrCeased());
1488
1489 mScheduledMetadataWrites.RemoveElement(aFile);
1490
1491 if (mScheduledMetadataWrites.Length() == 0 &&
1492 mMetadataWritesTimer) {
1493 mMetadataWritesTimer->Cancel();
1494 mMetadataWritesTimer = nullptr;
1495 }
1496
1497 return NS_OK;
1498 }
1499
1500 // static
1501 nsresult
1502 CacheFileIOManager::ShutdownMetadataWriteScheduling()
1503 {
1504 nsRefPtr<CacheFileIOManager> ioMan = gInstance;
1505 NS_ENSURE_TRUE(ioMan, NS_ERROR_NOT_INITIALIZED);
1506
1507 nsRefPtr<MetadataWriteScheduleEvent> event = new MetadataWriteScheduleEvent(
1508 ioMan, nullptr, MetadataWriteScheduleEvent::SHUTDOWN);
1509 nsCOMPtr<nsIEventTarget> target = ioMan->IOTarget();
1510 NS_ENSURE_TRUE(target, NS_ERROR_UNEXPECTED);
1511 return target->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL);
1512 }
1513
1514 nsresult
1515 CacheFileIOManager::ShutdownMetadataWriteSchedulingInternal()
1516 {
1517 MOZ_ASSERT(IsOnIOThreadOrCeased());
1518
1519 nsTArray<nsRefPtr<CacheFile> > files;
1520 files.SwapElements(mScheduledMetadataWrites);
1521 for (uint32_t i = 0; i < files.Length(); ++i) {
1522 CacheFile * file = files[i];
1523 file->WriteMetadataIfNeeded();
1524 }
1525
1526 if (mMetadataWritesTimer) {
1527 mMetadataWritesTimer->Cancel();
1528 mMetadataWritesTimer = nullptr;
1529 }
1530
1531 return NS_OK;
1532 }
1533
1534 NS_IMETHODIMP
1535 CacheFileIOManager::Notify(nsITimer * aTimer)
1536 {
1537 MOZ_ASSERT(IsOnIOThreadOrCeased());
1538 MOZ_ASSERT(mMetadataWritesTimer == aTimer);
1539
1540 mMetadataWritesTimer = nullptr;
1541
1542 nsTArray<nsRefPtr<CacheFile> > files;
1543 files.SwapElements(mScheduledMetadataWrites);
1544 for (uint32_t i = 0; i < files.Length(); ++i) {
1545 CacheFile * file = files[i];
1546 file->WriteMetadataIfNeeded();
1547 }
1548
1549 return NS_OK;
1550 }
1551
1552 // static
1553 nsresult
1554 CacheFileIOManager::OpenFile(const nsACString &aKey,
1555 uint32_t aFlags, bool aResultOnAnyThread,
1556 CacheFileIOListener *aCallback)
1557 {
1558 LOG(("CacheFileIOManager::OpenFile() [key=%s, flags=%d, listener=%p]",
1559 PromiseFlatCString(aKey).get(), aFlags, aCallback));
1560
1561 nsresult rv;
1562 nsRefPtr<CacheFileIOManager> ioMan = gInstance;
1563
1564 if (!ioMan) {
1565 return NS_ERROR_NOT_INITIALIZED;
1566 }
1567
1568 bool priority = aFlags & CacheFileIOManager::PRIORITY;
1569 nsRefPtr<OpenFileEvent> ev = new OpenFileEvent(aKey, aFlags, aResultOnAnyThread, aCallback);
1570 rv = ioMan->mIOThread->Dispatch(ev, priority
1571 ? CacheIOThread::OPEN_PRIORITY
1572 : CacheIOThread::OPEN);
1573 NS_ENSURE_SUCCESS(rv, rv);
1574
1575 return NS_OK;
1576 }
1577
1578 nsresult
1579 CacheFileIOManager::OpenFileInternal(const SHA1Sum::Hash *aHash,
1580 const nsACString &aKey,
1581 uint32_t aFlags,
1582 CacheFileHandle **_retval)
1583 {
1584 LOG(("CacheFileIOManager::OpenFileInternal() [hash=%08x%08x%08x%08x%08x, "
1585 "key=%s, flags=%d]", LOGSHA1(aHash), PromiseFlatCString(aKey).get(),
1586 aFlags));
1587
1588 MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
1589
1590 nsresult rv;
1591
1592 if (mShuttingDown) {
1593 return NS_ERROR_NOT_INITIALIZED;
1594 }
1595
1596 if (!mTreeCreated) {
1597 rv = CreateCacheTree();
1598 if (NS_FAILED(rv)) return rv;
1599 }
1600
1601 nsCOMPtr<nsIFile> file;
1602 rv = GetFile(aHash, getter_AddRefs(file));
1603 NS_ENSURE_SUCCESS(rv, rv);
1604
1605 nsRefPtr<CacheFileHandle> handle;
1606 mHandles.GetHandle(aHash, false, getter_AddRefs(handle));
1607
1608 if ((aFlags & (OPEN | CREATE | CREATE_NEW)) == CREATE_NEW) {
1609 if (handle) {
1610 rv = DoomFileInternal(handle);
1611 NS_ENSURE_SUCCESS(rv, rv);
1612 handle = nullptr;
1613 }
1614
1615 rv = mHandles.NewHandle(aHash, aFlags & PRIORITY, getter_AddRefs(handle));
1616 NS_ENSURE_SUCCESS(rv, rv);
1617
1618 bool exists;
1619 rv = file->Exists(&exists);
1620 NS_ENSURE_SUCCESS(rv, rv);
1621
1622 if (exists) {
1623 CacheIndex::RemoveEntry(aHash);
1624
1625 LOG(("CacheFileIOManager::OpenFileInternal() - Removing old file from "
1626 "disk"));
1627 rv = file->Remove(false);
1628 if (NS_FAILED(rv)) {
1629 NS_WARNING("Cannot remove old entry from the disk");
1630 LOG(("CacheFileIOManager::OpenFileInternal() - Removing old file failed"
1631 ". [rv=0x%08x]", rv));
1632 }
1633 }
1634
1635 CacheIndex::AddEntry(aHash);
1636 handle->mFile.swap(file);
1637 handle->mFileSize = 0;
1638 }
1639
1640 if (handle) {
1641 handle.swap(*_retval);
1642 return NS_OK;
1643 }
1644
1645 bool exists;
1646 rv = file->Exists(&exists);
1647 NS_ENSURE_SUCCESS(rv, rv);
1648
1649 if (exists && mContextEvictor) {
1650 if (mContextEvictor->ContextsCount() == 0) {
1651 mContextEvictor = nullptr;
1652 } else {
1653 bool wasEvicted = false;
1654 mContextEvictor->WasEvicted(aKey, file, &wasEvicted);
1655 if (wasEvicted) {
1656 LOG(("CacheFileIOManager::OpenFileInternal() - Removing file since the "
1657 "entry was evicted by EvictByContext()"));
1658 exists = false;
1659 file->Remove(false);
1660 CacheIndex::RemoveEntry(aHash);
1661 }
1662 }
1663 }
1664
1665 if (!exists && (aFlags & (OPEN | CREATE | CREATE_NEW)) == OPEN) {
1666 return NS_ERROR_NOT_AVAILABLE;
1667 }
1668
1669 rv = mHandles.NewHandle(aHash, aFlags & PRIORITY, getter_AddRefs(handle));
1670 NS_ENSURE_SUCCESS(rv, rv);
1671
1672 if (exists) {
1673 rv = file->GetFileSize(&handle->mFileSize);
1674 NS_ENSURE_SUCCESS(rv, rv);
1675
1676 handle->mFileExists = true;
1677
1678 CacheIndex::EnsureEntryExists(aHash);
1679 } else {
1680 handle->mFileSize = 0;
1681
1682 CacheIndex::AddEntry(aHash);
1683 }
1684
1685 handle->mFile.swap(file);
1686 handle.swap(*_retval);
1687 return NS_OK;
1688 }
1689
1690 nsresult
1691 CacheFileIOManager::OpenSpecialFileInternal(const nsACString &aKey,
1692 uint32_t aFlags,
1693 CacheFileHandle **_retval)
1694 {
1695 LOG(("CacheFileIOManager::OpenSpecialFileInternal() [key=%s, flags=%d]",
1696 PromiseFlatCString(aKey).get(), aFlags));
1697
1698 MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
1699
1700 nsresult rv;
1701
1702 if (mShuttingDown) {
1703 return NS_ERROR_NOT_INITIALIZED;
1704 }
1705
1706 if (!mTreeCreated) {
1707 rv = CreateCacheTree();
1708 if (NS_FAILED(rv)) return rv;
1709 }
1710
1711 nsCOMPtr<nsIFile> file;
1712 rv = GetSpecialFile(aKey, getter_AddRefs(file));
1713 NS_ENSURE_SUCCESS(rv, rv);
1714
1715 nsRefPtr<CacheFileHandle> handle;
1716 for (uint32_t i = 0 ; i < mSpecialHandles.Length() ; i++) {
1717 if (!mSpecialHandles[i]->IsDoomed() && mSpecialHandles[i]->Key() == aKey) {
1718 handle = mSpecialHandles[i];
1719 break;
1720 }
1721 }
1722
1723 if ((aFlags & (OPEN | CREATE | CREATE_NEW)) == CREATE_NEW) {
1724 if (handle) {
1725 rv = DoomFileInternal(handle);
1726 NS_ENSURE_SUCCESS(rv, rv);
1727 handle = nullptr;
1728 }
1729
1730 handle = new CacheFileHandle(aKey, aFlags & PRIORITY);
1731 mSpecialHandles.AppendElement(handle);
1732
1733 bool exists;
1734 rv = file->Exists(&exists);
1735 NS_ENSURE_SUCCESS(rv, rv);
1736
1737 if (exists) {
1738 LOG(("CacheFileIOManager::OpenSpecialFileInternal() - Removing file from "
1739 "disk"));
1740 rv = file->Remove(false);
1741 if (NS_FAILED(rv)) {
1742 NS_WARNING("Cannot remove old entry from the disk");
1743 LOG(("CacheFileIOManager::OpenSpecialFileInternal() - Removing file "
1744 "failed. [rv=0x%08x]", rv));
1745 }
1746 }
1747
1748 handle->mFile.swap(file);
1749 handle->mFileSize = 0;
1750 }
1751
1752 if (handle) {
1753 handle.swap(*_retval);
1754 return NS_OK;
1755 }
1756
1757 bool exists;
1758 rv = file->Exists(&exists);
1759 NS_ENSURE_SUCCESS(rv, rv);
1760
1761 if (!exists && (aFlags & (OPEN | CREATE | CREATE_NEW)) == OPEN) {
1762 return NS_ERROR_NOT_AVAILABLE;
1763 }
1764
1765 handle = new CacheFileHandle(aKey, aFlags & PRIORITY);
1766 mSpecialHandles.AppendElement(handle);
1767
1768 if (exists) {
1769 rv = file->GetFileSize(&handle->mFileSize);
1770 NS_ENSURE_SUCCESS(rv, rv);
1771
1772 handle->mFileExists = true;
1773 } else {
1774 handle->mFileSize = 0;
1775 }
1776
1777 handle->mFile.swap(file);
1778 handle.swap(*_retval);
1779 return NS_OK;
1780 }
1781
1782 nsresult
1783 CacheFileIOManager::CloseHandleInternal(CacheFileHandle *aHandle)
1784 {
1785 LOG(("CacheFileIOManager::CloseHandleInternal() [handle=%p]", aHandle));
1786 aHandle->Log();
1787
1788 MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
1789
1790 // Close file handle
1791 if (aHandle->mFD) {
1792 ReleaseNSPRHandleInternal(aHandle);
1793 }
1794
1795 // Delete the file if the entry was doomed or invalid
1796 if (aHandle->mIsDoomed || aHandle->mInvalid) {
1797 LOG(("CacheFileIOManager::CloseHandleInternal() - Removing file from "
1798 "disk"));
1799 aHandle->mFile->Remove(false);
1800 }
1801
1802 if (!aHandle->IsSpecialFile() && !aHandle->mIsDoomed &&
1803 (aHandle->mInvalid || !aHandle->mFileExists)) {
1804 CacheIndex::RemoveEntry(aHandle->Hash());
1805 }
1806
1807 // Don't remove handles after shutdown
1808 if (!mShuttingDown) {
1809 if (aHandle->IsSpecialFile()) {
1810 mSpecialHandles.RemoveElement(aHandle);
1811 } else {
1812 mHandles.RemoveHandle(aHandle);
1813 }
1814 }
1815
1816 return NS_OK;
1817 }
1818
1819 // static
1820 nsresult
1821 CacheFileIOManager::Read(CacheFileHandle *aHandle, int64_t aOffset,
1822 char *aBuf, int32_t aCount, bool aResultOnAnyThread,
1823 CacheFileIOListener *aCallback)
1824 {
1825 LOG(("CacheFileIOManager::Read() [handle=%p, offset=%lld, count=%d, "
1826 "listener=%p]", aHandle, aOffset, aCount, aCallback));
1827
1828 nsresult rv;
1829 nsRefPtr<CacheFileIOManager> ioMan = gInstance;
1830
1831 if (aHandle->IsClosed() || !ioMan) {
1832 return NS_ERROR_NOT_INITIALIZED;
1833 }
1834
1835 nsRefPtr<ReadEvent> ev = new ReadEvent(aHandle, aOffset, aBuf, aCount,
1836 aResultOnAnyThread, aCallback);
1837 rv = ioMan->mIOThread->Dispatch(ev, aHandle->IsPriority()
1838 ? CacheIOThread::READ_PRIORITY
1839 : CacheIOThread::READ);
1840 NS_ENSURE_SUCCESS(rv, rv);
1841
1842 return NS_OK;
1843 }
1844
1845 nsresult
1846 CacheFileIOManager::ReadInternal(CacheFileHandle *aHandle, int64_t aOffset,
1847 char *aBuf, int32_t aCount)
1848 {
1849 LOG(("CacheFileIOManager::ReadInternal() [handle=%p, offset=%lld, count=%d]",
1850 aHandle, aOffset, aCount));
1851
1852 nsresult rv;
1853
1854 if (!aHandle->mFileExists) {
1855 NS_WARNING("Trying to read from non-existent file");
1856 return NS_ERROR_NOT_AVAILABLE;
1857 }
1858
1859 if (!aHandle->mFD) {
1860 rv = OpenNSPRHandle(aHandle);
1861 NS_ENSURE_SUCCESS(rv, rv);
1862 } else {
1863 NSPRHandleUsed(aHandle);
1864 }
1865
1866 // Check again, OpenNSPRHandle could figure out the file was gone.
1867 if (!aHandle->mFileExists) {
1868 NS_WARNING("Trying to read from non-existent file");
1869 return NS_ERROR_NOT_AVAILABLE;
1870 }
1871
1872 int64_t offset = PR_Seek64(aHandle->mFD, aOffset, PR_SEEK_SET);
1873 if (offset == -1) {
1874 return NS_ERROR_FAILURE;
1875 }
1876
1877 int32_t bytesRead = PR_Read(aHandle->mFD, aBuf, aCount);
1878 if (bytesRead != aCount) {
1879 return NS_ERROR_FAILURE;
1880 }
1881
1882 return NS_OK;
1883 }
1884
1885 // static
1886 nsresult
1887 CacheFileIOManager::Write(CacheFileHandle *aHandle, int64_t aOffset,
1888 const char *aBuf, int32_t aCount, bool aValidate,
1889 CacheFileIOListener *aCallback)
1890 {
1891 LOG(("CacheFileIOManager::Write() [handle=%p, offset=%lld, count=%d, "
1892 "validate=%d, listener=%p]", aHandle, aOffset, aCount, aValidate,
1893 aCallback));
1894
1895 nsresult rv;
1896 nsRefPtr<CacheFileIOManager> ioMan = gInstance;
1897
1898 if (aHandle->IsClosed() || !ioMan) {
1899 return NS_ERROR_NOT_INITIALIZED;
1900 }
1901
1902 nsRefPtr<WriteEvent> ev = new WriteEvent(aHandle, aOffset, aBuf, aCount,
1903 aValidate, aCallback);
1904 rv = ioMan->mIOThread->Dispatch(ev, CacheIOThread::WRITE);
1905 NS_ENSURE_SUCCESS(rv, rv);
1906
1907 return NS_OK;
1908 }
1909
1910 nsresult
1911 CacheFileIOManager::WriteInternal(CacheFileHandle *aHandle, int64_t aOffset,
1912 const char *aBuf, int32_t aCount,
1913 bool aValidate)
1914 {
1915 LOG(("CacheFileIOManager::WriteInternal() [handle=%p, offset=%lld, count=%d, "
1916 "validate=%d]", aHandle, aOffset, aCount, aValidate));
1917
1918 nsresult rv;
1919
1920 if (!aHandle->mFileExists) {
1921 rv = CreateFile(aHandle);
1922 NS_ENSURE_SUCCESS(rv, rv);
1923 }
1924
1925 if (!aHandle->mFD) {
1926 rv = OpenNSPRHandle(aHandle);
1927 NS_ENSURE_SUCCESS(rv, rv);
1928 } else {
1929 NSPRHandleUsed(aHandle);
1930 }
1931
1932 // Check again, OpenNSPRHandle could figure out the file was gone.
1933 if (!aHandle->mFileExists) {
1934 return NS_ERROR_NOT_AVAILABLE;
1935 }
1936
1937 // Write invalidates the entry by default
1938 aHandle->mInvalid = true;
1939
1940 int64_t offset = PR_Seek64(aHandle->mFD, aOffset, PR_SEEK_SET);
1941 if (offset == -1) {
1942 return NS_ERROR_FAILURE;
1943 }
1944
1945 int32_t bytesWritten = PR_Write(aHandle->mFD, aBuf, aCount);
1946
1947 if (bytesWritten != -1 && aHandle->mFileSize < aOffset+bytesWritten) {
1948 aHandle->mFileSize = aOffset+bytesWritten;
1949
1950 if (!aHandle->IsDoomed() && !aHandle->IsSpecialFile()) {
1951 uint32_t size = aHandle->FileSizeInK();
1952 CacheIndex::UpdateEntry(aHandle->Hash(), nullptr, nullptr, &size);
1953 EvictIfOverLimitInternal();
1954 }
1955 }
1956
1957 if (bytesWritten != aCount) {
1958 return NS_ERROR_FAILURE;
1959 }
1960
1961 // Write was successful and this write validates the entry (i.e. metadata)
1962 if (aValidate) {
1963 aHandle->mInvalid = false;
1964 }
1965
1966 return NS_OK;
1967 }
1968
1969 // static
1970 nsresult
1971 CacheFileIOManager::DoomFile(CacheFileHandle *aHandle,
1972 CacheFileIOListener *aCallback)
1973 {
1974 LOG(("CacheFileIOManager::DoomFile() [handle=%p, listener=%p]",
1975 aHandle, aCallback));
1976
1977 nsresult rv;
1978 nsRefPtr<CacheFileIOManager> ioMan = gInstance;
1979
1980 if (aHandle->IsClosed() || !ioMan) {
1981 return NS_ERROR_NOT_INITIALIZED;
1982 }
1983
1984 nsRefPtr<DoomFileEvent> ev = new DoomFileEvent(aHandle, aCallback);
1985 rv = ioMan->mIOThread->Dispatch(ev, aHandle->IsPriority()
1986 ? CacheIOThread::OPEN_PRIORITY
1987 : CacheIOThread::OPEN);
1988 NS_ENSURE_SUCCESS(rv, rv);
1989
1990 return NS_OK;
1991 }
1992
1993 nsresult
1994 CacheFileIOManager::DoomFileInternal(CacheFileHandle *aHandle)
1995 {
1996 LOG(("CacheFileIOManager::DoomFileInternal() [handle=%p]", aHandle));
1997 aHandle->Log();
1998
1999 nsresult rv;
2000
2001 if (aHandle->IsDoomed()) {
2002 return NS_OK;
2003 }
2004
2005 if (aHandle->mFileExists) {
2006 // we need to move the current file to the doomed directory
2007 if (aHandle->mFD) {
2008 ReleaseNSPRHandleInternal(aHandle);
2009 }
2010
2011 // find unused filename
2012 nsCOMPtr<nsIFile> file;
2013 rv = GetDoomedFile(getter_AddRefs(file));
2014 NS_ENSURE_SUCCESS(rv, rv);
2015
2016 nsCOMPtr<nsIFile> parentDir;
2017 rv = file->GetParent(getter_AddRefs(parentDir));
2018 NS_ENSURE_SUCCESS(rv, rv);
2019
2020 nsAutoCString leafName;
2021 rv = file->GetNativeLeafName(leafName);
2022 NS_ENSURE_SUCCESS(rv, rv);
2023
2024 rv = aHandle->mFile->MoveToNative(parentDir, leafName);
2025 if (NS_ERROR_FILE_NOT_FOUND == rv || NS_ERROR_FILE_TARGET_DOES_NOT_EXIST == rv) {
2026 LOG((" file already removed under our hands"));
2027 aHandle->mFileExists = false;
2028 rv = NS_OK;
2029 } else {
2030 NS_ENSURE_SUCCESS(rv, rv);
2031 aHandle->mFile.swap(file);
2032 }
2033 }
2034
2035 if (!aHandle->IsSpecialFile()) {
2036 CacheIndex::RemoveEntry(aHandle->Hash());
2037 }
2038
2039 aHandle->mIsDoomed = true;
2040
2041 if (!aHandle->IsSpecialFile()) {
2042 nsRefPtr<CacheStorageService> storageService = CacheStorageService::Self();
2043 if (storageService) {
2044 nsAutoCString idExtension, url;
2045 nsCOMPtr<nsILoadContextInfo> info =
2046 CacheFileUtils::ParseKey(aHandle->Key(), &idExtension, &url);
2047 MOZ_ASSERT(info);
2048 if (info) {
2049 storageService->CacheFileDoomed(info, idExtension, url);
2050 }
2051 }
2052 }
2053
2054 return NS_OK;
2055 }
2056
2057 // static
2058 nsresult
2059 CacheFileIOManager::DoomFileByKey(const nsACString &aKey,
2060 CacheFileIOListener *aCallback)
2061 {
2062 LOG(("CacheFileIOManager::DoomFileByKey() [key=%s, listener=%p]",
2063 PromiseFlatCString(aKey).get(), aCallback));
2064
2065 nsresult rv;
2066 nsRefPtr<CacheFileIOManager> ioMan = gInstance;
2067
2068 if (!ioMan) {
2069 return NS_ERROR_NOT_INITIALIZED;
2070 }
2071
2072 nsRefPtr<DoomFileByKeyEvent> ev = new DoomFileByKeyEvent(aKey, aCallback);
2073 rv = ioMan->mIOThread->DispatchAfterPendingOpens(ev);
2074 NS_ENSURE_SUCCESS(rv, rv);
2075
2076 return NS_OK;
2077 }
2078
2079 nsresult
2080 CacheFileIOManager::DoomFileByKeyInternal(const SHA1Sum::Hash *aHash)
2081 {
2082 LOG(("CacheFileIOManager::DoomFileByKeyInternal() [hash=%08x%08x%08x%08x%08x]"
2083 , LOGSHA1(aHash)));
2084
2085 MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
2086
2087 nsresult rv;
2088
2089 if (mShuttingDown) {
2090 return NS_ERROR_NOT_INITIALIZED;
2091 }
2092
2093 if (!mCacheDirectory) {
2094 return NS_ERROR_FILE_INVALID_PATH;
2095 }
2096
2097 // Find active handle
2098 nsRefPtr<CacheFileHandle> handle;
2099 mHandles.GetHandle(aHash, true, getter_AddRefs(handle));
2100
2101 if (handle) {
2102 handle->Log();
2103
2104 if (handle->IsDoomed()) {
2105 return NS_OK;
2106 }
2107
2108 return DoomFileInternal(handle);
2109 }
2110
2111 // There is no handle for this file, delete the file if exists
2112 nsCOMPtr<nsIFile> file;
2113 rv = GetFile(aHash, getter_AddRefs(file));
2114 NS_ENSURE_SUCCESS(rv, rv);
2115
2116 bool exists;
2117 rv = file->Exists(&exists);
2118 NS_ENSURE_SUCCESS(rv, rv);
2119
2120 if (!exists) {
2121 return NS_ERROR_NOT_AVAILABLE;
2122 }
2123
2124 LOG(("CacheFileIOManager::DoomFileByKeyInternal() - Removing file from "
2125 "disk"));
2126 rv = file->Remove(false);
2127 if (NS_FAILED(rv)) {
2128 NS_WARNING("Cannot remove old entry from the disk");
2129 LOG(("CacheFileIOManager::DoomFileByKeyInternal() - Removing file failed. "
2130 "[rv=0x%08x]", rv));
2131 }
2132
2133 CacheIndex::RemoveEntry(aHash);
2134
2135 return NS_OK;
2136 }
2137
2138 // static
2139 nsresult
2140 CacheFileIOManager::ReleaseNSPRHandle(CacheFileHandle *aHandle)
2141 {
2142 LOG(("CacheFileIOManager::ReleaseNSPRHandle() [handle=%p]", aHandle));
2143
2144 nsresult rv;
2145 nsRefPtr<CacheFileIOManager> ioMan = gInstance;
2146
2147 if (aHandle->IsClosed() || !ioMan) {
2148 return NS_ERROR_NOT_INITIALIZED;
2149 }
2150
2151 nsRefPtr<ReleaseNSPRHandleEvent> ev = new ReleaseNSPRHandleEvent(aHandle);
2152 rv = ioMan->mIOThread->Dispatch(ev, CacheIOThread::CLOSE);
2153 NS_ENSURE_SUCCESS(rv, rv);
2154
2155 return NS_OK;
2156 }
2157
2158 nsresult
2159 CacheFileIOManager::ReleaseNSPRHandleInternal(CacheFileHandle *aHandle)
2160 {
2161 LOG(("CacheFileIOManager::ReleaseNSPRHandleInternal() [handle=%p]", aHandle));
2162
2163 MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
2164 MOZ_ASSERT(aHandle->mFD);
2165
2166 DebugOnly<bool> found;
2167 found = mHandlesByLastUsed.RemoveElement(aHandle);
2168 MOZ_ASSERT(found);
2169
2170 PR_Close(aHandle->mFD);
2171 aHandle->mFD = nullptr;
2172
2173 return NS_OK;
2174 }
2175
2176 // static
2177 nsresult
2178 CacheFileIOManager::TruncateSeekSetEOF(CacheFileHandle *aHandle,
2179 int64_t aTruncatePos, int64_t aEOFPos,
2180 CacheFileIOListener *aCallback)
2181 {
2182 LOG(("CacheFileIOManager::TruncateSeekSetEOF() [handle=%p, truncatePos=%lld, "
2183 "EOFPos=%lld, listener=%p]", aHandle, aTruncatePos, aEOFPos, aCallback));
2184
2185 nsresult rv;
2186 nsRefPtr<CacheFileIOManager> ioMan = gInstance;
2187
2188 if (aHandle->IsClosed() || !ioMan) {
2189 return NS_ERROR_NOT_INITIALIZED;
2190 }
2191
2192 nsRefPtr<TruncateSeekSetEOFEvent> ev = new TruncateSeekSetEOFEvent(
2193 aHandle, aTruncatePos, aEOFPos,
2194 aCallback);
2195 rv = ioMan->mIOThread->Dispatch(ev, CacheIOThread::WRITE);
2196 NS_ENSURE_SUCCESS(rv, rv);
2197
2198 return NS_OK;
2199 }
2200
2201 // static
2202 void CacheFileIOManager::GetCacheDirectory(nsIFile** result)
2203 {
2204 *result = nullptr;
2205
2206 nsRefPtr<CacheFileIOManager> ioMan = gInstance;
2207 if (!ioMan) {
2208 return;
2209 }
2210
2211 nsCOMPtr<nsIFile> file = ioMan->mCacheDirectory;
2212 file.forget(result);
2213 }
2214
2215 static nsresult
2216 TruncFile(PRFileDesc *aFD, uint32_t aEOF)
2217 {
2218 #if defined(XP_UNIX)
2219 if (ftruncate(PR_FileDesc2NativeHandle(aFD), aEOF) != 0) {
2220 NS_ERROR("ftruncate failed");
2221 return NS_ERROR_FAILURE;
2222 }
2223 #elif defined(XP_WIN)
2224 int32_t cnt = PR_Seek(aFD, aEOF, PR_SEEK_SET);
2225 if (cnt == -1) {
2226 return NS_ERROR_FAILURE;
2227 }
2228 if (!SetEndOfFile((HANDLE) PR_FileDesc2NativeHandle(aFD))) {
2229 NS_ERROR("SetEndOfFile failed");
2230 return NS_ERROR_FAILURE;
2231 }
2232 #else
2233 MOZ_ASSERT(false, "Not implemented!");
2234 #endif
2235
2236 return NS_OK;
2237 }
2238
2239 nsresult
2240 CacheFileIOManager::TruncateSeekSetEOFInternal(CacheFileHandle *aHandle,
2241 int64_t aTruncatePos,
2242 int64_t aEOFPos)
2243 {
2244 LOG(("CacheFileIOManager::TruncateSeekSetEOFInternal() [handle=%p, "
2245 "truncatePos=%lld, EOFPos=%lld]", aHandle, aTruncatePos, aEOFPos));
2246
2247 nsresult rv;
2248
2249 if (!aHandle->mFileExists) {
2250 rv = CreateFile(aHandle);
2251 NS_ENSURE_SUCCESS(rv, rv);
2252 }
2253
2254 if (!aHandle->mFD) {
2255 rv = OpenNSPRHandle(aHandle);
2256 NS_ENSURE_SUCCESS(rv, rv);
2257 } else {
2258 NSPRHandleUsed(aHandle);
2259 }
2260
2261 // Check again, OpenNSPRHandle could figure out the file was gone.
2262 if (!aHandle->mFileExists) {
2263 return NS_ERROR_NOT_AVAILABLE;
2264 }
2265
2266 // This operation always invalidates the entry
2267 aHandle->mInvalid = true;
2268
2269 rv = TruncFile(aHandle->mFD, static_cast<uint32_t>(aTruncatePos));
2270 NS_ENSURE_SUCCESS(rv, rv);
2271
2272 rv = TruncFile(aHandle->mFD, static_cast<uint32_t>(aEOFPos));
2273 NS_ENSURE_SUCCESS(rv, rv);
2274
2275 return NS_OK;
2276 }
2277
2278 // static
2279 nsresult
2280 CacheFileIOManager::RenameFile(CacheFileHandle *aHandle,
2281 const nsACString &aNewName,
2282 CacheFileIOListener *aCallback)
2283 {
2284 LOG(("CacheFileIOManager::RenameFile() [handle=%p, newName=%s, listener=%p]",
2285 aHandle, PromiseFlatCString(aNewName).get(), aCallback));
2286
2287 nsresult rv;
2288 nsRefPtr<CacheFileIOManager> ioMan = gInstance;
2289
2290 if (aHandle->IsClosed() || !ioMan) {
2291 return NS_ERROR_NOT_INITIALIZED;
2292 }
2293
2294 if (!aHandle->IsSpecialFile()) {
2295 return NS_ERROR_UNEXPECTED;
2296 }
2297
2298 nsRefPtr<RenameFileEvent> ev = new RenameFileEvent(aHandle, aNewName,
2299 aCallback);
2300 rv = ioMan->mIOThread->Dispatch(ev, CacheIOThread::WRITE);
2301 NS_ENSURE_SUCCESS(rv, rv);
2302
2303 return NS_OK;
2304 }
2305
2306 nsresult
2307 CacheFileIOManager::RenameFileInternal(CacheFileHandle *aHandle,
2308 const nsACString &aNewName)
2309 {
2310 LOG(("CacheFileIOManager::RenameFileInternal() [handle=%p, newName=%s]",
2311 aHandle, PromiseFlatCString(aNewName).get()));
2312
2313 nsresult rv;
2314
2315 MOZ_ASSERT(aHandle->IsSpecialFile());
2316
2317 if (aHandle->IsDoomed()) {
2318 return NS_ERROR_NOT_AVAILABLE;
2319 }
2320
2321 // Doom old handle if it exists and is not doomed
2322 for (uint32_t i = 0 ; i < mSpecialHandles.Length() ; i++) {
2323 if (!mSpecialHandles[i]->IsDoomed() &&
2324 mSpecialHandles[i]->Key() == aNewName) {
2325 MOZ_ASSERT(aHandle != mSpecialHandles[i]);
2326 rv = DoomFileInternal(mSpecialHandles[i]);
2327 NS_ENSURE_SUCCESS(rv, rv);
2328 break;
2329 }
2330 }
2331
2332 nsCOMPtr<nsIFile> file;
2333 rv = GetSpecialFile(aNewName, getter_AddRefs(file));
2334 NS_ENSURE_SUCCESS(rv, rv);
2335
2336 bool exists;
2337 rv = file->Exists(&exists);
2338 NS_ENSURE_SUCCESS(rv, rv);
2339
2340 if (exists) {
2341 LOG(("CacheFileIOManager::RenameFileInternal() - Removing old file from "
2342 "disk"));
2343 rv = file->Remove(false);
2344 if (NS_FAILED(rv)) {
2345 NS_WARNING("Cannot remove file from the disk");
2346 LOG(("CacheFileIOManager::RenameFileInternal() - Removing old file failed"
2347 ". [rv=0x%08x]", rv));
2348 }
2349 }
2350
2351 if (!aHandle->FileExists()) {
2352 aHandle->mKey = aNewName;
2353 return NS_OK;
2354 }
2355
2356 if (aHandle->mFD) {
2357 ReleaseNSPRHandleInternal(aHandle);
2358 }
2359
2360 rv = aHandle->mFile->MoveToNative(nullptr, aNewName);
2361 NS_ENSURE_SUCCESS(rv, rv);
2362
2363 aHandle->mKey = aNewName;
2364 return NS_OK;
2365 }
2366
2367 // static
2368 nsresult
2369 CacheFileIOManager::EvictIfOverLimit()
2370 {
2371 LOG(("CacheFileIOManager::EvictIfOverLimit()"));
2372
2373 nsresult rv;
2374 nsRefPtr<CacheFileIOManager> ioMan = gInstance;
2375
2376 if (!ioMan) {
2377 return NS_ERROR_NOT_INITIALIZED;
2378 }
2379
2380 nsCOMPtr<nsIRunnable> ev;
2381 ev = NS_NewRunnableMethod(ioMan,
2382 &CacheFileIOManager::EvictIfOverLimitInternal);
2383
2384 rv = ioMan->mIOThread->Dispatch(ev, CacheIOThread::EVICT);
2385 NS_ENSURE_SUCCESS(rv, rv);
2386
2387 return NS_OK;
2388 }
2389
2390 nsresult
2391 CacheFileIOManager::EvictIfOverLimitInternal()
2392 {
2393 LOG(("CacheFileIOManager::EvictIfOverLimitInternal()"));
2394
2395 nsresult rv;
2396
2397 MOZ_ASSERT(mIOThread->IsCurrentThread());
2398
2399 if (mShuttingDown) {
2400 return NS_ERROR_NOT_INITIALIZED;
2401 }
2402
2403 if (mOverLimitEvicting) {
2404 LOG(("CacheFileIOManager::EvictIfOverLimitInternal() - Eviction already "
2405 "running."));
2406 return NS_OK;
2407 }
2408
2409 UpdateSmartCacheSize();
2410
2411 uint32_t cacheUsage;
2412 rv = CacheIndex::GetCacheSize(&cacheUsage);
2413 NS_ENSURE_SUCCESS(rv, rv);
2414
2415 uint32_t cacheLimit = CacheObserver::DiskCacheCapacity() >> 10;
2416 if (cacheUsage <= cacheLimit) {
2417 LOG(("CacheFileIOManager::EvictIfOverLimitInternal() - Cache size under "
2418 "limit. [cacheSize=%u, limit=%u]", cacheUsage, cacheLimit));
2419 return NS_OK;
2420 }
2421
2422 LOG(("CacheFileIOManager::EvictIfOverLimitInternal() - Cache size exceeded "
2423 "limit. Starting overlimit eviction. [cacheSize=%u, limit=%u]",
2424 cacheUsage, cacheLimit));
2425
2426 nsCOMPtr<nsIRunnable> ev;
2427 ev = NS_NewRunnableMethod(this,
2428 &CacheFileIOManager::OverLimitEvictionInternal);
2429
2430 rv = mIOThread->Dispatch(ev, CacheIOThread::EVICT);
2431 NS_ENSURE_SUCCESS(rv, rv);
2432
2433 mOverLimitEvicting = true;
2434 return NS_OK;
2435 }
2436
2437 nsresult
2438 CacheFileIOManager::OverLimitEvictionInternal()
2439 {
2440 LOG(("CacheFileIOManager::OverLimitEvictionInternal()"));
2441
2442 nsresult rv;
2443
2444 MOZ_ASSERT(mIOThread->IsCurrentThread());
2445
2446 // mOverLimitEvicting is accessed only on IO thread, so we can set it to false
2447 // here and set it to true again once we dispatch another event that will
2448 // continue with the eviction. The reason why we do so is that we can fail
2449 // early anywhere in this method and the variable will contain a correct
2450 // value. Otherwise we would need to set it to false on every failing place.
2451 mOverLimitEvicting = false;
2452
2453 if (mShuttingDown) {
2454 return NS_ERROR_NOT_INITIALIZED;
2455 }
2456
2457 UpdateSmartCacheSize();
2458
2459 while (true) {
2460 uint32_t cacheUsage;
2461 rv = CacheIndex::GetCacheSize(&cacheUsage);
2462 NS_ENSURE_SUCCESS(rv, rv);
2463
2464 uint32_t cacheLimit = CacheObserver::DiskCacheCapacity() >> 10;
2465 if (cacheUsage <= cacheLimit) {
2466 LOG(("CacheFileIOManager::OverLimitEvictionInternal() - Cache size under "
2467 "limit. [cacheSize=%u, limit=%u]", cacheUsage, cacheLimit));
2468 return NS_OK;
2469 }
2470
2471 LOG(("CacheFileIOManager::OverLimitEvictionInternal() - Cache size over "
2472 "limit. [cacheSize=%u, limit=%u]", cacheUsage, cacheLimit));
2473
2474 if (CacheIOThread::YieldAndRerun()) {
2475 LOG(("CacheFileIOManager::OverLimitEvictionInternal() - Breaking loop "
2476 "for higher level events."));
2477 mOverLimitEvicting = true;
2478 return NS_OK;
2479 }
2480
2481 SHA1Sum::Hash hash;
2482 uint32_t cnt;
2483 static uint32_t consecutiveFailures = 0;
2484 rv = CacheIndex::GetEntryForEviction(&hash, &cnt);
2485 NS_ENSURE_SUCCESS(rv, rv);
2486
2487 rv = DoomFileByKeyInternal(&hash);
2488 if (NS_SUCCEEDED(rv)) {
2489 consecutiveFailures = 0;
2490 } else if (rv == NS_ERROR_NOT_AVAILABLE) {
2491 LOG(("CacheFileIOManager::OverLimitEvictionInternal() - "
2492 "DoomFileByKeyInternal() failed. [rv=0x%08x]", rv));
2493 // TODO index is outdated, start update
2494
2495 // Make sure index won't return the same entry again
2496 CacheIndex::RemoveEntry(&hash);
2497 consecutiveFailures = 0;
2498 } else {
2499 // This shouldn't normally happen, but the eviction must not fail
2500 // completely if we ever encounter this problem.
2501 NS_WARNING("CacheFileIOManager::OverLimitEvictionInternal() - Unexpected "
2502 "failure of DoomFileByKeyInternal()");
2503
2504 LOG(("CacheFileIOManager::OverLimitEvictionInternal() - "
2505 "DoomFileByKeyInternal() failed. [rv=0x%08x]", rv));
2506
2507 // Normally, CacheIndex::UpdateEntry() is called only to update newly
2508 // created/opened entries which are always fresh and UpdateEntry() expects
2509 // and checks this flag. The way we use UpdateEntry() here is a kind of
2510 // hack and we must make sure the flag is set by calling
2511 // EnsureEntryExists().
2512 rv = CacheIndex::EnsureEntryExists(&hash);
2513 NS_ENSURE_SUCCESS(rv, rv);
2514
2515 // Move the entry at the end of both lists to make sure we won't end up
2516 // failing on one entry forever.
2517 uint32_t frecency = 0;
2518 uint32_t expTime = nsICacheEntry::NO_EXPIRATION_TIME;
2519 rv = CacheIndex::UpdateEntry(&hash, &frecency, &expTime, nullptr);
2520 NS_ENSURE_SUCCESS(rv, rv);
2521
2522 consecutiveFailures++;
2523 if (consecutiveFailures >= cnt) {
2524 // This doesn't necessarily mean that we've tried to doom every entry
2525 // but we've reached a sane number of tries. It is likely that another
2526 // eviction will start soon. And as said earlier, this normally doesn't
2527 // happen at all.
2528 return NS_OK;
2529 }
2530 }
2531 }
2532
2533 NS_NOTREACHED("We should never get here");
2534 return NS_OK;
2535 }
2536
2537 // static
2538 nsresult
2539 CacheFileIOManager::EvictAll()
2540 {
2541 LOG(("CacheFileIOManager::EvictAll()"));
2542
2543 nsresult rv;
2544 nsRefPtr<CacheFileIOManager> ioMan = gInstance;
2545
2546 if (!ioMan) {
2547 return NS_ERROR_NOT_INITIALIZED;
2548 }
2549
2550 nsCOMPtr<nsIRunnable> ev;
2551 ev = NS_NewRunnableMethod(ioMan, &CacheFileIOManager::EvictAllInternal);
2552
2553 rv = ioMan->mIOThread->DispatchAfterPendingOpens(ev);
2554 if (NS_WARN_IF(NS_FAILED(rv))) {
2555 return rv;
2556 }
2557
2558 return NS_OK;
2559 }
2560
2561 namespace {
2562
2563 class EvictionNotifierRunnable : public nsRunnable
2564 {
2565 public:
2566 NS_DECL_NSIRUNNABLE
2567 };
2568
2569 NS_IMETHODIMP
2570 EvictionNotifierRunnable::Run()
2571 {
2572 nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService();
2573 if (obsSvc) {
2574 obsSvc->NotifyObservers(nullptr, "cacheservice:empty-cache", nullptr);
2575 }
2576 return NS_OK;
2577 }
2578
2579 } // anonymous namespace
2580
2581 nsresult
2582 CacheFileIOManager::EvictAllInternal()
2583 {
2584 LOG(("CacheFileIOManager::EvictAllInternal()"));
2585
2586 nsresult rv;
2587
2588 MOZ_ASSERT(mIOThread->IsCurrentThread());
2589
2590 nsRefPtr<EvictionNotifierRunnable> r = new EvictionNotifierRunnable();
2591
2592 if (!mCacheDirectory) {
2593 // This is a kind of hack. Somebody called EvictAll() without a profile.
2594 // This happens in xpcshell tests that use cache without profile. We need
2595 // to notify observers in this case since the tests are waiting for it.
2596 NS_DispatchToMainThread(r);
2597 return NS_ERROR_FILE_INVALID_PATH;
2598 }
2599
2600 if (mShuttingDown) {
2601 return NS_ERROR_NOT_INITIALIZED;
2602 }
2603
2604 if (!mTreeCreated) {
2605 rv = CreateCacheTree();
2606 if (NS_FAILED(rv)) {
2607 return rv;
2608 }
2609 }
2610
2611 // Doom all active handles
2612 nsTArray<nsRefPtr<CacheFileHandle> > handles;
2613 mHandles.GetActiveHandles(&handles);
2614
2615 for (uint32_t i = 0; i < handles.Length(); ++i) {
2616 rv = DoomFileInternal(handles[i]);
2617 if (NS_WARN_IF(NS_FAILED(rv))) {
2618 LOG(("CacheFileIOManager::EvictAllInternal() - Cannot doom handle "
2619 "[handle=%p]", handles[i].get()));
2620 }
2621 }
2622
2623 nsCOMPtr<nsIFile> file;
2624 rv = mCacheDirectory->Clone(getter_AddRefs(file));
2625 if (NS_WARN_IF(NS_FAILED(rv))) {
2626 return rv;
2627 }
2628
2629 rv = file->AppendNative(NS_LITERAL_CSTRING(kEntriesDir));
2630 if (NS_WARN_IF(NS_FAILED(rv))) {
2631 return rv;
2632 }
2633
2634 // Trash current entries directory
2635 rv = TrashDirectory(file);
2636 if (NS_WARN_IF(NS_FAILED(rv))) {
2637 return rv;
2638 }
2639
2640 // Files are now inaccessible in entries directory, notify observers.
2641 NS_DispatchToMainThread(r);
2642
2643 // Create a new empty entries directory
2644 rv = CheckAndCreateDir(mCacheDirectory, kEntriesDir, false);
2645 if (NS_WARN_IF(NS_FAILED(rv))) {
2646 return rv;
2647 }
2648
2649 CacheIndex::RemoveAll();
2650
2651 return NS_OK;
2652 }
2653
2654 // static
2655 nsresult
2656 CacheFileIOManager::EvictByContext(nsILoadContextInfo *aLoadContextInfo)
2657 {
2658 LOG(("CacheFileIOManager::EvictByContext() [loadContextInfo=%p]",
2659 aLoadContextInfo));
2660
2661 nsresult rv;
2662 nsRefPtr<CacheFileIOManager> ioMan = gInstance;
2663
2664 if (!ioMan) {
2665 return NS_ERROR_NOT_INITIALIZED;
2666 }
2667
2668 nsCOMPtr<nsIRunnable> ev;
2669 ev = NS_NewRunnableMethodWithArg<nsCOMPtr<nsILoadContextInfo> >
2670 (ioMan, &CacheFileIOManager::EvictByContextInternal, aLoadContextInfo);
2671
2672 rv = ioMan->mIOThread->DispatchAfterPendingOpens(ev);
2673 if (NS_WARN_IF(NS_FAILED(rv))) {
2674 return rv;
2675 }
2676
2677 return NS_OK;
2678 }
2679
2680 nsresult
2681 CacheFileIOManager::EvictByContextInternal(nsILoadContextInfo *aLoadContextInfo)
2682 {
2683 LOG(("CacheFileIOManager::EvictByContextInternal() [loadContextInfo=%p, "
2684 "anonymous=%u, inBrowser=%u, appId=%u]", aLoadContextInfo,
2685 aLoadContextInfo->IsAnonymous(), aLoadContextInfo->IsInBrowserElement(),
2686 aLoadContextInfo->AppId()));
2687
2688 nsresult rv;
2689
2690 MOZ_ASSERT(mIOThread->IsCurrentThread());
2691
2692 MOZ_ASSERT(!aLoadContextInfo->IsPrivate());
2693 if (aLoadContextInfo->IsPrivate()) {
2694 return NS_ERROR_INVALID_ARG;
2695 }
2696
2697 if (!mCacheDirectory) {
2698 return NS_ERROR_FILE_INVALID_PATH;
2699 }
2700
2701 if (mShuttingDown) {
2702 return NS_ERROR_NOT_INITIALIZED;
2703 }
2704
2705 if (!mTreeCreated) {
2706 rv = CreateCacheTree();
2707 if (NS_FAILED(rv)) {
2708 return rv;
2709 }
2710 }
2711
2712 // Doom all active handles that matches the load context
2713 nsTArray<nsRefPtr<CacheFileHandle> > handles;
2714 mHandles.GetActiveHandles(&handles);
2715
2716 for (uint32_t i = 0; i < handles.Length(); ++i) {
2717 bool equals;
2718 rv = CacheFileUtils::KeyMatchesLoadContextInfo(handles[i]->Key(),
2719 aLoadContextInfo,
2720 &equals);
2721 if (NS_FAILED(rv)) {
2722 LOG(("CacheFileIOManager::EvictByContextInternal() - Cannot parse key in "
2723 "handle! [handle=%p, key=%s]", handles[i].get(),
2724 handles[i]->Key().get()));
2725 MOZ_CRASH("Unexpected error!");
2726 }
2727
2728 if (equals) {
2729 rv = DoomFileInternal(handles[i]);
2730 if (NS_WARN_IF(NS_FAILED(rv))) {
2731 LOG(("CacheFileIOManager::EvictByContextInternal() - Cannot doom handle"
2732 " [handle=%p]", handles[i].get()));
2733 }
2734 }
2735 }
2736
2737 if (!mContextEvictor) {
2738 mContextEvictor = new CacheFileContextEvictor();
2739 mContextEvictor->Init(mCacheDirectory);
2740 }
2741
2742 mContextEvictor->AddContext(aLoadContextInfo);
2743
2744 return NS_OK;
2745 }
2746
2747 // static
2748 nsresult
2749 CacheFileIOManager::CacheIndexStateChanged()
2750 {
2751 LOG(("CacheFileIOManager::CacheIndexStateChanged()"));
2752
2753 nsresult rv;
2754
2755 // CacheFileIOManager lives longer than CacheIndex so gInstance must be
2756 // non-null here.
2757 MOZ_ASSERT(gInstance);
2758
2759 // We have to re-distatch even if we are on IO thread to prevent reentering
2760 // the lock in CacheIndex
2761 nsCOMPtr<nsIRunnable> ev;
2762 ev = NS_NewRunnableMethod(
2763 gInstance, &CacheFileIOManager::CacheIndexStateChangedInternal);
2764
2765 nsCOMPtr<nsIEventTarget> ioTarget = IOTarget();
2766 MOZ_ASSERT(ioTarget);
2767
2768 rv = ioTarget->Dispatch(ev, nsIEventTarget::DISPATCH_NORMAL);
2769 if (NS_WARN_IF(NS_FAILED(rv))) {
2770 return rv;
2771 }
2772
2773 return NS_OK;
2774 }
2775
2776 nsresult
2777 CacheFileIOManager::CacheIndexStateChangedInternal()
2778 {
2779 if (mShuttingDown) {
2780 // ignore notification during shutdown
2781 return NS_OK;
2782 }
2783
2784 if (!mContextEvictor) {
2785 return NS_OK;
2786 }
2787
2788 mContextEvictor->CacheIndexStateChanged();
2789 return NS_OK;
2790 }
2791
2792 nsresult
2793 CacheFileIOManager::TrashDirectory(nsIFile *aFile)
2794 {
2795 #ifdef PR_LOGGING
2796 nsAutoCString path;
2797 aFile->GetNativePath(path);
2798 #endif
2799 LOG(("CacheFileIOManager::TrashDirectory() [file=%s]", path.get()));
2800
2801 nsresult rv;
2802
2803 MOZ_ASSERT(mIOThread->IsCurrentThread());
2804 MOZ_ASSERT(mCacheDirectory);
2805
2806 // When the directory is empty, it is cheaper to remove it directly instead of
2807 // using the trash mechanism.
2808 bool isEmpty;
2809 rv = IsEmptyDirectory(aFile, &isEmpty);
2810 NS_ENSURE_SUCCESS(rv, rv);
2811
2812 if (isEmpty) {
2813 rv = aFile->Remove(false);
2814 LOG(("CacheFileIOManager::TrashDirectory() - Directory removed [rv=0x%08x]",
2815 rv));
2816 return rv;
2817 }
2818
2819 #ifdef DEBUG
2820 nsCOMPtr<nsIFile> dirCheck;
2821 rv = aFile->GetParent(getter_AddRefs(dirCheck));
2822 NS_ENSURE_SUCCESS(rv, rv);
2823
2824 bool equals = false;
2825 rv = dirCheck->Equals(mCacheDirectory, &equals);
2826 NS_ENSURE_SUCCESS(rv, rv);
2827
2828 MOZ_ASSERT(equals);
2829 #endif
2830
2831 nsCOMPtr<nsIFile> dir, trash;
2832 nsAutoCString leaf;
2833
2834 rv = aFile->Clone(getter_AddRefs(dir));
2835 NS_ENSURE_SUCCESS(rv, rv);
2836
2837 rv = aFile->Clone(getter_AddRefs(trash));
2838 NS_ENSURE_SUCCESS(rv, rv);
2839
2840 srand(static_cast<unsigned>(PR_Now()));
2841 while (true) {
2842 leaf = kTrashDir;
2843 leaf.AppendInt(rand());
2844 rv = trash->SetNativeLeafName(leaf);
2845 NS_ENSURE_SUCCESS(rv, rv);
2846
2847 bool exists;
2848 if (NS_SUCCEEDED(trash->Exists(&exists)) && !exists) {
2849 break;
2850 }
2851 }
2852
2853 LOG(("CacheFileIOManager::TrashDirectory() - Renaming directory [leaf=%s]",
2854 leaf.get()));
2855
2856 rv = dir->MoveToNative(nullptr, leaf);
2857 NS_ENSURE_SUCCESS(rv, rv);
2858
2859 StartRemovingTrash();
2860 return NS_OK;
2861 }
2862
2863 // static
2864 void
2865 CacheFileIOManager::OnTrashTimer(nsITimer *aTimer, void *aClosure)
2866 {
2867 LOG(("CacheFileIOManager::OnTrashTimer() [timer=%p, closure=%p]", aTimer,
2868 aClosure));
2869
2870 nsRefPtr<CacheFileIOManager> ioMan = gInstance;
2871
2872 if (!ioMan) {
2873 return;
2874 }
2875
2876 ioMan->mTrashTimer = nullptr;
2877 ioMan->StartRemovingTrash();
2878 }
2879
2880 nsresult
2881 CacheFileIOManager::StartRemovingTrash()
2882 {
2883 LOG(("CacheFileIOManager::StartRemovingTrash()"));
2884
2885 nsresult rv;
2886
2887 MOZ_ASSERT(mIOThread->IsCurrentThread());
2888
2889 if (mShuttingDown) {
2890 return NS_ERROR_NOT_INITIALIZED;
2891 }
2892
2893 if (!mCacheDirectory) {
2894 return NS_ERROR_FILE_INVALID_PATH;
2895 }
2896
2897 if (mTrashTimer) {
2898 LOG(("CacheFileIOManager::StartRemovingTrash() - Trash timer exists."));
2899 return NS_OK;
2900 }
2901
2902 if (mRemovingTrashDirs) {
2903 LOG(("CacheFileIOManager::StartRemovingTrash() - Trash removing in "
2904 "progress."));
2905 return NS_OK;
2906 }
2907
2908 uint32_t elapsed = (TimeStamp::NowLoRes() - mStartTime).ToMilliseconds();
2909 if (elapsed < kRemoveTrashStartDelay) {
2910 nsCOMPtr<nsITimer> timer = do_CreateInstance("@mozilla.org/timer;1", &rv);
2911 NS_ENSURE_SUCCESS(rv, rv);
2912
2913 nsCOMPtr<nsIEventTarget> ioTarget = IOTarget();
2914 MOZ_ASSERT(ioTarget);
2915
2916 rv = timer->SetTarget(ioTarget);
2917 NS_ENSURE_SUCCESS(rv, rv);
2918
2919 rv = timer->InitWithFuncCallback(CacheFileIOManager::OnTrashTimer, nullptr,
2920 kRemoveTrashStartDelay - elapsed,
2921 nsITimer::TYPE_ONE_SHOT);
2922 NS_ENSURE_SUCCESS(rv, rv);
2923
2924 mTrashTimer.swap(timer);
2925 return NS_OK;
2926 }
2927
2928 nsCOMPtr<nsIRunnable> ev;
2929 ev = NS_NewRunnableMethod(this,
2930 &CacheFileIOManager::RemoveTrashInternal);
2931
2932 rv = mIOThread->Dispatch(ev, CacheIOThread::EVICT);
2933 NS_ENSURE_SUCCESS(rv, rv);
2934
2935 mRemovingTrashDirs = true;
2936 return NS_OK;
2937 }
2938
2939 nsresult
2940 CacheFileIOManager::RemoveTrashInternal()
2941 {
2942 LOG(("CacheFileIOManager::RemoveTrashInternal()"));
2943
2944 nsresult rv;
2945
2946 MOZ_ASSERT(mIOThread->IsCurrentThread());
2947
2948 if (mShuttingDown) {
2949 return NS_ERROR_NOT_INITIALIZED;
2950 }
2951
2952 MOZ_ASSERT(!mTrashTimer);
2953 MOZ_ASSERT(mRemovingTrashDirs);
2954
2955 if (!mTreeCreated) {
2956 rv = CreateCacheTree();
2957 if (NS_FAILED(rv)) {
2958 return rv;
2959 }
2960 }
2961
2962 // mRemovingTrashDirs is accessed only on IO thread, so we can drop the flag
2963 // here and set it again once we dispatch a continuation event. By doing so,
2964 // we don't have to drop the flag on any possible early return.
2965 mRemovingTrashDirs = false;
2966
2967 while (true) {
2968 if (CacheIOThread::YieldAndRerun()) {
2969 LOG(("CacheFileIOManager::RemoveTrashInternal() - Breaking loop for "
2970 "higher level events."));
2971 mRemovingTrashDirs = true;
2972 return NS_OK;
2973 }
2974
2975 // Find some trash directory
2976 if (!mTrashDir) {
2977 MOZ_ASSERT(!mTrashDirEnumerator);
2978
2979 rv = FindTrashDirToRemove();
2980 if (rv == NS_ERROR_NOT_AVAILABLE) {
2981 LOG(("CacheFileIOManager::RemoveTrashInternal() - No trash directory "
2982 "found."));
2983 return NS_OK;
2984 }
2985 NS_ENSURE_SUCCESS(rv, rv);
2986
2987 nsCOMPtr<nsISimpleEnumerator> enumerator;
2988 rv = mTrashDir->GetDirectoryEntries(getter_AddRefs(enumerator));
2989 if (NS_SUCCEEDED(rv)) {
2990 mTrashDirEnumerator = do_QueryInterface(enumerator, &rv);
2991 NS_ENSURE_SUCCESS(rv, rv);
2992 }
2993
2994 continue; // check elapsed time
2995 }
2996
2997 // We null out mTrashDirEnumerator once we remove all files in the
2998 // directory, so remove the trash directory if we don't have enumerator.
2999 if (!mTrashDirEnumerator) {
3000 rv = mTrashDir->Remove(false);
3001 if (NS_FAILED(rv)) {
3002 // There is no reason why removing an empty directory should fail, but
3003 // if it does, we should continue and try to remove all other trash
3004 // directories.
3005 nsAutoCString leafName;
3006 mTrashDir->GetNativeLeafName(leafName);
3007 mFailedTrashDirs.AppendElement(leafName);
3008 LOG(("CacheFileIOManager::RemoveTrashInternal() - Cannot remove "
3009 "trashdir. [name=%s]", leafName.get()));
3010 }
3011
3012 mTrashDir = nullptr;
3013 continue; // check elapsed time
3014 }
3015
3016 nsCOMPtr<nsIFile> file;
3017 rv = mTrashDirEnumerator->GetNextFile(getter_AddRefs(file));
3018 if (!file) {
3019 mTrashDirEnumerator->Close();
3020 mTrashDirEnumerator = nullptr;
3021 continue; // check elapsed time
3022 } else {
3023 bool isDir = false;
3024 file->IsDirectory(&isDir);
3025 if (isDir) {
3026 NS_WARNING("Found a directory in a trash directory! It will be removed "
3027 "recursively, but this can block IO thread for a while!");
3028 #ifdef PR_LOGGING
3029 nsAutoCString path;
3030 file->GetNativePath(path);
3031 #endif
3032 LOG(("CacheFileIOManager::RemoveTrashInternal() - Found a directory in a trash "
3033 "directory! It will be removed recursively, but this can block IO "
3034 "thread for a while! [file=%s]", path.get()));
3035 }
3036 file->Remove(isDir);
3037 }
3038 }
3039
3040 NS_NOTREACHED("We should never get here");
3041 return NS_OK;
3042 }
3043
3044 nsresult
3045 CacheFileIOManager::FindTrashDirToRemove()
3046 {
3047 LOG(("CacheFileIOManager::FindTrashDirToRemove()"));
3048
3049 nsresult rv;
3050
3051 // We call this method on the main thread during shutdown when user wants to
3052 // remove all cache files.
3053 MOZ_ASSERT(mIOThread->IsCurrentThread() || mShuttingDown);
3054
3055 nsCOMPtr<nsISimpleEnumerator> iter;
3056 rv = mCacheDirectory->GetDirectoryEntries(getter_AddRefs(iter));
3057 NS_ENSURE_SUCCESS(rv, rv);
3058
3059 bool more;
3060 nsCOMPtr<nsISupports> elem;
3061
3062 while (NS_SUCCEEDED(iter->HasMoreElements(&more)) && more) {
3063 rv = iter->GetNext(getter_AddRefs(elem));
3064 if (NS_FAILED(rv)) {
3065 continue;
3066 }
3067
3068 nsCOMPtr<nsIFile> file = do_QueryInterface(elem);
3069 if (!file) {
3070 continue;
3071 }
3072
3073 bool isDir = false;
3074 file->IsDirectory(&isDir);
3075 if (!isDir) {
3076 continue;
3077 }
3078
3079 nsAutoCString leafName;
3080 rv = file->GetNativeLeafName(leafName);
3081 if (NS_FAILED(rv)) {
3082 continue;
3083 }
3084
3085 if (leafName.Length() < strlen(kTrashDir)) {
3086 continue;
3087 }
3088
3089 if (!StringBeginsWith(leafName, NS_LITERAL_CSTRING(kTrashDir))) {
3090 continue;
3091 }
3092
3093 if (mFailedTrashDirs.Contains(leafName)) {
3094 continue;
3095 }
3096
3097 LOG(("CacheFileIOManager::FindTrashDirToRemove() - Returning directory %s",
3098 leafName.get()));
3099
3100 mTrashDir = file;
3101 return NS_OK;
3102 }
3103
3104 // When we're here we've tried to delete all trash directories. Clear
3105 // mFailedTrashDirs so we will try to delete them again when we start removing
3106 // trash directories next time.
3107 mFailedTrashDirs.Clear();
3108 return NS_ERROR_NOT_AVAILABLE;
3109 }
3110
3111 // static
3112 nsresult
3113 CacheFileIOManager::InitIndexEntry(CacheFileHandle *aHandle,
3114 uint32_t aAppId,
3115 bool aAnonymous,
3116 bool aInBrowser)
3117 {
3118 LOG(("CacheFileIOManager::InitIndexEntry() [handle=%p, appId=%u, anonymous=%d"
3119 ", inBrowser=%d]", aHandle, aAppId, aAnonymous, aInBrowser));
3120
3121 nsresult rv;
3122 nsRefPtr<CacheFileIOManager> ioMan = gInstance;
3123
3124 if (aHandle->IsClosed() || !ioMan) {
3125 return NS_ERROR_NOT_INITIALIZED;
3126 }
3127
3128 if (aHandle->IsSpecialFile()) {
3129 return NS_ERROR_UNEXPECTED;
3130 }
3131
3132 nsRefPtr<InitIndexEntryEvent> ev =
3133 new InitIndexEntryEvent(aHandle, aAppId, aAnonymous, aInBrowser);
3134 rv = ioMan->mIOThread->Dispatch(ev, CacheIOThread::WRITE);
3135 NS_ENSURE_SUCCESS(rv, rv);
3136
3137 return NS_OK;
3138 }
3139
3140 // static
3141 nsresult
3142 CacheFileIOManager::UpdateIndexEntry(CacheFileHandle *aHandle,
3143 const uint32_t *aFrecency,
3144 const uint32_t *aExpirationTime)
3145 {
3146 LOG(("CacheFileIOManager::UpdateIndexEntry() [handle=%p, frecency=%s, "
3147 "expirationTime=%s]", aHandle,
3148 aFrecency ? nsPrintfCString("%u", *aFrecency).get() : "",
3149 aExpirationTime ? nsPrintfCString("%u", *aExpirationTime).get() : ""));
3150
3151 nsresult rv;
3152 nsRefPtr<CacheFileIOManager> ioMan = gInstance;
3153
3154 if (aHandle->IsClosed() || !ioMan) {
3155 return NS_ERROR_NOT_INITIALIZED;
3156 }
3157
3158 if (aHandle->IsSpecialFile()) {
3159 return NS_ERROR_UNEXPECTED;
3160 }
3161
3162 nsRefPtr<UpdateIndexEntryEvent> ev =
3163 new UpdateIndexEntryEvent(aHandle, aFrecency, aExpirationTime);
3164 rv = ioMan->mIOThread->Dispatch(ev, CacheIOThread::WRITE);
3165 NS_ENSURE_SUCCESS(rv, rv);
3166
3167 return NS_OK;
3168 }
3169
3170 nsresult
3171 CacheFileIOManager::CreateFile(CacheFileHandle *aHandle)
3172 {
3173 MOZ_ASSERT(!aHandle->mFD);
3174 MOZ_ASSERT(aHandle->mFile);
3175
3176 nsresult rv;
3177
3178 if (aHandle->IsDoomed()) {
3179 nsCOMPtr<nsIFile> file;
3180
3181 rv = GetDoomedFile(getter_AddRefs(file));
3182 NS_ENSURE_SUCCESS(rv, rv);
3183
3184 aHandle->mFile.swap(file);
3185 } else {
3186 bool exists;
3187 if (NS_SUCCEEDED(aHandle->mFile->Exists(&exists)) && exists) {
3188 NS_WARNING("Found a file that should not exist!");
3189 }
3190 }
3191
3192 rv = OpenNSPRHandle(aHandle, true);
3193 NS_ENSURE_SUCCESS(rv, rv);
3194
3195 aHandle->mFileSize = 0;
3196 return NS_OK;
3197 }
3198
3199 // static
3200 void
3201 CacheFileIOManager::HashToStr(const SHA1Sum::Hash *aHash, nsACString &_retval)
3202 {
3203 _retval.Assign("");
3204 const char hexChars[] = {'0', '1', '2', '3', '4', '5', '6', '7',
3205 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
3206 for (uint32_t i=0 ; i<sizeof(SHA1Sum::Hash) ; i++) {
3207 _retval.Append(hexChars[(*aHash)[i] >> 4]);
3208 _retval.Append(hexChars[(*aHash)[i] & 0xF]);
3209 }
3210 }
3211
3212 // static
3213 nsresult
3214 CacheFileIOManager::StrToHash(const nsACString &aHash, SHA1Sum::Hash *_retval)
3215 {
3216 if (aHash.Length() != 2*sizeof(SHA1Sum::Hash)) {
3217 return NS_ERROR_INVALID_ARG;
3218 }
3219
3220 for (uint32_t i=0 ; i<aHash.Length() ; i++) {
3221 uint8_t value;
3222
3223 if (aHash[i] >= '0' && aHash[i] <= '9') {
3224 value = aHash[i] - '0';
3225 } else if (aHash[i] >= 'A' && aHash[i] <= 'F') {
3226 value = aHash[i] - 'A' + 10;
3227 } else if (aHash[i] >= 'a' && aHash[i] <= 'f') {
3228 value = aHash[i] - 'a' + 10;
3229 } else {
3230 return NS_ERROR_INVALID_ARG;
3231 }
3232
3233 if (i%2 == 0) {
3234 (reinterpret_cast<uint8_t *>(_retval))[i/2] = value << 4;
3235 } else {
3236 (reinterpret_cast<uint8_t *>(_retval))[i/2] += value;
3237 }
3238 }
3239
3240 return NS_OK;
3241 }
3242
3243 nsresult
3244 CacheFileIOManager::GetFile(const SHA1Sum::Hash *aHash, nsIFile **_retval)
3245 {
3246 nsresult rv;
3247 nsCOMPtr<nsIFile> file;
3248 rv = mCacheDirectory->Clone(getter_AddRefs(file));
3249 NS_ENSURE_SUCCESS(rv, rv);
3250
3251 rv = file->AppendNative(NS_LITERAL_CSTRING(kEntriesDir));
3252 NS_ENSURE_SUCCESS(rv, rv);
3253
3254 nsAutoCString leafName;
3255 HashToStr(aHash, leafName);
3256
3257 rv = file->AppendNative(leafName);
3258 NS_ENSURE_SUCCESS(rv, rv);
3259
3260 file.swap(*_retval);
3261 return NS_OK;
3262 }
3263
3264 nsresult
3265 CacheFileIOManager::GetSpecialFile(const nsACString &aKey, nsIFile **_retval)
3266 {
3267 nsresult rv;
3268 nsCOMPtr<nsIFile> file;
3269 rv = mCacheDirectory->Clone(getter_AddRefs(file));
3270 NS_ENSURE_SUCCESS(rv, rv);
3271
3272 rv = file->AppendNative(aKey);
3273 NS_ENSURE_SUCCESS(rv, rv);
3274
3275 file.swap(*_retval);
3276 return NS_OK;
3277 }
3278
3279 nsresult
3280 CacheFileIOManager::GetDoomedFile(nsIFile **_retval)
3281 {
3282 nsresult rv;
3283 nsCOMPtr<nsIFile> file;
3284 rv = mCacheDirectory->Clone(getter_AddRefs(file));
3285 NS_ENSURE_SUCCESS(rv, rv);
3286
3287 rv = file->AppendNative(NS_LITERAL_CSTRING(kDoomedDir));
3288 NS_ENSURE_SUCCESS(rv, rv);
3289
3290 rv = file->AppendNative(NS_LITERAL_CSTRING("dummyleaf"));
3291 NS_ENSURE_SUCCESS(rv, rv);
3292
3293 srand(static_cast<unsigned>(PR_Now()));
3294 nsAutoCString leafName;
3295 uint32_t iter=0;
3296 while (true) {
3297 iter++;
3298 leafName.AppendInt(rand());
3299 rv = file->SetNativeLeafName(leafName);
3300 NS_ENSURE_SUCCESS(rv, rv);
3301
3302 bool exists;
3303 if (NS_SUCCEEDED(file->Exists(&exists)) && !exists) {
3304 break;
3305 }
3306
3307 leafName.Truncate();
3308 }
3309
3310 // Telemetry::Accumulate(Telemetry::DISK_CACHE_GETDOOMEDFILE_ITERATIONS, iter);
3311
3312 file.swap(*_retval);
3313 return NS_OK;
3314 }
3315
3316 nsresult
3317 CacheFileIOManager::IsEmptyDirectory(nsIFile *aFile, bool *_retval)
3318 {
3319 MOZ_ASSERT(mIOThread->IsCurrentThread());
3320
3321 nsresult rv;
3322
3323 nsCOMPtr<nsISimpleEnumerator> enumerator;
3324 rv = aFile->GetDirectoryEntries(getter_AddRefs(enumerator));
3325 NS_ENSURE_SUCCESS(rv, rv);
3326
3327 bool hasMoreElements = false;
3328 rv = enumerator->HasMoreElements(&hasMoreElements);
3329 NS_ENSURE_SUCCESS(rv, rv);
3330
3331 *_retval = !hasMoreElements;
3332 return NS_OK;
3333 }
3334
3335 nsresult
3336 CacheFileIOManager::CheckAndCreateDir(nsIFile *aFile, const char *aDir,
3337 bool aEnsureEmptyDir)
3338 {
3339 nsresult rv;
3340
3341 nsCOMPtr<nsIFile> file;
3342 if (!aDir) {
3343 file = aFile;
3344 } else {
3345 nsAutoCString dir(aDir);
3346 rv = aFile->Clone(getter_AddRefs(file));
3347 NS_ENSURE_SUCCESS(rv, rv);
3348 rv = file->AppendNative(dir);
3349 NS_ENSURE_SUCCESS(rv, rv);
3350 }
3351
3352 bool exists = false;
3353 rv = file->Exists(&exists);
3354 if (NS_SUCCEEDED(rv) && exists) {
3355 bool isDirectory = false;
3356 rv = file->IsDirectory(&isDirectory);
3357 if (NS_FAILED(rv) || !isDirectory) {
3358 // Try to remove the file
3359 rv = file->Remove(false);
3360 if (NS_SUCCEEDED(rv)) {
3361 exists = false;
3362 }
3363 }
3364 NS_ENSURE_SUCCESS(rv, rv);
3365 }
3366
3367 if (aEnsureEmptyDir && NS_SUCCEEDED(rv) && exists) {
3368 bool isEmpty;
3369 rv = IsEmptyDirectory(file, &isEmpty);
3370 NS_ENSURE_SUCCESS(rv, rv);
3371
3372 if (!isEmpty) {
3373 rv = TrashDirectory(file);
3374 NS_ENSURE_SUCCESS(rv, rv);
3375
3376 exists = false;
3377 }
3378 }
3379
3380 if (NS_SUCCEEDED(rv) && !exists) {
3381 rv = file->Create(nsIFile::DIRECTORY_TYPE, 0700);
3382 }
3383 if (NS_FAILED(rv)) {
3384 NS_WARNING("Cannot create directory");
3385 return NS_ERROR_FAILURE;
3386 }
3387
3388 return NS_OK;
3389 }
3390
3391 nsresult
3392 CacheFileIOManager::CreateCacheTree()
3393 {
3394 MOZ_ASSERT(mIOThread->IsCurrentThread());
3395 MOZ_ASSERT(!mTreeCreated);
3396
3397 if (!mCacheDirectory) {
3398 return NS_ERROR_FILE_INVALID_PATH;
3399 }
3400
3401 nsresult rv;
3402
3403 // ensure parent directory exists
3404 nsCOMPtr<nsIFile> parentDir;
3405 rv = mCacheDirectory->GetParent(getter_AddRefs(parentDir));
3406 NS_ENSURE_SUCCESS(rv, rv);
3407 rv = CheckAndCreateDir(parentDir, nullptr, false);
3408 NS_ENSURE_SUCCESS(rv, rv);
3409
3410 // ensure cache directory exists
3411 rv = CheckAndCreateDir(mCacheDirectory, nullptr, false);
3412 NS_ENSURE_SUCCESS(rv, rv);
3413
3414 // ensure entries directory exists
3415 rv = CheckAndCreateDir(mCacheDirectory, kEntriesDir, false);
3416 NS_ENSURE_SUCCESS(rv, rv);
3417
3418 // ensure doomed directory exists
3419 rv = CheckAndCreateDir(mCacheDirectory, kDoomedDir, true);
3420 NS_ENSURE_SUCCESS(rv, rv);
3421
3422 mTreeCreated = true;
3423
3424 if (!mContextEvictor) {
3425 nsRefPtr<CacheFileContextEvictor> contextEvictor;
3426 contextEvictor = new CacheFileContextEvictor();
3427
3428 // Init() method will try to load unfinished contexts from the disk. Store
3429 // the evictor as a member only when there is some unfinished job.
3430 contextEvictor->Init(mCacheDirectory);
3431 if (contextEvictor->ContextsCount()) {
3432 contextEvictor.swap(mContextEvictor);
3433 }
3434 }
3435
3436 StartRemovingTrash();
3437
3438 return NS_OK;
3439 }
3440
3441 nsresult
3442 CacheFileIOManager::OpenNSPRHandle(CacheFileHandle *aHandle, bool aCreate)
3443 {
3444 MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
3445 MOZ_ASSERT(!aHandle->mFD);
3446 MOZ_ASSERT(mHandlesByLastUsed.IndexOf(aHandle) == mHandlesByLastUsed.NoIndex);
3447 MOZ_ASSERT(mHandlesByLastUsed.Length() <= kOpenHandlesLimit);
3448 MOZ_ASSERT((aCreate && !aHandle->mFileExists) ||
3449 (!aCreate && aHandle->mFileExists));
3450
3451 nsresult rv;
3452
3453 if (mHandlesByLastUsed.Length() == kOpenHandlesLimit) {
3454 // close handle that hasn't been used for the longest time
3455 rv = ReleaseNSPRHandleInternal(mHandlesByLastUsed[0]);
3456 NS_ENSURE_SUCCESS(rv, rv);
3457 }
3458
3459 if (aCreate) {
3460 rv = aHandle->mFile->OpenNSPRFileDesc(
3461 PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE, 0600, &aHandle->mFD);
3462 NS_ENSURE_SUCCESS(rv, rv);
3463
3464 aHandle->mFileExists = true;
3465 } else {
3466 rv = aHandle->mFile->OpenNSPRFileDesc(PR_RDWR, 0600, &aHandle->mFD);
3467 if (NS_ERROR_FILE_NOT_FOUND == rv) {
3468 LOG((" file doesn't exists"));
3469 aHandle->mFileExists = false;
3470 return DoomFileInternal(aHandle);
3471 }
3472 NS_ENSURE_SUCCESS(rv, rv);
3473 }
3474
3475 mHandlesByLastUsed.AppendElement(aHandle);
3476 return NS_OK;
3477 }
3478
3479 void
3480 CacheFileIOManager::NSPRHandleUsed(CacheFileHandle *aHandle)
3481 {
3482 MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
3483 MOZ_ASSERT(aHandle->mFD);
3484
3485 DebugOnly<bool> found;
3486 found = mHandlesByLastUsed.RemoveElement(aHandle);
3487 MOZ_ASSERT(found);
3488
3489 mHandlesByLastUsed.AppendElement(aHandle);
3490 }
3491
3492 nsresult
3493 CacheFileIOManager::SyncRemoveDir(nsIFile *aFile, const char *aDir)
3494 {
3495 nsresult rv;
3496 nsCOMPtr<nsIFile> file;
3497
3498 if (!aDir) {
3499 file = aFile;
3500 } else {
3501 rv = aFile->Clone(getter_AddRefs(file));
3502 if (NS_WARN_IF(NS_FAILED(rv))) {
3503 return rv;
3504 }
3505
3506 rv = file->AppendNative(nsDependentCString(aDir));
3507 if (NS_WARN_IF(NS_FAILED(rv))) {
3508 return rv;
3509 }
3510 }
3511
3512 #ifdef PR_LOGGING
3513 nsAutoCString path;
3514 file->GetNativePath(path);
3515 #endif
3516
3517 LOG(("CacheFileIOManager::SyncRemoveDir() - Removing directory %s",
3518 path.get()));
3519
3520 rv = file->Remove(true);
3521 if (NS_WARN_IF(NS_FAILED(rv))) {
3522 LOG(("CacheFileIOManager::SyncRemoveDir() - Removing failed! [rv=0x%08x]",
3523 rv));
3524 }
3525
3526 return rv;
3527 }
3528
3529 void
3530 CacheFileIOManager::SyncRemoveAllCacheFiles()
3531 {
3532 LOG(("CacheFileIOManager::SyncRemoveAllCacheFiles()"));
3533
3534 nsresult rv;
3535
3536 SyncRemoveDir(mCacheDirectory, kEntriesDir);
3537 SyncRemoveDir(mCacheDirectory, kDoomedDir);
3538
3539 // Clear any intermediate state of trash dir enumeration.
3540 mFailedTrashDirs.Clear();
3541 mTrashDir = nullptr;
3542
3543 while (true) {
3544 // FindTrashDirToRemove() fills mTrashDir if there is any trash directory.
3545 rv = FindTrashDirToRemove();
3546 if (rv == NS_ERROR_NOT_AVAILABLE) {
3547 LOG(("CacheFileIOManager::SyncRemoveAllCacheFiles() - No trash directory "
3548 "found."));
3549 break;
3550 }
3551 if (NS_WARN_IF(NS_FAILED(rv))) {
3552 LOG(("CacheFileIOManager::SyncRemoveAllCacheFiles() - "
3553 "FindTrashDirToRemove() returned an unexpected error. [rv=0x%08x]",
3554 rv));
3555 break;
3556 }
3557
3558 rv = SyncRemoveDir(mTrashDir, nullptr);
3559 if (NS_FAILED(rv)) {
3560 nsAutoCString leafName;
3561 mTrashDir->GetNativeLeafName(leafName);
3562 mFailedTrashDirs.AppendElement(leafName);
3563 }
3564 }
3565 }
3566
3567 // Returns default ("smart") size (in KB) of cache, given available disk space
3568 // (also in KB)
3569 static uint32_t
3570 SmartCacheSize(const uint32_t availKB)
3571 {
3572 uint32_t maxSize = kMaxCacheSizeKB;
3573
3574 if (availKB > 100 * 1024 * 1024) {
3575 return maxSize; // skip computing if we're over 100 GB
3576 }
3577
3578 // Grow/shrink in 10 MB units, deliberately, so that in the common case we
3579 // don't shrink cache and evict items every time we startup (it's important
3580 // that we don't slow down startup benchmarks).
3581 uint32_t sz10MBs = 0;
3582 uint32_t avail10MBs = availKB / (1024*10);
3583
3584 // .5% of space above 25 GB
3585 if (avail10MBs > 2500) {
3586 sz10MBs += static_cast<uint32_t>((avail10MBs - 2500)*.005);
3587 avail10MBs = 2500;
3588 }
3589 // 1% of space between 7GB -> 25 GB
3590 if (avail10MBs > 700) {
3591 sz10MBs += static_cast<uint32_t>((avail10MBs - 700)*.01);
3592 avail10MBs = 700;
3593 }
3594 // 5% of space between 500 MB -> 7 GB
3595 if (avail10MBs > 50) {
3596 sz10MBs += static_cast<uint32_t>((avail10MBs - 50)*.05);
3597 avail10MBs = 50;
3598 }
3599
3600 #ifdef ANDROID
3601 // On Android, smaller/older devices may have very little storage and
3602 // device owners may be sensitive to storage footprint: Use a smaller
3603 // percentage of available space and a smaller minimum.
3604
3605 // 20% of space up to 500 MB (10 MB min)
3606 sz10MBs += std::max<uint32_t>(1, static_cast<uint32_t>(avail10MBs * .2));
3607 #else
3608 // 40% of space up to 500 MB (50 MB min)
3609 sz10MBs += std::max<uint32_t>(5, static_cast<uint32_t>(avail10MBs * .4));
3610 #endif
3611
3612 return std::min<uint32_t>(maxSize, sz10MBs * 10 * 1024);
3613 }
3614
3615 nsresult
3616 CacheFileIOManager::UpdateSmartCacheSize()
3617 {
3618 MOZ_ASSERT(mIOThread->IsCurrentThread());
3619
3620 nsresult rv;
3621
3622 if (!CacheObserver::UseNewCache()) {
3623 return NS_ERROR_NOT_AVAILABLE;
3624 }
3625
3626 if (!CacheObserver::SmartCacheSizeEnabled()) {
3627 return NS_ERROR_NOT_AVAILABLE;
3628 }
3629
3630 // Wait at least kSmartSizeUpdateInterval before recomputing smart size.
3631 static const TimeDuration kUpdateLimit =
3632 TimeDuration::FromMilliseconds(kSmartSizeUpdateInterval);
3633 if (!mLastSmartSizeTime.IsNull() &&
3634 (TimeStamp::NowLoRes() - mLastSmartSizeTime) < kUpdateLimit) {
3635 return NS_OK;
3636 }
3637
3638 // Do not compute smart size when cache size is not reliable.
3639 bool isUpToDate = false;
3640 CacheIndex::IsUpToDate(&isUpToDate);
3641 if (!isUpToDate) {
3642 return NS_ERROR_NOT_AVAILABLE;
3643 }
3644
3645 uint32_t cacheUsage;
3646 rv = CacheIndex::GetCacheSize(&cacheUsage);
3647 if (NS_WARN_IF(NS_FAILED(rv))) {
3648 LOG(("CacheFileIOManager::UpdateSmartCacheSize() - Cannot get cacheUsage! "
3649 "[rv=0x%08x]", rv));
3650 return rv;
3651 }
3652
3653 int64_t avail;
3654 rv = mCacheDirectory->GetDiskSpaceAvailable(&avail);
3655 if (NS_WARN_IF(NS_FAILED(rv))) {
3656 // Do not change smart size.
3657 LOG(("CacheFileIOManager::UpdateSmartCacheSize() - GetDiskSpaceAvailable() "
3658 "failed! [rv=0x%08x]", rv));
3659 return rv;
3660 }
3661
3662 mLastSmartSizeTime = TimeStamp::NowLoRes();
3663
3664 uint32_t smartSize = SmartCacheSize(static_cast<uint32_t>(avail / 1024) +
3665 cacheUsage);
3666
3667 if (smartSize == (CacheObserver::DiskCacheCapacity() >> 10)) {
3668 // Smart size has not changed.
3669 return NS_OK;
3670 }
3671
3672 CacheObserver::SetDiskCacheCapacity(smartSize << 10);
3673
3674 return NS_OK;
3675 }
3676
3677 // Memory reporting
3678
3679 namespace { // anon
3680
3681 // A helper class that dispatches and waits for an event that gets result of
3682 // CacheFileIOManager->mHandles.SizeOfExcludingThis() on the I/O thread
3683 // to safely get handles memory report.
3684 // We must do this, since the handle list is only accessed and managed w/o
3685 // locking on the I/O thread. That is by design.
3686 class SizeOfHandlesRunnable : public nsRunnable
3687 {
3688 public:
3689 SizeOfHandlesRunnable(mozilla::MallocSizeOf mallocSizeOf,
3690 CacheFileHandles const &handles,
3691 nsTArray<CacheFileHandle *> const &specialHandles)
3692 : mMonitor("SizeOfHandlesRunnable.mMonitor")
3693 , mMallocSizeOf(mallocSizeOf)
3694 , mHandles(handles)
3695 , mSpecialHandles(specialHandles)
3696 {
3697 }
3698
3699 size_t Get(CacheIOThread* thread)
3700 {
3701 nsCOMPtr<nsIEventTarget> target = thread->Target();
3702 if (!target) {
3703 NS_ERROR("If we have the I/O thread we also must have the I/O target");
3704 return 0;
3705 }
3706
3707 mozilla::MonitorAutoLock mon(mMonitor);
3708 nsresult rv = target->Dispatch(this, nsIEventTarget::DISPATCH_NORMAL);
3709 if (NS_FAILED(rv)) {
3710 NS_ERROR("Dispatch failed, cannot do memory report of CacheFileHandles");
3711 return 0;
3712 }
3713
3714 mon.Wait();
3715 return mSize;
3716 }
3717
3718 NS_IMETHOD Run()
3719 {
3720 mozilla::MonitorAutoLock mon(mMonitor);
3721 // Excluding this since the object itself is a member of CacheFileIOManager
3722 // reported in CacheFileIOManager::SizeOfIncludingThis as part of |this|.
3723 mSize = mHandles.SizeOfExcludingThis(mMallocSizeOf);
3724 for (uint32_t i = 0; i < mSpecialHandles.Length(); ++i) {
3725 mSize += mSpecialHandles[i]->SizeOfIncludingThis(mMallocSizeOf);
3726 }
3727
3728 mon.Notify();
3729 return NS_OK;
3730 }
3731
3732 private:
3733 mozilla::Monitor mMonitor;
3734 mozilla::MallocSizeOf mMallocSizeOf;
3735 CacheFileHandles const &mHandles;
3736 nsTArray<CacheFileHandle *> const &mSpecialHandles;
3737 size_t mSize;
3738 };
3739
3740 } // anon
3741
3742 size_t
3743 CacheFileIOManager::SizeOfExcludingThisInternal(mozilla::MallocSizeOf mallocSizeOf) const
3744 {
3745 size_t n = 0;
3746 nsCOMPtr<nsISizeOf> sizeOf;
3747
3748 if (mIOThread) {
3749 n += mIOThread->SizeOfIncludingThis(mallocSizeOf);
3750
3751 // mHandles and mSpecialHandles must be accessed only on the I/O thread,
3752 // must sync dispatch.
3753 nsRefPtr<SizeOfHandlesRunnable> sizeOfHandlesRunnable =
3754 new SizeOfHandlesRunnable(mallocSizeOf, mHandles, mSpecialHandles);
3755 n += sizeOfHandlesRunnable->Get(mIOThread);
3756 }
3757
3758 // mHandlesByLastUsed just refers handles reported by mHandles.
3759
3760 sizeOf = do_QueryInterface(mCacheDirectory);
3761 if (sizeOf)
3762 n += sizeOf->SizeOfIncludingThis(mallocSizeOf);
3763
3764 sizeOf = do_QueryInterface(mMetadataWritesTimer);
3765 if (sizeOf)
3766 n += sizeOf->SizeOfIncludingThis(mallocSizeOf);
3767
3768 sizeOf = do_QueryInterface(mTrashTimer);
3769 if (sizeOf)
3770 n += sizeOf->SizeOfIncludingThis(mallocSizeOf);
3771
3772 sizeOf = do_QueryInterface(mTrashDir);
3773 if (sizeOf)
3774 n += sizeOf->SizeOfIncludingThis(mallocSizeOf);
3775
3776 for (uint32_t i = 0; i < mFailedTrashDirs.Length(); ++i) {
3777 n += mFailedTrashDirs[i].SizeOfExcludingThisIfUnshared(mallocSizeOf);
3778 }
3779
3780 return n;
3781 }
3782
3783 // static
3784 size_t
3785 CacheFileIOManager::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf)
3786 {
3787 if (!gInstance)
3788 return 0;
3789
3790 return gInstance->SizeOfExcludingThisInternal(mallocSizeOf);
3791 }
3792
3793 // static
3794 size_t
3795 CacheFileIOManager::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf)
3796 {
3797 return mallocSizeOf(gInstance) + SizeOfExcludingThis(mallocSizeOf);
3798 }
3799
3800 } // net
3801 } // mozilla

mercurial