Source code

Revision control

Other Tools

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 "nsICacheStorageVisitor.h"
23
#include "nsISizeOf.h"
24
#include "mozilla/net/MozURL.h"
25
#include "mozilla/Telemetry.h"
26
#include "mozilla/DebugOnly.h"
27
#include "mozilla/Services.h"
28
#include "nsDirectoryServiceUtils.h"
29
#include "nsAppDirectoryServiceDefs.h"
30
#include "private/pprio.h"
31
#include "mozilla/IntegerPrintfMacros.h"
32
#include "mozilla/Preferences.h"
33
#include "nsNetUtil.h"
34
35
// include files for ftruncate (or equivalent)
36
#if defined(XP_UNIX)
37
# include <unistd.h>
38
#elif defined(XP_WIN)
39
# include <windows.h>
40
# undef CreateFile
41
# undef CREATE_NEW
42
#else
43
// XXX add necessary include file for ftruncate (or equivalent)
44
#endif
45
46
namespace mozilla {
47
namespace net {
48
49
#define kOpenHandlesLimit 128
50
#define kMetadataWriteDelay 5000
51
#define kRemoveTrashStartDelay 60000 // in milliseconds
52
#define kSmartSizeUpdateInterval 60000 // in milliseconds
53
54
#ifdef ANDROID
55
const uint32_t kMaxCacheSizeKB = 512 * 1024; // 512 MB
56
#else
57
const uint32_t kMaxCacheSizeKB = 1024 * 1024; // 1 GB
58
#endif
59
const uint32_t kMaxClearOnShutdownCacheSizeKB = 150 * 1024; // 150 MB
60
61
bool CacheFileHandle::DispatchRelease() {
62
if (CacheFileIOManager::IsOnIOThreadOrCeased()) {
63
return false;
64
}
65
66
nsCOMPtr<nsIEventTarget> ioTarget = CacheFileIOManager::IOTarget();
67
if (!ioTarget) {
68
return false;
69
}
70
71
nsresult rv = ioTarget->Dispatch(
72
NewNonOwningRunnableMethod("net::CacheFileHandle::Release", this,
73
&CacheFileHandle::Release),
74
nsIEventTarget::DISPATCH_NORMAL);
75
if (NS_FAILED(rv)) {
76
return false;
77
}
78
79
return true;
80
}
81
82
NS_IMPL_ADDREF(CacheFileHandle)
83
NS_IMETHODIMP_(MozExternalRefCountType)
84
CacheFileHandle::Release() {
85
nsrefcnt count = mRefCnt - 1;
86
if (DispatchRelease()) {
87
// Redispatched to the IO thread.
88
return count;
89
}
90
91
MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
92
93
LOG(("CacheFileHandle::Release() [this=%p, refcnt=%" PRIuPTR "]", this,
94
mRefCnt.get()));
95
MOZ_ASSERT(0 != mRefCnt, "dup release");
96
count = --mRefCnt;
97
NS_LOG_RELEASE(this, count, "CacheFileHandle");
98
99
if (0 == count) {
100
mRefCnt = 1;
101
delete (this);
102
return 0;
103
}
104
105
return count;
106
}
107
108
NS_INTERFACE_MAP_BEGIN(CacheFileHandle)
109
NS_INTERFACE_MAP_ENTRY(nsISupports)
110
NS_INTERFACE_MAP_END
111
112
CacheFileHandle::CacheFileHandle(const SHA1Sum::Hash* aHash, bool aPriority,
113
PinningStatus aPinning)
114
: mHash(aHash),
115
mIsDoomed(false),
116
mClosed(false),
117
mPriority(aPriority),
118
mSpecialFile(false),
119
mInvalid(false),
120
mFileExists(false),
121
mDoomWhenFoundPinned(false),
122
mDoomWhenFoundNonPinned(false),
123
mKilled(false),
124
mPinning(aPinning),
125
mFileSize(-1),
126
mFD(nullptr) {
127
// If we initialize mDoomed in the initialization list, that initialization is
128
// not guaranteeded to be atomic. Whereas this assignment here is guaranteed
129
// to be atomic. TSan will see this (atomic) assignment and be satisfied
130
// that cross-thread accesses to mIsDoomed are properly synchronized.
131
mIsDoomed = false;
132
LOG((
133
"CacheFileHandle::CacheFileHandle() [this=%p, hash=%08x%08x%08x%08x%08x]",
134
this, LOGSHA1(aHash)));
135
}
136
137
CacheFileHandle::CacheFileHandle(const nsACString& aKey, bool aPriority,
138
PinningStatus aPinning)
139
: mHash(nullptr),
140
mIsDoomed(false),
141
mClosed(false),
142
mPriority(aPriority),
143
mSpecialFile(true),
144
mInvalid(false),
145
mFileExists(false),
146
mDoomWhenFoundPinned(false),
147
mDoomWhenFoundNonPinned(false),
148
mKilled(false),
149
mPinning(aPinning),
150
mFileSize(-1),
151
mFD(nullptr),
152
mKey(aKey) {
153
// See comment above about the initialization of mIsDoomed.
154
mIsDoomed = false;
155
LOG(("CacheFileHandle::CacheFileHandle() [this=%p, key=%s]", this,
156
PromiseFlatCString(aKey).get()));
157
}
158
159
CacheFileHandle::~CacheFileHandle() {
160
LOG(("CacheFileHandle::~CacheFileHandle() [this=%p]", this));
161
162
MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
163
164
RefPtr<CacheFileIOManager> ioMan = CacheFileIOManager::gInstance;
165
if (!IsClosed() && ioMan) {
166
ioMan->CloseHandleInternal(this);
167
}
168
}
169
170
void CacheFileHandle::Log() {
171
nsAutoCString leafName;
172
if (mFile) {
173
mFile->GetNativeLeafName(leafName);
174
}
175
176
if (mSpecialFile) {
177
LOG(
178
("CacheFileHandle::Log() - special file [this=%p, "
179
"isDoomed=%d, priority=%d, closed=%d, invalid=%d, "
180
"pinning=%" PRIu32 ", fileExists=%d, fileSize=%" PRId64
181
", leafName=%s, key=%s]",
182
this, bool(mIsDoomed), bool(mPriority), bool(mClosed), bool(mInvalid),
183
static_cast<uint32_t>(mPinning), bool(mFileExists), mFileSize,
184
leafName.get(), mKey.get()));
185
} else {
186
LOG(
187
("CacheFileHandle::Log() - entry file [this=%p, "
188
"hash=%08x%08x%08x%08x%08x, "
189
"isDoomed=%d, priority=%d, closed=%d, invalid=%d, "
190
"pinning=%" PRIu32 ", fileExists=%d, fileSize=%" PRId64
191
", leafName=%s, key=%s]",
192
this, LOGSHA1(mHash), bool(mIsDoomed), bool(mPriority), bool(mClosed),
193
bool(mInvalid), static_cast<uint32_t>(mPinning), bool(mFileExists),
194
mFileSize, leafName.get(), mKey.get()));
195
}
196
}
197
198
uint32_t CacheFileHandle::FileSizeInK() const {
199
MOZ_ASSERT(mFileSize != -1);
200
uint64_t size64 = mFileSize;
201
202
size64 += 0x3FF;
203
size64 >>= 10;
204
205
uint32_t size;
206
if (size64 >> 32) {
207
NS_WARNING(
208
"CacheFileHandle::FileSizeInK() - FileSize is too large, "
209
"truncating to PR_UINT32_MAX");
210
size = PR_UINT32_MAX;
211
} else {
212
size = static_cast<uint32_t>(size64);
213
}
214
215
return size;
216
}
217
218
bool CacheFileHandle::SetPinned(bool aPinned) {
219
LOG(("CacheFileHandle::SetPinned [this=%p, pinned=%d]", this, aPinned));
220
221
MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
222
223
mPinning = aPinned ? PinningStatus::PINNED : PinningStatus::NON_PINNED;
224
225
if ((MOZ_UNLIKELY(mDoomWhenFoundPinned) && aPinned) ||
226
(MOZ_UNLIKELY(mDoomWhenFoundNonPinned) && !aPinned)) {
227
LOG((" dooming, when: pinned=%d, non-pinned=%d, found: pinned=%d",
228
bool(mDoomWhenFoundPinned), bool(mDoomWhenFoundNonPinned), aPinned));
229
230
mDoomWhenFoundPinned = false;
231
mDoomWhenFoundNonPinned = false;
232
233
return false;
234
}
235
236
return true;
237
}
238
239
// Memory reporting
240
241
size_t CacheFileHandle::SizeOfExcludingThis(
242
mozilla::MallocSizeOf mallocSizeOf) const {
243
size_t n = 0;
244
nsCOMPtr<nsISizeOf> sizeOf;
245
246
sizeOf = do_QueryInterface(mFile);
247
if (sizeOf) {
248
n += sizeOf->SizeOfIncludingThis(mallocSizeOf);
249
}
250
251
n += mallocSizeOf(mFD);
252
n += mKey.SizeOfExcludingThisIfUnshared(mallocSizeOf);
253
return n;
254
}
255
256
size_t CacheFileHandle::SizeOfIncludingThis(
257
mozilla::MallocSizeOf mallocSizeOf) const {
258
return mallocSizeOf(this) + SizeOfExcludingThis(mallocSizeOf);
259
}
260
261
/******************************************************************************
262
* CacheFileHandles::HandleHashKey
263
*****************************************************************************/
264
265
void CacheFileHandles::HandleHashKey::AddHandle(CacheFileHandle* aHandle) {
266
MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
267
268
mHandles.InsertElementAt(0, aHandle);
269
}
270
271
void CacheFileHandles::HandleHashKey::RemoveHandle(CacheFileHandle* aHandle) {
272
MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
273
274
DebugOnly<bool> found;
275
found = mHandles.RemoveElement(aHandle);
276
MOZ_ASSERT(found);
277
}
278
279
already_AddRefed<CacheFileHandle>
280
CacheFileHandles::HandleHashKey::GetNewestHandle() {
281
MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
282
283
RefPtr<CacheFileHandle> handle;
284
if (mHandles.Length()) {
285
handle = mHandles[0];
286
}
287
288
return handle.forget();
289
}
290
291
void CacheFileHandles::HandleHashKey::GetHandles(
292
nsTArray<RefPtr<CacheFileHandle> >& aResult) {
293
MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
294
295
for (uint32_t i = 0; i < mHandles.Length(); ++i) {
296
CacheFileHandle* handle = mHandles[i];
297
aResult.AppendElement(handle);
298
}
299
}
300
301
#ifdef DEBUG
302
303
void CacheFileHandles::HandleHashKey::AssertHandlesState() {
304
for (uint32_t i = 0; i < mHandles.Length(); ++i) {
305
CacheFileHandle* handle = mHandles[i];
306
MOZ_ASSERT(handle->IsDoomed());
307
}
308
}
309
310
#endif
311
312
size_t CacheFileHandles::HandleHashKey::SizeOfExcludingThis(
313
mozilla::MallocSizeOf mallocSizeOf) const {
314
MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
315
316
size_t n = 0;
317
n += mallocSizeOf(mHash.get());
318
for (uint32_t i = 0; i < mHandles.Length(); ++i) {
319
n += mHandles[i]->SizeOfIncludingThis(mallocSizeOf);
320
}
321
322
return n;
323
}
324
325
/******************************************************************************
326
* CacheFileHandles
327
*****************************************************************************/
328
329
CacheFileHandles::CacheFileHandles() {
330
LOG(("CacheFileHandles::CacheFileHandles() [this=%p]", this));
331
MOZ_COUNT_CTOR(CacheFileHandles);
332
}
333
334
CacheFileHandles::~CacheFileHandles() {
335
LOG(("CacheFileHandles::~CacheFileHandles() [this=%p]", this));
336
MOZ_COUNT_DTOR(CacheFileHandles);
337
}
338
339
nsresult CacheFileHandles::GetHandle(const SHA1Sum::Hash* aHash,
340
CacheFileHandle** _retval) {
341
MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
342
MOZ_ASSERT(aHash);
343
344
#ifdef DEBUG_HANDLES
345
LOG(("CacheFileHandles::GetHandle() [hash=%08x%08x%08x%08x%08x]",
346
LOGSHA1(aHash)));
347
#endif
348
349
// find hash entry for key
350
HandleHashKey* entry = mTable.GetEntry(*aHash);
351
if (!entry) {
352
LOG(
353
("CacheFileHandles::GetHandle() hash=%08x%08x%08x%08x%08x "
354
"no handle entries found",
355
LOGSHA1(aHash)));
356
return NS_ERROR_NOT_AVAILABLE;
357
}
358
359
#ifdef DEBUG_HANDLES
360
Log(entry);
361
#endif
362
363
// Check if the entry is doomed
364
RefPtr<CacheFileHandle> handle = entry->GetNewestHandle();
365
if (!handle) {
366
LOG(
367
("CacheFileHandles::GetHandle() hash=%08x%08x%08x%08x%08x "
368
"no handle found %p, entry %p",
369
LOGSHA1(aHash), handle.get(), entry));
370
return NS_ERROR_NOT_AVAILABLE;
371
}
372
373
if (handle->IsDoomed()) {
374
LOG(
375
("CacheFileHandles::GetHandle() hash=%08x%08x%08x%08x%08x "
376
"found doomed handle %p, entry %p",
377
LOGSHA1(aHash), handle.get(), entry));
378
return NS_ERROR_NOT_AVAILABLE;
379
}
380
381
LOG(
382
("CacheFileHandles::GetHandle() hash=%08x%08x%08x%08x%08x "
383
"found handle %p, entry %p",
384
LOGSHA1(aHash), handle.get(), entry));
385
386
handle.forget(_retval);
387
return NS_OK;
388
}
389
390
nsresult CacheFileHandles::NewHandle(const SHA1Sum::Hash* aHash, bool aPriority,
391
CacheFileHandle::PinningStatus aPinning,
392
CacheFileHandle** _retval) {
393
MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
394
MOZ_ASSERT(aHash);
395
396
#ifdef DEBUG_HANDLES
397
LOG(("CacheFileHandles::NewHandle() [hash=%08x%08x%08x%08x%08x]",
398
LOGSHA1(aHash)));
399
#endif
400
401
// find hash entry for key
402
HandleHashKey* entry = mTable.PutEntry(*aHash);
403
404
#ifdef DEBUG_HANDLES
405
Log(entry);
406
#endif
407
408
#ifdef DEBUG
409
entry->AssertHandlesState();
410
#endif
411
412
RefPtr<CacheFileHandle> handle =
413
new CacheFileHandle(entry->Hash(), aPriority, aPinning);
414
entry->AddHandle(handle);
415
416
LOG(
417
("CacheFileHandles::NewHandle() hash=%08x%08x%08x%08x%08x "
418
"created new handle %p, entry=%p",
419
LOGSHA1(aHash), handle.get(), entry));
420
421
handle.forget(_retval);
422
return NS_OK;
423
}
424
425
void CacheFileHandles::RemoveHandle(CacheFileHandle* aHandle) {
426
MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
427
MOZ_ASSERT(aHandle);
428
429
if (!aHandle) {
430
return;
431
}
432
433
#ifdef DEBUG_HANDLES
434
LOG((
435
"CacheFileHandles::RemoveHandle() [handle=%p, hash=%08x%08x%08x%08x%08x]",
436
aHandle, LOGSHA1(aHandle->Hash())));
437
#endif
438
439
// find hash entry for key
440
HandleHashKey* entry = mTable.GetEntry(*aHandle->Hash());
441
if (!entry) {
442
MOZ_ASSERT(CacheFileIOManager::IsShutdown(),
443
"Should find entry when removing a handle before shutdown");
444
445
LOG(
446
("CacheFileHandles::RemoveHandle() hash=%08x%08x%08x%08x%08x "
447
"no entries found",
448
LOGSHA1(aHandle->Hash())));
449
return;
450
}
451
452
#ifdef DEBUG_HANDLES
453
Log(entry);
454
#endif
455
456
LOG(
457
("CacheFileHandles::RemoveHandle() hash=%08x%08x%08x%08x%08x "
458
"removing handle %p",
459
LOGSHA1(entry->Hash()), aHandle));
460
entry->RemoveHandle(aHandle);
461
462
if (entry->IsEmpty()) {
463
LOG(
464
("CacheFileHandles::RemoveHandle() hash=%08x%08x%08x%08x%08x "
465
"list is empty, removing entry %p",
466
LOGSHA1(entry->Hash()), entry));
467
mTable.RemoveEntry(entry);
468
}
469
}
470
471
void CacheFileHandles::GetAllHandles(
472
nsTArray<RefPtr<CacheFileHandle> >* _retval) {
473
MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
474
for (auto iter = mTable.Iter(); !iter.Done(); iter.Next()) {
475
iter.Get()->GetHandles(*_retval);
476
}
477
}
478
479
void CacheFileHandles::GetActiveHandles(
480
nsTArray<RefPtr<CacheFileHandle> >* _retval) {
481
MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
482
for (auto iter = mTable.Iter(); !iter.Done(); iter.Next()) {
483
RefPtr<CacheFileHandle> handle = iter.Get()->GetNewestHandle();
484
MOZ_ASSERT(handle);
485
486
if (!handle->IsDoomed()) {
487
_retval->AppendElement(handle);
488
}
489
}
490
}
491
492
void CacheFileHandles::ClearAll() {
493
MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
494
mTable.Clear();
495
}
496
497
uint32_t CacheFileHandles::HandleCount() { return mTable.Count(); }
498
499
#ifdef DEBUG_HANDLES
500
void CacheFileHandles::Log(CacheFileHandlesEntry* entry) {
501
LOG(("CacheFileHandles::Log() BEGIN [entry=%p]", entry));
502
503
nsTArray<RefPtr<CacheFileHandle> > array;
504
aEntry->GetHandles(array);
505
506
for (uint32_t i = 0; i < array.Length(); ++i) {
507
CacheFileHandle* handle = array[i];
508
handle->Log();
509
}
510
511
LOG(("CacheFileHandles::Log() END [entry=%p]", entry));
512
}
513
#endif
514
515
// Memory reporting
516
517
size_t CacheFileHandles::SizeOfExcludingThis(
518
mozilla::MallocSizeOf mallocSizeOf) const {
519
MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
520
521
return mTable.SizeOfExcludingThis(mallocSizeOf);
522
}
523
524
// Events
525
526
class ShutdownEvent : public Runnable {
527
public:
528
ShutdownEvent()
529
: Runnable("net::ShutdownEvent"),
530
mMonitor("ShutdownEvent.mMonitor"),
531
mNotified(false) {}
532
533
protected:
534
~ShutdownEvent() = default;
535
536
public:
537
NS_IMETHOD Run() override {
538
MonitorAutoLock mon(mMonitor);
539
540
CacheFileIOManager::gInstance->ShutdownInternal();
541
542
mNotified = true;
543
mon.Notify();
544
545
return NS_OK;
546
}
547
548
void PostAndWait() {
549
MonitorAutoLock mon(mMonitor);
550
551
DebugOnly<nsresult> rv;
552
rv = CacheFileIOManager::gInstance->mIOThread->Dispatch(
553
this,
554
CacheIOThread::WRITE); // When writes and closing of handles is done
555
MOZ_ASSERT(NS_SUCCEEDED(rv));
556
557
TimeDuration waitTime = TimeDuration::FromSeconds(1);
558
while (!mNotified) {
559
mon.Wait(waitTime);
560
if (!mNotified) {
561
// If there is any IO blocking on the IO thread, this will
562
// try to cancel it. Returns no later than after two seconds.
563
MonitorAutoUnlock unmon(mMonitor); // Prevent delays
564
CacheFileIOManager::gInstance->mIOThread->CancelBlockingIO();
565
}
566
}
567
}
568
569
protected:
570
mozilla::Monitor mMonitor;
571
bool mNotified;
572
};
573
574
// Class responsible for reporting IO performance stats
575
class IOPerfReportEvent {
576
public:
577
explicit IOPerfReportEvent(CacheFileUtils::CachePerfStats::EDataType aType)
578
: mType(aType), mEventCounter(0) {}
579
580
void Start(CacheIOThread* aIOThread) {
581
mStartTime = TimeStamp::Now();
582
mEventCounter = aIOThread->EventCounter();
583
}
584
585
void Report(CacheIOThread* aIOThread) {
586
if (mStartTime.IsNull()) {
587
return;
588
}
589
590
// Single IO operations can take less than 1ms. So we use microseconds to
591
// keep a good resolution of data.
592
uint32_t duration = (TimeStamp::Now() - mStartTime).ToMicroseconds();
593
594
// This is a simple prefiltering of values that might differ a lot from the
595
// average value. Do not add the value to the filtered stats when the event
596
// had to wait in a long queue.
597
uint32_t eventCounter = aIOThread->EventCounter();
598
bool shortOnly = eventCounter - mEventCounter < 5 ? false : true;
599
600
CacheFileUtils::CachePerfStats::AddValue(mType, duration, shortOnly);
601
}
602
603
protected:
604
CacheFileUtils::CachePerfStats::EDataType mType;
605
TimeStamp mStartTime;
606
uint32_t mEventCounter;
607
};
608
609
class OpenFileEvent : public Runnable, public IOPerfReportEvent {
610
public:
611
OpenFileEvent(const nsACString& aKey, uint32_t aFlags,
612
CacheFileIOListener* aCallback)
613
: Runnable("net::OpenFileEvent"),
614
IOPerfReportEvent(CacheFileUtils::CachePerfStats::IO_OPEN),
615
mFlags(aFlags),
616
mCallback(aCallback),
617
mKey(aKey) {
618
mIOMan = CacheFileIOManager::gInstance;
619
if (!(mFlags & CacheFileIOManager::SPECIAL_FILE)) {
620
Start(mIOMan->mIOThread);
621
}
622
}
623
624
protected:
625
~OpenFileEvent() = default;
626
627
public:
628
NS_IMETHOD Run() override {
629
nsresult rv = NS_OK;
630
631
if (!(mFlags & CacheFileIOManager::SPECIAL_FILE)) {
632
SHA1Sum sum;
633
sum.update(mKey.BeginReading(), mKey.Length());
634
sum.finish(mHash);
635
}
636
637
if (!mIOMan) {
638
rv = NS_ERROR_NOT_INITIALIZED;
639
} else {
640
if (mFlags & CacheFileIOManager::SPECIAL_FILE) {
641
rv = mIOMan->OpenSpecialFileInternal(mKey, mFlags,
642
getter_AddRefs(mHandle));
643
} else {
644
rv = mIOMan->OpenFileInternal(&mHash, mKey, mFlags,
645
getter_AddRefs(mHandle));
646
if (NS_SUCCEEDED(rv)) {
647
Report(mIOMan->mIOThread);
648
}
649
}
650
mIOMan = nullptr;
651
if (mHandle) {
652
if (mHandle->Key().IsEmpty()) {
653
mHandle->Key() = mKey;
654
}
655
}
656
}
657
658
mCallback->OnFileOpened(mHandle, rv);
659
return NS_OK;
660
}
661
662
protected:
663
SHA1Sum::Hash mHash;
664
uint32_t mFlags;
665
nsCOMPtr<CacheFileIOListener> mCallback;
666
RefPtr<CacheFileIOManager> mIOMan;
667
RefPtr<CacheFileHandle> mHandle;
668
nsCString mKey;
669
};
670
671
class ReadEvent : public Runnable, public IOPerfReportEvent {
672
public:
673
ReadEvent(CacheFileHandle* aHandle, int64_t aOffset, char* aBuf,
674
int32_t aCount, CacheFileIOListener* aCallback)
675
: Runnable("net::ReadEvent"),
676
IOPerfReportEvent(CacheFileUtils::CachePerfStats::IO_READ),
677
mHandle(aHandle),
678
mOffset(aOffset),
679
mBuf(aBuf),
680
mCount(aCount),
681
mCallback(aCallback) {
682
if (!mHandle->IsSpecialFile()) {
683
Start(CacheFileIOManager::gInstance->mIOThread);
684
}
685
}
686
687
protected:
688
~ReadEvent() = default;
689
690
public:
691
NS_IMETHOD Run() override {
692
nsresult rv;
693
694
if (mHandle->IsClosed() || (mCallback && mCallback->IsKilled())) {
695
rv = NS_ERROR_NOT_INITIALIZED;
696
} else {
697
rv = CacheFileIOManager::gInstance->ReadInternal(mHandle, mOffset, mBuf,
698
mCount);
699
if (NS_SUCCEEDED(rv)) {
700
Report(CacheFileIOManager::gInstance->mIOThread);
701
}
702
}
703
704
mCallback->OnDataRead(mHandle, mBuf, rv);
705
return NS_OK;
706
}
707
708
protected:
709
RefPtr<CacheFileHandle> mHandle;
710
int64_t mOffset;
711
char* mBuf;
712
int32_t mCount;
713
nsCOMPtr<CacheFileIOListener> mCallback;
714
};
715
716
class WriteEvent : public Runnable, public IOPerfReportEvent {
717
public:
718
WriteEvent(CacheFileHandle* aHandle, int64_t aOffset, const char* aBuf,
719
int32_t aCount, bool aValidate, bool aTruncate,
720
CacheFileIOListener* aCallback)
721
: Runnable("net::WriteEvent"),
722
IOPerfReportEvent(CacheFileUtils::CachePerfStats::IO_WRITE),
723
mHandle(aHandle),
724
mOffset(aOffset),
725
mBuf(aBuf),
726
mCount(aCount),
727
mValidate(aValidate),
728
mTruncate(aTruncate),
729
mCallback(aCallback) {
730
if (!mHandle->IsSpecialFile()) {
731
Start(CacheFileIOManager::gInstance->mIOThread);
732
}
733
}
734
735
protected:
736
~WriteEvent() {
737
if (!mCallback && mBuf) {
738
free(const_cast<char*>(mBuf));
739
}
740
}
741
742
public:
743
NS_IMETHOD Run() override {
744
nsresult rv;
745
746
if (mHandle->IsClosed() || (mCallback && mCallback->IsKilled())) {
747
// We usually get here only after the internal shutdown
748
// (i.e. mShuttingDown == true). Pretend write has succeeded
749
// to avoid any past-shutdown file dooming.
750
rv = (CacheObserver::IsPastShutdownIOLag() ||
751
CacheFileIOManager::gInstance->mShuttingDown)
752
? NS_OK
753
: NS_ERROR_NOT_INITIALIZED;
754
} else {
755
rv = CacheFileIOManager::gInstance->WriteInternal(
756
mHandle, mOffset, mBuf, mCount, mValidate, mTruncate);
757
if (NS_SUCCEEDED(rv)) {
758
Report(CacheFileIOManager::gInstance->mIOThread);
759
}
760
if (NS_FAILED(rv) && !mCallback) {
761
// No listener is going to handle the error, doom the file
762
CacheFileIOManager::gInstance->DoomFileInternal(mHandle);
763
}
764
}
765
if (mCallback) {
766
mCallback->OnDataWritten(mHandle, mBuf, rv);
767
} else {
768
free(const_cast<char*>(mBuf));
769
mBuf = nullptr;
770
}
771
772
return NS_OK;
773
}
774
775
protected:
776
RefPtr<CacheFileHandle> mHandle;
777
int64_t mOffset;
778
const char* mBuf;
779
int32_t mCount;
780
bool mValidate : 1;
781
bool mTruncate : 1;
782
nsCOMPtr<CacheFileIOListener> mCallback;
783
};
784
785
class DoomFileEvent : public Runnable {
786
public:
787
DoomFileEvent(CacheFileHandle* aHandle, CacheFileIOListener* aCallback)
788
: Runnable("net::DoomFileEvent"),
789
mCallback(aCallback),
790
mHandle(aHandle) {}
791
792
protected:
793
~DoomFileEvent() = default;
794
795
public:
796
NS_IMETHOD Run() override {
797
nsresult rv;
798
799
if (mHandle->IsClosed()) {
800
rv = NS_ERROR_NOT_INITIALIZED;
801
} else {
802
rv = CacheFileIOManager::gInstance->DoomFileInternal(mHandle);
803
}
804
805
if (mCallback) {
806
mCallback->OnFileDoomed(mHandle, rv);
807
}
808
809
return NS_OK;
810
}
811
812
protected:
813
nsCOMPtr<CacheFileIOListener> mCallback;
814
nsCOMPtr<nsIEventTarget> mTarget;
815
RefPtr<CacheFileHandle> mHandle;
816
};
817
818
class DoomFileByKeyEvent : public Runnable {
819
public:
820
DoomFileByKeyEvent(const nsACString& aKey, CacheFileIOListener* aCallback)
821
: Runnable("net::DoomFileByKeyEvent"), mCallback(aCallback) {
822
SHA1Sum sum;
823
sum.update(aKey.BeginReading(), aKey.Length());
824
sum.finish(mHash);
825
826
mIOMan = CacheFileIOManager::gInstance;
827
}
828
829
protected:
830
~DoomFileByKeyEvent() = default;
831
832
public:
833
NS_IMETHOD Run() override {
834
nsresult rv;
835
836
if (!mIOMan) {
837
rv = NS_ERROR_NOT_INITIALIZED;
838
} else {
839
rv = mIOMan->DoomFileByKeyInternal(&mHash);
840
mIOMan = nullptr;
841
}
842
843
if (mCallback) {
844
mCallback->OnFileDoomed(nullptr, rv);
845
}
846
847
return NS_OK;
848
}
849
850
protected:
851
SHA1Sum::Hash mHash;
852
nsCOMPtr<CacheFileIOListener> mCallback;
853
RefPtr<CacheFileIOManager> mIOMan;
854
};
855
856
class ReleaseNSPRHandleEvent : public Runnable {
857
public:
858
explicit ReleaseNSPRHandleEvent(CacheFileHandle* aHandle)
859
: Runnable("net::ReleaseNSPRHandleEvent"), mHandle(aHandle) {}
860
861
protected:
862
~ReleaseNSPRHandleEvent() = default;
863
864
public:
865
NS_IMETHOD Run() override {
866
if (!mHandle->IsClosed()) {
867
CacheFileIOManager::gInstance->MaybeReleaseNSPRHandleInternal(mHandle);
868
}
869
870
return NS_OK;
871
}
872
873
protected:
874
RefPtr<CacheFileHandle> mHandle;
875
};
876
877
class TruncateSeekSetEOFEvent : public Runnable {
878
public:
879
TruncateSeekSetEOFEvent(CacheFileHandle* aHandle, int64_t aTruncatePos,
880
int64_t aEOFPos, CacheFileIOListener* aCallback)
881
: Runnable("net::TruncateSeekSetEOFEvent"),
882
mHandle(aHandle),
883
mTruncatePos(aTruncatePos),
884
mEOFPos(aEOFPos),
885
mCallback(aCallback) {}
886
887
protected:
888
~TruncateSeekSetEOFEvent() = default;
889
890
public:
891
NS_IMETHOD Run() override {
892
nsresult rv;
893
894
if (mHandle->IsClosed() || (mCallback && mCallback->IsKilled())) {
895
rv = NS_ERROR_NOT_INITIALIZED;
896
} else {
897
rv = CacheFileIOManager::gInstance->TruncateSeekSetEOFInternal(
898
mHandle, mTruncatePos, mEOFPos);
899
}
900
901
if (mCallback) {
902
mCallback->OnEOFSet(mHandle, rv);
903
}
904
905
return NS_OK;
906
}
907
908
protected:
909
RefPtr<CacheFileHandle> mHandle;
910
int64_t mTruncatePos;
911
int64_t mEOFPos;
912
nsCOMPtr<CacheFileIOListener> mCallback;
913
};
914
915
class RenameFileEvent : public Runnable {
916
public:
917
RenameFileEvent(CacheFileHandle* aHandle, const nsACString& aNewName,
918
CacheFileIOListener* aCallback)
919
: Runnable("net::RenameFileEvent"),
920
mHandle(aHandle),
921
mNewName(aNewName),
922
mCallback(aCallback) {}
923
924
protected:
925
~RenameFileEvent() = default;
926
927
public:
928
NS_IMETHOD Run() override {
929
nsresult rv;
930
931
if (mHandle->IsClosed()) {
932
rv = NS_ERROR_NOT_INITIALIZED;
933
} else {
934
rv = CacheFileIOManager::gInstance->RenameFileInternal(mHandle, mNewName);
935
}
936
937
if (mCallback) {
938
mCallback->OnFileRenamed(mHandle, rv);
939
}
940
941
return NS_OK;
942
}
943
944
protected:
945
RefPtr<CacheFileHandle> mHandle;
946
nsCString mNewName;
947
nsCOMPtr<CacheFileIOListener> mCallback;
948
};
949
950
class InitIndexEntryEvent : public Runnable {
951
public:
952
InitIndexEntryEvent(CacheFileHandle* aHandle,
953
OriginAttrsHash aOriginAttrsHash, bool aAnonymous,
954
bool aPinning)
955
: Runnable("net::InitIndexEntryEvent"),
956
mHandle(aHandle),
957
mOriginAttrsHash(aOriginAttrsHash),
958
mAnonymous(aAnonymous),
959
mPinning(aPinning) {}
960
961
protected:
962
~InitIndexEntryEvent() = default;
963
964
public:
965
NS_IMETHOD Run() override {
966
if (mHandle->IsClosed() || mHandle->IsDoomed()) {
967
return NS_OK;
968
}
969
970
CacheIndex::InitEntry(mHandle->Hash(), mOriginAttrsHash, mAnonymous,
971
mPinning);
972
973
// We cannot set the filesize before we init the entry. If we're opening
974
// an existing entry file, frecency will be set after parsing the entry
975
// file, but we must set the filesize here since nobody is going to set it
976
// if there is no write to the file.
977
uint32_t sizeInK = mHandle->FileSizeInK();
978
CacheIndex::UpdateEntry(mHandle->Hash(), nullptr, nullptr, nullptr, nullptr,
979
nullptr, nullptr, 0, &sizeInK);
980
981
return NS_OK;
982
}
983
984
protected:
985
RefPtr<CacheFileHandle> mHandle;
986
OriginAttrsHash mOriginAttrsHash;
987
bool mAnonymous;
988
bool mPinning;
989
};
990
991
class UpdateIndexEntryEvent : public Runnable {
992
public:
993
UpdateIndexEntryEvent(CacheFileHandle* aHandle, const uint32_t* aFrecency,
994
const bool* aHasAltData, const uint16_t* aOnStartTime,
995
const uint16_t* aOnStopTime,
996
const uint8_t* aContentType,
997
const uint16_t* aBaseDomainAccessCount,
998
const uint32_t aTelemetryReportID)
999
: Runnable("net::UpdateIndexEntryEvent"),
1000
mHandle(aHandle),
1001
mHasFrecency(false),
1002
mHasHasAltData(false),
1003
mHasOnStartTime(false),
1004
mHasOnStopTime(false),
1005
mHasContentType(false),
1006
mHasBaseDomainAccessCount(false),
1007
mFrecency(0),
1008
mHasAltData(false),
1009
mOnStartTime(0),
1010
mOnStopTime(0),
1011
mContentType(nsICacheEntry::CONTENT_TYPE_UNKNOWN),
1012
mBaseDomainAccessCount(0),
1013
mTelemetryReportID(aTelemetryReportID) {
1014
if (aFrecency) {
1015
mHasFrecency = true;
1016
mFrecency = *aFrecency;
1017
}
1018
if (aHasAltData) {
1019
mHasHasAltData = true;
1020
mHasAltData = *aHasAltData;
1021
}
1022
if (aOnStartTime) {
1023
mHasOnStartTime = true;
1024
mOnStartTime = *aOnStartTime;
1025
}
1026
if (aOnStopTime) {
1027
mHasOnStopTime = true;
1028
mOnStopTime = *aOnStopTime;
1029
}
1030
if (aContentType) {
1031
mHasContentType = true;
1032
mContentType = *aContentType;
1033
}
1034
if (aBaseDomainAccessCount) {
1035
mHasBaseDomainAccessCount = true;
1036
mBaseDomainAccessCount = *aBaseDomainAccessCount;
1037
}
1038
}
1039
1040
protected:
1041
~UpdateIndexEntryEvent() = default;
1042
1043
public:
1044
NS_IMETHOD Run() override {
1045
if (mHandle->IsClosed() || mHandle->IsDoomed()) {
1046
return NS_OK;
1047
}
1048
1049
CacheIndex::UpdateEntry(
1050
mHandle->Hash(), mHasFrecency ? &mFrecency : nullptr,
1051
mHasHasAltData ? &mHasAltData : nullptr,
1052
mHasOnStartTime ? &mOnStartTime : nullptr,
1053
mHasOnStopTime ? &mOnStopTime : nullptr,
1054
mHasContentType ? &mContentType : nullptr,
1055
mHasBaseDomainAccessCount ? &mBaseDomainAccessCount : nullptr,
1056
mTelemetryReportID, nullptr);
1057
return NS_OK;
1058
}
1059
1060
protected:
1061
RefPtr<CacheFileHandle> mHandle;
1062
1063
bool mHasFrecency;
1064
bool mHasHasAltData;
1065
bool mHasOnStartTime;
1066
bool mHasOnStopTime;
1067
bool mHasContentType;
1068
bool mHasBaseDomainAccessCount;
1069
1070
uint32_t mFrecency;
1071
bool mHasAltData;
1072
uint16_t mOnStartTime;
1073
uint16_t mOnStopTime;
1074
uint8_t mContentType;
1075
uint16_t mBaseDomainAccessCount;
1076
uint32_t mTelemetryReportID;
1077
};
1078
1079
class MetadataWriteScheduleEvent : public Runnable {
1080
public:
1081
enum EMode { SCHEDULE, UNSCHEDULE, SHUTDOWN } mMode;
1082
1083
RefPtr<CacheFile> mFile;
1084
RefPtr<CacheFileIOManager> mIOMan;
1085
1086
MetadataWriteScheduleEvent(CacheFileIOManager* aManager, CacheFile* aFile,
1087
EMode aMode)
1088
: Runnable("net::MetadataWriteScheduleEvent"),
1089
mMode(aMode),
1090
mFile(aFile),
1091
mIOMan(aManager) {}
1092
1093
virtual ~MetadataWriteScheduleEvent() = default;
1094
1095
NS_IMETHOD Run() override {
1096
RefPtr<CacheFileIOManager> ioMan = CacheFileIOManager::gInstance;
1097
if (!ioMan) {
1098
NS_WARNING(
1099
"CacheFileIOManager already gone in "
1100
"MetadataWriteScheduleEvent::Run()");
1101
return NS_OK;
1102
}
1103
1104
switch (mMode) {
1105
case SCHEDULE:
1106
ioMan->ScheduleMetadataWriteInternal(mFile);
1107
break;
1108
case UNSCHEDULE:
1109
ioMan->UnscheduleMetadataWriteInternal(mFile);
1110
break;
1111
case SHUTDOWN:
1112
ioMan->ShutdownMetadataWriteSchedulingInternal();
1113
break;
1114
}
1115
return NS_OK;
1116
}
1117
};
1118
1119
StaticRefPtr<CacheFileIOManager> CacheFileIOManager::gInstance;
1120
1121
NS_IMPL_ISUPPORTS(CacheFileIOManager, nsITimerCallback, nsINamed)
1122
1123
CacheFileIOManager::CacheFileIOManager()
1124
: mShuttingDown(false),
1125
mTreeCreated(false),
1126
mTreeCreationFailed(false),
1127
mOverLimitEvicting(false),
1128
mCacheSizeOnHardLimit(false),
1129
mRemovingTrashDirs(false) {
1130
LOG(("CacheFileIOManager::CacheFileIOManager [this=%p]", this));
1131
MOZ_ASSERT(!gInstance, "multiple CacheFileIOManager instances!");
1132
}
1133
1134
CacheFileIOManager::~CacheFileIOManager() {
1135
LOG(("CacheFileIOManager::~CacheFileIOManager [this=%p]", this));
1136
}
1137
1138
// static
1139
nsresult CacheFileIOManager::Init() {
1140
LOG(("CacheFileIOManager::Init()"));
1141
1142
MOZ_ASSERT(NS_IsMainThread());
1143
1144
if (gInstance) {
1145
return NS_ERROR_ALREADY_INITIALIZED;
1146
}
1147
1148
RefPtr<CacheFileIOManager> ioMan = new CacheFileIOManager();
1149
1150
nsresult rv = ioMan->InitInternal();
1151
NS_ENSURE_SUCCESS(rv, rv);
1152
1153
gInstance = ioMan.forget();
1154
return NS_OK;
1155
}
1156
1157
nsresult CacheFileIOManager::InitInternal() {
1158
nsresult rv;
1159
1160
mIOThread = new CacheIOThread();
1161
1162
rv = mIOThread->Init();
1163
MOZ_ASSERT(NS_SUCCEEDED(rv), "Can't create background thread");
1164
NS_ENSURE_SUCCESS(rv, rv);
1165
1166
mStartTime = TimeStamp::NowLoRes();
1167
1168
return NS_OK;
1169
}
1170
1171
// static
1172
nsresult CacheFileIOManager::Shutdown() {
1173
LOG(("CacheFileIOManager::Shutdown() [gInstance=%p]", gInstance.get()));
1174
1175
MOZ_ASSERT(NS_IsMainThread());
1176
1177
if (!gInstance) {
1178
return NS_ERROR_NOT_INITIALIZED;
1179
}
1180
1181
Telemetry::AutoTimer<Telemetry::NETWORK_DISK_CACHE_SHUTDOWN_V2> shutdownTimer;
1182
1183
CacheIndex::PreShutdown();
1184
1185
ShutdownMetadataWriteScheduling();
1186
1187
RefPtr<ShutdownEvent> ev = new ShutdownEvent();
1188
ev->PostAndWait();
1189
1190
MOZ_ASSERT(gInstance->mHandles.HandleCount() == 0);
1191
MOZ_ASSERT(gInstance->mHandlesByLastUsed.Length() == 0);
1192
1193
if (gInstance->mIOThread) {
1194
gInstance->mIOThread->Shutdown();
1195
}
1196
1197
CacheIndex::Shutdown();
1198
1199
if (CacheObserver::ClearCacheOnShutdown()) {
1200
Telemetry::AutoTimer<Telemetry::NETWORK_DISK_CACHE2_SHUTDOWN_CLEAR_PRIVATE>
1201
totalTimer;
1202
gInstance->SyncRemoveAllCacheFiles();
1203
}
1204
1205
gInstance = nullptr;
1206
1207
return NS_OK;
1208
}
1209
1210
nsresult CacheFileIOManager::ShutdownInternal() {
1211
LOG(("CacheFileIOManager::ShutdownInternal() [this=%p]", this));
1212
1213
MOZ_ASSERT(mIOThread->IsCurrentThread());
1214
1215
// No new handles can be created after this flag is set
1216
mShuttingDown = true;
1217
1218
if (mTrashTimer) {
1219
mTrashTimer->Cancel();
1220
mTrashTimer = nullptr;
1221
}
1222
1223
// close all handles and delete all associated files
1224
nsTArray<RefPtr<CacheFileHandle> > handles;
1225
mHandles.GetAllHandles(&handles);
1226
handles.AppendElements(mSpecialHandles);
1227
1228
for (uint32_t i = 0; i < handles.Length(); i++) {
1229
CacheFileHandle* h = handles[i];
1230
h->mClosed = true;
1231
1232
h->Log();
1233
1234
// Close completely written files.
1235
MaybeReleaseNSPRHandleInternal(h);
1236
// Don't bother removing invalid and/or doomed files to improve
1237
// shutdown perfomrance.
1238
// Doomed files are already in the doomed directory from which
1239
// we never reuse files and delete the dir on next session startup.
1240
// Invalid files don't have metadata and thus won't load anyway
1241
// (hashes won't match).
1242
1243
if (!h->IsSpecialFile() && !h->mIsDoomed && !h->mFileExists) {
1244
CacheIndex::RemoveEntry(h->Hash());
1245
}
1246
1247
// Remove the handle from mHandles/mSpecialHandles
1248
if (h->IsSpecialFile()) {
1249
mSpecialHandles.RemoveElement(h);
1250
} else {
1251
mHandles.RemoveHandle(h);
1252
}
1253
1254
// Pointer to the hash is no longer valid once the last handle with the
1255
// given hash is released. Null out the pointer so that we crash if there
1256
// is a bug in this code and we dereference the pointer after this point.
1257
if (!h->IsSpecialFile()) {
1258
h->mHash = nullptr;
1259
}
1260
}
1261
1262
// Assert the table is empty. When we are here, no new handles can be added
1263
// and handles will no longer remove them self from this table and we don't
1264
// want to keep invalid handles here. Also, there is no lookup after this
1265
// point to happen.
1266
MOZ_ASSERT(mHandles.HandleCount() == 0);
1267
1268
// Release trash directory enumerator
1269
if (mTrashDirEnumerator) {
1270
mTrashDirEnumerator->Close();
1271
mTrashDirEnumerator = nullptr;
1272
}
1273
1274
if (mContextEvictor) {
1275
mContextEvictor->Shutdown();
1276
mContextEvictor = nullptr;
1277
}
1278
1279
return NS_OK;
1280
}
1281
1282
// static
1283
nsresult CacheFileIOManager::OnProfile() {
1284
LOG(("CacheFileIOManager::OnProfile() [gInstance=%p]", gInstance.get()));
1285
1286
RefPtr<CacheFileIOManager> ioMan = gInstance;
1287
if (!ioMan) {
1288
// CacheFileIOManager::Init() failed, probably could not create the IO
1289
// thread, just go with it...
1290
return NS_ERROR_NOT_INITIALIZED;
1291
}
1292
1293
nsresult rv;
1294
1295
nsCOMPtr<nsIFile> directory;
1296
1297
CacheObserver::ParentDirOverride(getter_AddRefs(directory));
1298
1299
#if defined(MOZ_WIDGET_ANDROID)
1300
nsCOMPtr<nsIFile> profilelessDirectory;
1301
char* cachePath = getenv("CACHE_DIRECTORY");
1302
if (!directory && cachePath && *cachePath) {
1303
rv = NS_NewNativeLocalFile(nsDependentCString(cachePath), true,
1304
getter_AddRefs(directory));
1305
if (NS_SUCCEEDED(rv)) {
1306
// Save this directory as the profileless path.
1307
rv = directory->Clone(getter_AddRefs(profilelessDirectory));
1308
NS_ENSURE_SUCCESS(rv, rv);
1309
1310
// Add profile leaf name to the directory name to distinguish
1311
// multiple profiles Fennec supports.
1312
nsCOMPtr<nsIFile> profD;
1313
rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
1314
getter_AddRefs(profD));
1315
1316
nsAutoCString leafName;
1317
if (NS_SUCCEEDED(rv)) {
1318
rv = profD->GetNativeLeafName(leafName);
1319
}
1320
if (NS_SUCCEEDED(rv)) {
1321
rv = directory->AppendNative(leafName);
1322
}
1323
if (NS_FAILED(rv)) {
1324
directory = nullptr;
1325
}
1326
}
1327
}
1328
#endif
1329
1330
if (!directory) {
1331
rv = NS_GetSpecialDirectory(NS_APP_CACHE_PARENT_DIR,
1332
getter_AddRefs(directory));
1333
}
1334
1335
if (!directory) {
1336
rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR,
1337
getter_AddRefs(directory));
1338
}
1339
1340
if (directory) {
1341
rv = directory->Append(NS_LITERAL_STRING("cache2"));
1342
NS_ENSURE_SUCCESS(rv, rv);
1343
}
1344
1345
// All functions return a clone.
1346
ioMan->mCacheDirectory.swap(directory);
1347
1348
#if defined(MOZ_WIDGET_ANDROID)
1349
if (profilelessDirectory) {
1350
rv = profilelessDirectory->Append(NS_LITERAL_STRING("cache2"));
1351
NS_ENSURE_SUCCESS(rv, rv);
1352
}
1353
1354
ioMan->mCacheProfilelessDirectory.swap(profilelessDirectory);
1355
#endif
1356
1357
if (ioMan->mCacheDirectory) {
1358
CacheIndex::Init(ioMan->mCacheDirectory);
1359
}
1360
1361
return NS_OK;
1362
}
1363
1364
// static
1365
already_AddRefed<nsIEventTarget> CacheFileIOManager::IOTarget() {
1366
nsCOMPtr<nsIEventTarget> target;
1367
if (gInstance && gInstance->mIOThread) {
1368
target = gInstance->mIOThread->Target();
1369
}
1370
1371
return target.forget();
1372
}
1373
1374
// static
1375
already_AddRefed<CacheIOThread> CacheFileIOManager::IOThread() {
1376
RefPtr<CacheIOThread> thread;
1377
if (gInstance) {
1378
thread = gInstance->mIOThread;
1379
}
1380
1381
return thread.forget();
1382
}
1383
1384
// static
1385
bool CacheFileIOManager::IsOnIOThread() {
1386
RefPtr<CacheFileIOManager> ioMan = gInstance;
1387
if (ioMan && ioMan->mIOThread) {
1388
return ioMan->mIOThread->IsCurrentThread();
1389
}
1390
1391
return false;
1392
}
1393
1394
// static
1395
bool CacheFileIOManager::IsOnIOThreadOrCeased() {
1396
RefPtr<CacheFileIOManager> ioMan = gInstance;
1397
if (ioMan && ioMan->mIOThread) {
1398
return ioMan->mIOThread->IsCurrentThread();
1399
}
1400
1401
// Ceased...
1402
return true;
1403
}
1404
1405
// static
1406
bool CacheFileIOManager::IsShutdown() {
1407
if (!gInstance) {
1408
return true;
1409
}
1410
return gInstance->mShuttingDown;
1411
}
1412
1413
// static
1414
nsresult CacheFileIOManager::ScheduleMetadataWrite(CacheFile* aFile) {
1415
RefPtr<CacheFileIOManager> ioMan = gInstance;
1416
NS_ENSURE_TRUE(ioMan, NS_ERROR_NOT_INITIALIZED);
1417
1418
NS_ENSURE_TRUE(!ioMan->mShuttingDown, NS_ERROR_NOT_INITIALIZED);
1419
1420
RefPtr<MetadataWriteScheduleEvent> event = new MetadataWriteScheduleEvent(
1421
ioMan, aFile, MetadataWriteScheduleEvent::SCHEDULE);
1422
nsCOMPtr<nsIEventTarget> target = ioMan->IOTarget();
1423
NS_ENSURE_TRUE(target, NS_ERROR_UNEXPECTED);
1424
return target->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL);
1425
}
1426
1427
nsresult CacheFileIOManager::ScheduleMetadataWriteInternal(CacheFile* aFile) {
1428
MOZ_ASSERT(IsOnIOThreadOrCeased());
1429
1430
nsresult rv;
1431
1432
if (!mMetadataWritesTimer) {
1433
rv = NS_NewTimerWithCallback(getter_AddRefs(mMetadataWritesTimer), this,
1434
kMetadataWriteDelay, nsITimer::TYPE_ONE_SHOT);
1435
NS_ENSURE_SUCCESS(rv, rv);
1436
}
1437
1438
if (mScheduledMetadataWrites.IndexOf(aFile) !=
1439
mScheduledMetadataWrites.NoIndex) {
1440
return NS_OK;
1441
}
1442
1443
mScheduledMetadataWrites.AppendElement(aFile);
1444
1445
return NS_OK;
1446
}
1447
1448
// static
1449
nsresult CacheFileIOManager::UnscheduleMetadataWrite(CacheFile* aFile) {
1450
RefPtr<CacheFileIOManager> ioMan = gInstance;
1451
NS_ENSURE_TRUE(ioMan, NS_ERROR_NOT_INITIALIZED);
1452
1453
NS_ENSURE_TRUE(!ioMan->mShuttingDown, NS_ERROR_NOT_INITIALIZED);
1454
1455
RefPtr<MetadataWriteScheduleEvent> event = new MetadataWriteScheduleEvent(
1456
ioMan, aFile, MetadataWriteScheduleEvent::UNSCHEDULE);
1457
nsCOMPtr<nsIEventTarget> target = ioMan->IOTarget();
1458
NS_ENSURE_TRUE(target, NS_ERROR_UNEXPECTED);
1459
return target->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL);
1460
}
1461
1462
nsresult CacheFileIOManager::UnscheduleMetadataWriteInternal(CacheFile* aFile) {
1463
MOZ_ASSERT(IsOnIOThreadOrCeased());
1464
1465
mScheduledMetadataWrites.RemoveElement(aFile);
1466
1467
if (mScheduledMetadataWrites.Length() == 0 && mMetadataWritesTimer) {
1468
mMetadataWritesTimer->Cancel();
1469
mMetadataWritesTimer = nullptr;
1470
}
1471
1472
return NS_OK;
1473
}
1474
1475
// static
1476
nsresult CacheFileIOManager::ShutdownMetadataWriteScheduling() {
1477
RefPtr<CacheFileIOManager> ioMan = gInstance;
1478
NS_ENSURE_TRUE(ioMan, NS_ERROR_NOT_INITIALIZED);
1479
1480
RefPtr<MetadataWriteScheduleEvent> event = new MetadataWriteScheduleEvent(
1481
ioMan, nullptr, MetadataWriteScheduleEvent::SHUTDOWN);
1482
nsCOMPtr<nsIEventTarget> target = ioMan->IOTarget();
1483
NS_ENSURE_TRUE(target, NS_ERROR_UNEXPECTED);
1484
return target->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL);
1485
}
1486
1487
nsresult CacheFileIOManager::ShutdownMetadataWriteSchedulingInternal() {
1488
MOZ_ASSERT(IsOnIOThreadOrCeased());
1489
1490
nsTArray<RefPtr<CacheFile> > files;
1491
files.SwapElements(mScheduledMetadataWrites);
1492
for (uint32_t i = 0; i < files.Length(); ++i) {
1493
CacheFile* file = files[i];
1494
file->WriteMetadataIfNeeded();
1495
}
1496
1497
if (mMetadataWritesTimer) {
1498
mMetadataWritesTimer->Cancel();
1499
mMetadataWritesTimer = nullptr;
1500
}
1501
1502
return NS_OK;
1503
}
1504
1505
NS_IMETHODIMP
1506
CacheFileIOManager::Notify(nsITimer* aTimer) {
1507
MOZ_ASSERT(IsOnIOThreadOrCeased());
1508
MOZ_ASSERT(mMetadataWritesTimer == aTimer);
1509
1510
mMetadataWritesTimer = nullptr;
1511
1512
nsTArray<RefPtr<CacheFile> > files;
1513
files.SwapElements(mScheduledMetadataWrites);
1514
for (uint32_t i = 0; i < files.Length(); ++i) {
1515
CacheFile* file = files[i];
1516
file->WriteMetadataIfNeeded();
1517
}
1518
1519
return NS_OK;
1520
}
1521
1522
NS_IMETHODIMP
1523
CacheFileIOManager::GetName(nsACString& aName) {
1524
aName.AssignLiteral("CacheFileIOManager");
1525
return NS_OK;
1526
}
1527
1528
// static
1529
nsresult CacheFileIOManager::OpenFile(const nsACString& aKey, uint32_t aFlags,
1530
CacheFileIOListener* aCallback) {
1531
LOG(("CacheFileIOManager::OpenFile() [key=%s, flags=%d, listener=%p]",
1532
PromiseFlatCString(aKey).get(), aFlags, aCallback));
1533
1534
nsresult rv;
1535
RefPtr<CacheFileIOManager> ioMan = gInstance;
1536
1537
if (!ioMan) {
1538
return NS_ERROR_NOT_INITIALIZED;
1539
}
1540
1541
bool priority = aFlags & CacheFileIOManager::PRIORITY;
1542
RefPtr<OpenFileEvent> ev = new OpenFileEvent(aKey, aFlags, aCallback);
1543
rv = ioMan->mIOThread->Dispatch(
1544
ev, priority ? CacheIOThread::OPEN_PRIORITY : CacheIOThread::OPEN);
1545
NS_ENSURE_SUCCESS(rv, rv);
1546
1547
return NS_OK;
1548
}
1549
1550
nsresult CacheFileIOManager::OpenFileInternal(const SHA1Sum::Hash* aHash,
1551
const nsACString& aKey,
1552
uint32_t aFlags,
1553
CacheFileHandle** _retval) {
1554
LOG(
1555
("CacheFileIOManager::OpenFileInternal() [hash=%08x%08x%08x%08x%08x, "
1556
"key=%s, flags=%d]",
1557
LOGSHA1(aHash), PromiseFlatCString(aKey).get(), aFlags));
1558
1559
MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
1560
1561
nsresult rv;
1562
1563
if (mShuttingDown) {
1564
return NS_ERROR_NOT_INITIALIZED;
1565
}
1566
1567
CacheIOThread::Cancelable cancelable(
1568
true /* never called for special handles */);
1569
1570
if (!mTreeCreated) {
1571
rv = CreateCacheTree();
1572
if (NS_FAILED(rv)) return rv;
1573
}
1574
1575
CacheFileHandle::PinningStatus pinning =
1576
aFlags & PINNED ? CacheFileHandle::PinningStatus::PINNED
1577
: CacheFileHandle::PinningStatus::NON_PINNED;
1578
1579
nsCOMPtr<nsIFile> file;
1580
rv = GetFile(aHash, getter_AddRefs(file));
1581
NS_ENSURE_SUCCESS(rv, rv);
1582
1583
RefPtr<CacheFileHandle> handle;
1584
mHandles.GetHandle(aHash, getter_AddRefs(handle));
1585
1586
if ((aFlags & (OPEN | CREATE | CREATE_NEW)) == CREATE_NEW) {
1587
if (handle) {
1588
rv = DoomFileInternal(handle);
1589
NS_ENSURE_SUCCESS(rv, rv);
1590
handle = nullptr;
1591
}
1592
1593
rv = mHandles.NewHandle(aHash, aFlags & PRIORITY, pinning,
1594
getter_AddRefs(handle));
1595
NS_ENSURE_SUCCESS(rv, rv);
1596
1597
bool exists;
1598
rv = file->Exists(&exists);
1599
NS_ENSURE_SUCCESS(rv, rv);
1600
1601
if (exists) {
1602
CacheIndex::RemoveEntry(aHash);
1603
1604
LOG(
1605
("CacheFileIOManager::OpenFileInternal() - Removing old file from "
1606
"disk"));
1607
rv = file->Remove(false);
1608
if (NS_FAILED(rv)) {
1609
NS_WARNING("Cannot remove old entry from the disk");
1610
LOG(
1611
("CacheFileIOManager::OpenFileInternal() - Removing old file failed"
1612
". [rv=0x%08" PRIx32 "]",
1613
static_cast<uint32_t>(rv)));
1614
}
1615
}
1616
1617
CacheIndex::AddEntry(aHash);
1618
handle->mFile.swap(file);
1619
handle->mFileSize = 0;
1620
}
1621
1622
if (handle) {
1623
handle.swap(*_retval);
1624
return NS_OK;
1625
}
1626
1627
bool exists, evictedAsPinned = false, evictedAsNonPinned = false;
1628
rv = file->Exists(&exists);
1629
NS_ENSURE_SUCCESS(rv, rv);
1630
1631
if (exists && mContextEvictor) {
1632
if (mContextEvictor->ContextsCount() == 0) {
1633
mContextEvictor = nullptr;
1634
} else {
1635
mContextEvictor->WasEvicted(aKey, file, &evictedAsPinned,
1636
&evictedAsNonPinned);
1637
}
1638
}
1639
1640
if (!exists && (aFlags & (OPEN | CREATE | CREATE_NEW)) == OPEN) {
1641
return NS_ERROR_NOT_AVAILABLE;
1642
}
1643
1644
if (exists) {
1645
// For existing files we determine the pinning status later, after the
1646
// metadata gets parsed.
1647
pinning = CacheFileHandle::PinningStatus::UNKNOWN;
1648
}
1649
1650
rv = mHandles.NewHandle(aHash, aFlags & PRIORITY, pinning,
1651
getter_AddRefs(handle));
1652
NS_ENSURE_SUCCESS(rv, rv);
1653
1654
if (exists) {
1655
// If this file has been found evicted through the context file evictor
1656
// above for any of pinned or non-pinned state, these calls ensure we doom
1657
// the handle ASAP we know the real pinning state after metadta has been
1658
// parsed. DoomFileInternal on the |handle| doesn't doom right now, since
1659
// the pinning state is unknown and we pass down a pinning restriction.
1660
if (evictedAsPinned) {
1661
rv = DoomFileInternal(handle, DOOM_WHEN_PINNED);
1662
MOZ_ASSERT(!handle->IsDoomed() && NS_SUCCEEDED(rv));
1663
}
1664
if (evictedAsNonPinned) {
1665
rv = DoomFileInternal(handle, DOOM_WHEN_NON_PINNED);
1666
MOZ_ASSERT(!handle->IsDoomed() && NS_SUCCEEDED(rv));
1667
}
1668
1669
rv = file->GetFileSize(&handle->mFileSize);
1670
NS_ENSURE_SUCCESS(rv, rv);
1671
1672
handle->mFileExists = true;
1673
1674
CacheIndex::EnsureEntryExists(aHash);
1675
} else {
1676
handle->mFileSize = 0;
1677
1678
CacheIndex::AddEntry(aHash);
1679
}
1680
1681
handle->mFile.swap(file);
1682
handle.swap(*_retval);
1683
return NS_OK;
1684
}
1685
1686
nsresult CacheFileIOManager::OpenSpecialFileInternal(
1687
const nsACString& aKey, uint32_t aFlags, CacheFileHandle** _retval) {
1688
LOG(("CacheFileIOManager::OpenSpecialFileInternal() [key=%s, flags=%d]",
1689
PromiseFlatCString(aKey).get(), aFlags));
1690
1691
MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
1692
1693
nsresult rv;
1694
1695
if (mShuttingDown) {
1696
return NS_ERROR_NOT_INITIALIZED;
1697
}
1698
1699
if (!mTreeCreated) {
1700
rv = CreateCacheTree();
1701
if (NS_FAILED(rv)) return rv;
1702
}
1703
1704
nsCOMPtr<nsIFile> file;
1705
rv = GetSpecialFile(aKey, getter_AddRefs(file));
1706
NS_ENSURE_SUCCESS(rv, rv);
1707
1708
RefPtr<CacheFileHandle> handle;
1709
for (uint32_t i = 0; i < mSpecialHandles.Length(); i++) {
1710
if (!mSpecialHandles[i]->IsDoomed() && mSpecialHandles[i]->Key() == aKey) {
1711
handle = mSpecialHandles[i];
1712
break;
1713
}
1714
}
1715
1716
if ((aFlags & (OPEN | CREATE | CREATE_NEW)) == CREATE_NEW) {
1717
if (handle) {
1718
rv = DoomFileInternal(handle);
1719
NS_ENSURE_SUCCESS(rv, rv);
1720
handle = nullptr;
1721
}
1722
1723
handle = new CacheFileHandle(aKey, aFlags & PRIORITY,
1724
CacheFileHandle::PinningStatus::NON_PINNED);
1725
mSpecialHandles.AppendElement(handle);
1726
1727
bool exists;
1728
rv = file->Exists(&exists);
1729
NS_ENSURE_SUCCESS(rv, rv);
1730
1731
if (exists) {
1732
LOG(
1733
("CacheFileIOManager::OpenSpecialFileInternal() - Removing file from "
1734
"disk"));
1735
rv = file->Remove(false);
1736
if (NS_FAILED(rv)) {
1737
NS_WARNING("Cannot remove old entry from the disk");
1738
LOG(
1739
("CacheFileIOManager::OpenSpecialFileInternal() - Removing file "
1740
"failed. [rv=0x%08" PRIx32 "]",
1741
static_cast<uint32_t>(rv)));
1742
}
1743
}
1744
1745
handle->mFile.swap(file);
1746
handle->mFileSize = 0;
1747
}
1748
1749
if (handle) {
1750
handle.swap(*_retval);
1751
return NS_OK;
1752
}
1753
1754
bool exists;
1755
rv = file->Exists(&exists);
1756
NS_ENSURE_SUCCESS(rv, rv);
1757
1758
if (!exists && (aFlags & (OPEN | CREATE | CREATE_NEW)) == OPEN) {
1759
return NS_ERROR_NOT_AVAILABLE;
1760
}
1761
1762
handle = new CacheFileHandle(aKey, aFlags & PRIORITY,
1763
CacheFileHandle::PinningStatus::NON_PINNED);
1764
mSpecialHandles.AppendElement(handle);
1765
1766
if (exists) {
1767
rv = file->GetFileSize(&handle->mFileSize);
1768
NS_ENSURE_SUCCESS(rv, rv);
1769
1770
handle->mFileExists = true;
1771
} else {
1772
handle->mFileSize = 0;
1773
}
1774
1775
handle->mFile.swap(file);
1776
handle.swap(*_retval);
1777
return NS_OK;
1778
}
1779
1780
nsresult CacheFileIOManager::CloseHandleInternal(CacheFileHandle* aHandle) {
1781
nsresult rv;
1782
1783
LOG(("CacheFileIOManager::CloseHandleInternal() [handle=%p]", aHandle));
1784
1785
MOZ_ASSERT(!aHandle->IsClosed());
1786
1787
aHandle->Log();
1788
1789
MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
1790
1791
CacheIOThread::Cancelable cancelable(!aHandle->IsSpecialFile());
1792
1793
// Maybe close file handle (can be legally bypassed after shutdown)
1794
rv = MaybeReleaseNSPRHandleInternal(aHandle);
1795
1796
// Delete the file if the entry was doomed or invalid and
1797
// filedesc properly closed
1798
if ((aHandle->mIsDoomed || aHandle->mInvalid) && aHandle->mFileExists &&
1799
NS_SUCCEEDED(rv)) {
1800
LOG(
1801
("CacheFileIOManager::CloseHandleInternal() - Removing file from "
1802
"disk"));
1803
1804
rv = aHandle->mFile->Remove(false);
1805
if (NS_SUCCEEDED(rv)) {
1806
aHandle->mFileExists = false;
1807
} else {
1808
LOG((" failed to remove the file [rv=0x%08" PRIx32 "]",
1809
static_cast<uint32_t>(rv)));
1810
}
1811
}
1812
1813
if (!aHandle->IsSpecialFile() && !aHandle->mIsDoomed &&
1814
(aHandle->mInvalid || !aHandle->mFileExists)) {
1815
CacheIndex::RemoveEntry(aHandle->Hash());
1816
}
1817
1818
// Don't remove handles after shutdown
1819
if (!mShuttingDown) {
1820
if (aHandle->IsSpecialFile()) {
1821
mSpecialHandles.RemoveElement(aHandle);
1822
} else {
1823
mHandles.RemoveHandle(aHandle);
1824
}
1825
}
1826
1827
return NS_OK;
1828
}
1829
1830
// static
1831
nsresult CacheFileIOManager::Read(CacheFileHandle* aHandle, int64_t aOffset,
1832
char* aBuf, int32_t aCount,
1833
CacheFileIOListener* aCallback) {
1834
LOG(("CacheFileIOManager::Read() [handle=%p, offset=%" PRId64 ", count=%d, "
1835
"listener=%p]",
1836
aHandle, aOffset, aCount, aCallback));
1837
1838
if (CacheObserver::ShuttingDown()) {
1839
LOG((" no reads after shutdown"));
1840
return NS_ERROR_NOT_INITIALIZED;
1841
}
1842
1843
nsresult rv;
1844
RefPtr<CacheFileIOManager> ioMan = gInstance;
1845
1846
if (aHandle->IsClosed() || !ioMan) {
1847
return NS_ERROR_NOT_INITIALIZED;
1848
}
1849
1850
RefPtr<ReadEvent> ev =
1851
new ReadEvent(aHandle, aOffset, aBuf, aCount, aCallback);
1852
rv = ioMan->mIOThread->Dispatch(ev, aHandle->IsPriority()
1853
? CacheIOThread::READ_PRIORITY
1854
: CacheIOThread::READ);
1855
NS_ENSURE_SUCCESS(rv, rv);
1856
1857
return NS_OK;
1858
}
1859
1860
nsresult CacheFileIOManager::ReadInternal(CacheFileHandle* aHandle,
1861
int64_t aOffset, char* aBuf,
1862
int32_t aCount) {
1863
LOG(("CacheFileIOManager::ReadInternal() [handle=%p, offset=%" PRId64
1864
", count=%d]",
1865
aHandle, aOffset, aCount));
1866
1867
nsresult rv;
1868
1869
if (CacheObserver::ShuttingDown()) {
1870
LOG((" no reads after shutdown"));
1871
return NS_ERROR_NOT_INITIALIZED;
1872