Skip to content

Commit 8f88fd6

Browse files
committed
Bug 1032254 - Generic way to pin reasource in the HTTP cache, r=michal
* * * Bug NNNNNNN - message, r=reviewer --HG-- rename : netwerk/test/unit/test_cache2-28-concurrent_read_resumable_entry_size_zero.js => netwerk/test/unit/test_cache2-29a-concurrent_read_resumable_entry_size_zero.js rename : netwerk/test/unit/test_cache2-29-concurrent_read_non-resumable_entry_size_zero.js => netwerk/test/unit/test_cache2-29b-concurrent_read_non-resumable_entry_size_zero.js
1 parent e043621 commit 8f88fd6

38 files changed

+1216
-264
lines changed

netwerk/base/nsICachingChannel.idl

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ interface nsIFile;
1717
* 3) Support for uniquely identifying cached data in cases when the URL
1818
* is insufficient (e.g., HTTP form submission).
1919
*/
20-
[scriptable, uuid(436b939d-e391-48e5-ba64-ab0e496e3400)]
20+
[scriptable, uuid(dd1d6122-5ecf-4fe4-8f0f-995e7ab3121a)]
2121
interface nsICachingChannel : nsICacheInfoChannel
2222
{
2323
/**
@@ -53,6 +53,11 @@ interface nsICachingChannel : nsICacheInfoChannel
5353
*/
5454
attribute boolean cacheOnlyMetadata;
5555

56+
/**
57+
* Tells the channel to use the pinning storage.
58+
*/
59+
attribute boolean pin;
60+
5661
/**************************************************************************
5762
* Caching channel specific load flags:
5863
*/

netwerk/cache2/AppCacheStorage.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ NS_IMPL_ISUPPORTS_INHERITED0(AppCacheStorage, CacheStorage)
2525

2626
AppCacheStorage::AppCacheStorage(nsILoadContextInfo* aInfo,
2727
nsIApplicationCache* aAppCache)
28-
: CacheStorage(aInfo, true /* disk */, false /* lookup app cache */, false /* skip size check */)
28+
: CacheStorage(aInfo, true /* disk */, false /* lookup app cache */, false /* skip size check */, false /* pin */)
2929
, mAppCache(aAppCache)
3030
{
3131
MOZ_COUNT_CTOR(AppCacheStorage);

netwerk/cache2/CacheEntry.cpp

Lines changed: 116 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@ CacheEntry::Callback::Callback(CacheEntry* aEntry,
8787
, mRecheckAfterWrite(false)
8888
, mNotWanted(false)
8989
, mSecret(aSecret)
90+
, mDoomWhenFoundPinned(false)
91+
, mDoomWhenFoundNonPinned(false)
9092
{
9193
MOZ_COUNT_CTOR(CacheEntry::Callback);
9294

@@ -96,6 +98,22 @@ CacheEntry::Callback::Callback(CacheEntry* aEntry,
9698
mEntry->AddHandleRef();
9799
}
98100

101+
CacheEntry::Callback::Callback(CacheEntry* aEntry, bool aDoomWhenFoundInPinStatus)
102+
: mEntry(aEntry)
103+
, mReadOnly(false)
104+
, mRevalidating(false)
105+
, mCheckOnAnyThread(true)
106+
, mRecheckAfterWrite(false)
107+
, mNotWanted(false)
108+
, mSecret(false)
109+
, mDoomWhenFoundPinned(aDoomWhenFoundInPinStatus == true)
110+
, mDoomWhenFoundNonPinned(aDoomWhenFoundInPinStatus == false)
111+
{
112+
MOZ_COUNT_CTOR(CacheEntry::Callback);
113+
MOZ_ASSERT(mEntry->HandlesCount());
114+
mEntry->AddHandleRef();
115+
}
116+
99117
CacheEntry::Callback::Callback(CacheEntry::Callback const &aThat)
100118
: mEntry(aThat.mEntry)
101119
, mCallback(aThat.mCallback)
@@ -106,6 +124,8 @@ CacheEntry::Callback::Callback(CacheEntry::Callback const &aThat)
106124
, mRecheckAfterWrite(aThat.mRecheckAfterWrite)
107125
, mNotWanted(aThat.mNotWanted)
108126
, mSecret(aThat.mSecret)
127+
, mDoomWhenFoundPinned(aThat.mDoomWhenFoundPinned)
128+
, mDoomWhenFoundNonPinned(aThat.mDoomWhenFoundNonPinned)
109129
{
110130
MOZ_COUNT_CTOR(CacheEntry::Callback);
111131

@@ -136,6 +156,20 @@ void CacheEntry::Callback::ExchangeEntry(CacheEntry* aEntry)
136156
mEntry = aEntry;
137157
}
138158

159+
bool CacheEntry::Callback::DeferDoom(bool *aDoom) const
160+
{
161+
MOZ_ASSERT(mEntry->mPinningKnown);
162+
163+
if (MOZ_UNLIKELY(mDoomWhenFoundNonPinned) || MOZ_UNLIKELY(mDoomWhenFoundPinned)) {
164+
*aDoom = (MOZ_UNLIKELY(mDoomWhenFoundNonPinned) && MOZ_LIKELY(!mEntry->mPinned)) ||
165+
(MOZ_UNLIKELY(mDoomWhenFoundPinned) && MOZ_UNLIKELY(mEntry->mPinned));
166+
167+
return true;
168+
}
169+
170+
return false;
171+
}
172+
139173
nsresult CacheEntry::Callback::OnCheckThread(bool *aOnCheckThread) const
140174
{
141175
if (!mCheckOnAnyThread) {
@@ -164,7 +198,8 @@ CacheEntry::CacheEntry(const nsACString& aStorageID,
164198
nsIURI* aURI,
165199
const nsACString& aEnhanceID,
166200
bool aUseDisk,
167-
bool aSkipSizeCheck)
201+
bool aSkipSizeCheck,
202+
bool aPin)
168203
: mFrecency(0)
169204
, mSortingExpirationTime(uint32_t(-1))
170205
, mLock("CacheEntry")
@@ -174,10 +209,12 @@ CacheEntry::CacheEntry(const nsACString& aStorageID,
174209
, mStorageID(aStorageID)
175210
, mUseDisk(aUseDisk)
176211
, mSkipSizeCheck(aSkipSizeCheck)
177-
, mIsDoomed(false)
178212
, mSecurityInfoLoaded(false)
179213
, mPreventCallbacks(false)
180214
, mHasData(false)
215+
, mPinned(aPin)
216+
, mPinningKnown(false)
217+
, mIsDoomed(false)
181218
, mState(NOTLOADED)
182219
, mRegistration(NEVERREGISTERED)
183220
, mWriter(nullptr)
@@ -350,16 +387,18 @@ bool CacheEntry::Load(bool aTruncate, bool aPriority)
350387
if (NS_SUCCEEDED(CacheIndex::HasEntry(fileKey, &status))) {
351388
switch (status) {
352389
case CacheIndex::DOES_NOT_EXIST:
353-
LOG((" entry doesn't exist according information from the index, truncating"));
390+
// Doesn't apply to memory-only entries, Load() is called only once for them
391+
// and never again for their session lifetime.
354392
if (!aTruncate && mUseDisk) {
393+
LOG((" entry doesn't exist according information from the index, truncating"));
355394
reportMiss = true;
395+
aTruncate = true;
356396
}
357-
aTruncate = true;
358397
break;
359398
case CacheIndex::EXISTS:
360399
case CacheIndex::DO_NOT_KNOW:
361400
if (!mUseDisk) {
362-
LOG((" entry open as memory-only, but there is (status=%d) a file, dooming it", status));
401+
LOG((" entry open as memory-only, but there is a file, status=%d, dooming it", status));
363402
CacheFileIOManager::DoomFileByKey(fileKey, nullptr);
364403
}
365404
break;
@@ -376,6 +415,7 @@ bool CacheEntry::Load(bool aTruncate, bool aPriority)
376415
// mLoadStart will be used to calculate telemetry of life-time of this entry.
377416
// Low resulution is then enough.
378417
mLoadStart = TimeStamp::NowLoRes();
418+
mPinningKnown = true;
379419
} else {
380420
mLoadStart = TimeStamp::Now();
381421
}
@@ -395,6 +435,7 @@ bool CacheEntry::Load(bool aTruncate, bool aPriority)
395435
!mUseDisk,
396436
mSkipSizeCheck,
397437
aPriority,
438+
mPinned,
398439
directLoad ? nullptr : this);
399440
}
400441

@@ -432,9 +473,10 @@ NS_IMETHODIMP CacheEntry::OnFileReady(nsresult aResult, bool aIsNew)
432473
}
433474

434475
// OnFileReady, that is the only code that can transit from LOADING
435-
// to any follow-on state, can only be invoked ones on an entry,
436-
// thus no need to lock. Until this moment there is no consumer that
437-
// could manipulate the entry state.
476+
// to any follow-on state and can only be invoked ones on an entry.
477+
// Until this moment there is no consumer that could manipulate
478+
// the entry state.
479+
438480
mozilla::MutexAutoLock lock(mLock);
439481

440482
MOZ_ASSERT(mState == LOADING);
@@ -445,6 +487,10 @@ NS_IMETHODIMP CacheEntry::OnFileReady(nsresult aResult, bool aIsNew)
445487

446488
mFileStatus = aResult;
447489

490+
mPinned = mFile->IsPinned();;
491+
mPinningKnown = true;
492+
LOG((" pinning=%d", mPinned));
493+
448494
if (mState == READY) {
449495
mHasData = true;
450496

@@ -456,6 +502,7 @@ NS_IMETHODIMP CacheEntry::OnFileReady(nsresult aResult, bool aIsNew)
456502
}
457503

458504
InvokeCallbacks();
505+
459506
return NS_OK;
460507
}
461508

@@ -483,13 +530,21 @@ already_AddRefed<CacheEntryHandle> CacheEntry::ReopenTruncated(bool aMemoryOnly,
483530
RefPtr<CacheEntryHandle> handle;
484531
RefPtr<CacheEntry> newEntry;
485532
{
533+
if (mPinned) {
534+
MOZ_ASSERT(mUseDisk);
535+
// We want to pin even no-store entries (the case we recreate a disk entry as
536+
// a memory-only entry.)
537+
aMemoryOnly = false;
538+
}
539+
486540
mozilla::MutexAutoUnlock unlock(mLock);
487541

488542
// The following call dooms this entry (calls DoomAlreadyRemoved on us)
489543
nsresult rv = CacheStorageService::Self()->AddStorageEntry(
490544
GetStorageID(), GetURI(), GetEnhanceID(),
491545
mUseDisk && !aMemoryOnly,
492546
mSkipSizeCheck,
547+
mPinned,
493548
true, // always create
494549
true, // truncate existing (this one)
495550
getter_AddRefs(handle));
@@ -577,6 +632,8 @@ bool CacheEntry::InvokeCallbacks(bool aReadOnly)
577632
{
578633
mLock.AssertCurrentThreadOwns();
579634

635+
RefPtr<CacheEntryHandle> recreatedHandle;
636+
580637
uint32_t i = 0;
581638
while (i < mCallbacks.Length()) {
582639
if (mPreventCallbacks) {
@@ -589,6 +646,18 @@ bool CacheEntry::InvokeCallbacks(bool aReadOnly)
589646
return false;
590647
}
591648

649+
bool recreate;
650+
if (mCallbacks[i].DeferDoom(&recreate)) {
651+
mCallbacks.RemoveElementAt(i);
652+
if (!recreate) {
653+
continue;
654+
}
655+
656+
LOG((" defer doom marker callback hit positive, recreating"));
657+
recreatedHandle = ReopenTruncated(!mUseDisk, nullptr);
658+
break;
659+
}
660+
592661
if (mCallbacks[i].mReadOnly != aReadOnly) {
593662
// Callback is not r/w or r/o, go to another one in line
594663
++i;
@@ -625,6 +694,12 @@ bool CacheEntry::InvokeCallbacks(bool aReadOnly)
625694
}
626695
}
627696

697+
if (recreatedHandle) {
698+
// Must be released outside of the lock, enters InvokeCallback on the new entry
699+
mozilla::MutexAutoUnlock unlock(mLock);
700+
recreatedHandle = nullptr;
701+
}
702+
628703
return true;
629704
}
630705

@@ -991,6 +1066,13 @@ NS_IMETHODIMP CacheEntry::GetIsForcedValid(bool *aIsForcedValid)
9911066
{
9921067
NS_ENSURE_ARG(aIsForcedValid);
9931068

1069+
MOZ_ASSERT(mState > LOADING);
1070+
1071+
if (mPinned) {
1072+
*aIsForcedValid = true;
1073+
return NS_OK;
1074+
}
1075+
9941076
nsAutoCString key;
9951077

9961078
nsresult rv = HashingKeyWithStorage(key);
@@ -1442,6 +1524,28 @@ void CacheEntry::SetRegistered(bool aRegistered)
14421524
}
14431525
}
14441526

1527+
bool CacheEntry::DeferOrBypassRemovalOnPinStatus(bool aPinned)
1528+
{
1529+
LOG(("CacheEntry::DeferOrBypassRemovalOnPinStatus [this=%p]", this));
1530+
1531+
mozilla::MutexAutoLock lock(mLock);
1532+
1533+
if (mPinningKnown) {
1534+
LOG((" pinned=%d, caller=%d", mPinned, aPinned));
1535+
// Bypass when the pin status of this entry doesn't match the pin status
1536+
// caller wants to remove
1537+
return mPinned != aPinned;
1538+
}
1539+
1540+
LOG((" pinning unknown, caller=%d", aPinned));
1541+
// Oterwise, remember to doom after the status is determined for any
1542+
// callback opening the entry after this point...
1543+
Callback c(this, aPinned);
1544+
RememberCallback(c);
1545+
// ...and always bypass
1546+
return true;
1547+
}
1548+
14451549
bool CacheEntry::Purge(uint32_t aWhat)
14461550
{
14471551
LOG(("CacheEntry::Purge [this=%p, what=%d]", this, aWhat));
@@ -1523,6 +1627,10 @@ void CacheEntry::DoomAlreadyRemoved()
15231627

15241628
mIsDoomed = true;
15251629

1630+
// Pretend pinning is know. This entry is now doomed for good, so don't
1631+
// bother with defering doom because of unknown pinning state any more.
1632+
mPinningKnown = true;
1633+
15261634
// This schedules dooming of the file, dooming is ensured to happen
15271635
// sooner than demand to open the same file made after this point
15281636
// so that we don't get this file for any newer opened entry(s).

netwerk/cache2/CacheEntry.h

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ class CacheEntry final : public nsICacheEntry
5555
NS_DECL_NSIRUNNABLE
5656

5757
CacheEntry(const nsACString& aStorageID, nsIURI* aURI, const nsACString& aEnhanceID,
58-
bool aUseDisk, bool aSkipSizeCheck);
58+
bool aUseDisk, bool aSkipSizeCheck, bool aPin);
5959

6060
void AsyncOpen(nsICacheEntryOpenCallback* aCallback, uint32_t aFlags);
6161

@@ -92,6 +92,7 @@ class CacheEntry final : public nsICacheEntry
9292
PURGE_WHOLE,
9393
};
9494

95+
bool DeferOrBypassRemovalOnPinStatus(bool aPinned);
9596
bool Purge(uint32_t aWhat);
9697
void PurgeAndDoom();
9798
void DoomAlreadyRemoved();
@@ -136,13 +137,20 @@ class CacheEntry final : public nsICacheEntry
136137
Callback(CacheEntry* aEntry,
137138
nsICacheEntryOpenCallback *aCallback,
138139
bool aReadOnly, bool aCheckOnAnyThread, bool aSecret);
140+
// Special constructor for Callback objects added to the chain
141+
// just to ensure proper defer dooming (recreation) of this entry.
142+
Callback(CacheEntry* aEntry, bool aDoomWhenFoundInPinStatus);
139143
Callback(Callback const &aThat);
140144
~Callback();
141145

142146
// Called when this callback record changes it's owning entry,
143147
// mainly during recreation.
144148
void ExchangeEntry(CacheEntry* aEntry);
145149

150+
// Returns true when an entry is about to be "defer" doomed and this is
151+
// a "defer" callback.
152+
bool DeferDoom(bool *aDoom) const;
153+
146154
// We are raising reference count here to take into account the pending
147155
// callback (that virtually holds a ref to this entry before it gets
148156
// it's pointer).
@@ -156,6 +164,13 @@ class CacheEntry final : public nsICacheEntry
156164
bool mNotWanted : 1;
157165
bool mSecret : 1;
158166

167+
// These are set only for the defer-doomer Callback instance inserted
168+
// to the callback chain. When any of these is set and also any of
169+
// the corressponding flags on the entry is set, this callback will
170+
// indicate (via DeferDoom()) the entry have to be recreated/doomed.
171+
bool mDoomWhenFoundPinned : 1;
172+
bool mDoomWhenFoundNonPinned : 1;
173+
159174
nsresult OnCheckThread(bool *aOnCheckThread) const;
160175
nsresult OnAvailThread(bool *aOnAvailThread) const;
161176
};
@@ -274,14 +289,9 @@ class CacheEntry final : public nsICacheEntry
274289
nsCString mStorageID;
275290

276291
// Whether it's allowed to persist the data to disk
277-
bool const mUseDisk;
278-
292+
bool const mUseDisk : 1;
279293
// Whether it should skip max size check.
280-
bool const mSkipSizeCheck;
281-
282-
// Set when entry is doomed with AsyncDoom() or DoomAlreadyRemoved().
283-
// Left as a standalone flag to not bother with locking (there is no need).
284-
bool mIsDoomed;
294+
bool const mSkipSizeCheck : 1;
285295

286296
// Following flags are all synchronized with the cache entry lock.
287297

@@ -296,6 +306,15 @@ class CacheEntry final : public nsICacheEntry
296306
// false: after load and a new file, or dropped to back to false when a writer
297307
// fails to open an output stream.
298308
bool mHasData : 1;
309+
// The indication of pinning this entry was open with
310+
bool mPinned : 1;
311+
// Whether the pinning state of the entry is known (equals to the actual state
312+
// of the cache file)
313+
bool mPinningKnown : 1;
314+
315+
// Set when entry is doomed with AsyncDoom() or DoomAlreadyRemoved().
316+
// Left as a standalone flag to not bother with locking (there is no need).
317+
bool mIsDoomed;
299318

300319
static char const * StateString(uint32_t aState);
301320

0 commit comments

Comments
 (0)