зеркало из https://github.com/mozilla/gecko-dev.git
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
This commit is contained in:
Родитель
e0436217a8
Коммит
8f88fd6cdc
|
@ -17,7 +17,7 @@ interface nsIFile;
|
|||
* 3) Support for uniquely identifying cached data in cases when the URL
|
||||
* is insufficient (e.g., HTTP form submission).
|
||||
*/
|
||||
[scriptable, uuid(436b939d-e391-48e5-ba64-ab0e496e3400)]
|
||||
[scriptable, uuid(dd1d6122-5ecf-4fe4-8f0f-995e7ab3121a)]
|
||||
interface nsICachingChannel : nsICacheInfoChannel
|
||||
{
|
||||
/**
|
||||
|
@ -53,6 +53,11 @@ interface nsICachingChannel : nsICacheInfoChannel
|
|||
*/
|
||||
attribute boolean cacheOnlyMetadata;
|
||||
|
||||
/**
|
||||
* Tells the channel to use the pinning storage.
|
||||
*/
|
||||
attribute boolean pin;
|
||||
|
||||
/**************************************************************************
|
||||
* Caching channel specific load flags:
|
||||
*/
|
||||
|
|
|
@ -25,7 +25,7 @@ NS_IMPL_ISUPPORTS_INHERITED0(AppCacheStorage, CacheStorage)
|
|||
|
||||
AppCacheStorage::AppCacheStorage(nsILoadContextInfo* aInfo,
|
||||
nsIApplicationCache* aAppCache)
|
||||
: CacheStorage(aInfo, true /* disk */, false /* lookup app cache */, false /* skip size check */)
|
||||
: CacheStorage(aInfo, true /* disk */, false /* lookup app cache */, false /* skip size check */, false /* pin */)
|
||||
, mAppCache(aAppCache)
|
||||
{
|
||||
MOZ_COUNT_CTOR(AppCacheStorage);
|
||||
|
|
|
@ -87,6 +87,8 @@ CacheEntry::Callback::Callback(CacheEntry* aEntry,
|
|||
, mRecheckAfterWrite(false)
|
||||
, mNotWanted(false)
|
||||
, mSecret(aSecret)
|
||||
, mDoomWhenFoundPinned(false)
|
||||
, mDoomWhenFoundNonPinned(false)
|
||||
{
|
||||
MOZ_COUNT_CTOR(CacheEntry::Callback);
|
||||
|
||||
|
@ -96,6 +98,22 @@ CacheEntry::Callback::Callback(CacheEntry* aEntry,
|
|||
mEntry->AddHandleRef();
|
||||
}
|
||||
|
||||
CacheEntry::Callback::Callback(CacheEntry* aEntry, bool aDoomWhenFoundInPinStatus)
|
||||
: mEntry(aEntry)
|
||||
, mReadOnly(false)
|
||||
, mRevalidating(false)
|
||||
, mCheckOnAnyThread(true)
|
||||
, mRecheckAfterWrite(false)
|
||||
, mNotWanted(false)
|
||||
, mSecret(false)
|
||||
, mDoomWhenFoundPinned(aDoomWhenFoundInPinStatus == true)
|
||||
, mDoomWhenFoundNonPinned(aDoomWhenFoundInPinStatus == false)
|
||||
{
|
||||
MOZ_COUNT_CTOR(CacheEntry::Callback);
|
||||
MOZ_ASSERT(mEntry->HandlesCount());
|
||||
mEntry->AddHandleRef();
|
||||
}
|
||||
|
||||
CacheEntry::Callback::Callback(CacheEntry::Callback const &aThat)
|
||||
: mEntry(aThat.mEntry)
|
||||
, mCallback(aThat.mCallback)
|
||||
|
@ -106,6 +124,8 @@ CacheEntry::Callback::Callback(CacheEntry::Callback const &aThat)
|
|||
, mRecheckAfterWrite(aThat.mRecheckAfterWrite)
|
||||
, mNotWanted(aThat.mNotWanted)
|
||||
, mSecret(aThat.mSecret)
|
||||
, mDoomWhenFoundPinned(aThat.mDoomWhenFoundPinned)
|
||||
, mDoomWhenFoundNonPinned(aThat.mDoomWhenFoundNonPinned)
|
||||
{
|
||||
MOZ_COUNT_CTOR(CacheEntry::Callback);
|
||||
|
||||
|
@ -136,6 +156,20 @@ void CacheEntry::Callback::ExchangeEntry(CacheEntry* aEntry)
|
|||
mEntry = aEntry;
|
||||
}
|
||||
|
||||
bool CacheEntry::Callback::DeferDoom(bool *aDoom) const
|
||||
{
|
||||
MOZ_ASSERT(mEntry->mPinningKnown);
|
||||
|
||||
if (MOZ_UNLIKELY(mDoomWhenFoundNonPinned) || MOZ_UNLIKELY(mDoomWhenFoundPinned)) {
|
||||
*aDoom = (MOZ_UNLIKELY(mDoomWhenFoundNonPinned) && MOZ_LIKELY(!mEntry->mPinned)) ||
|
||||
(MOZ_UNLIKELY(mDoomWhenFoundPinned) && MOZ_UNLIKELY(mEntry->mPinned));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
nsresult CacheEntry::Callback::OnCheckThread(bool *aOnCheckThread) const
|
||||
{
|
||||
if (!mCheckOnAnyThread) {
|
||||
|
@ -164,7 +198,8 @@ CacheEntry::CacheEntry(const nsACString& aStorageID,
|
|||
nsIURI* aURI,
|
||||
const nsACString& aEnhanceID,
|
||||
bool aUseDisk,
|
||||
bool aSkipSizeCheck)
|
||||
bool aSkipSizeCheck,
|
||||
bool aPin)
|
||||
: mFrecency(0)
|
||||
, mSortingExpirationTime(uint32_t(-1))
|
||||
, mLock("CacheEntry")
|
||||
|
@ -174,10 +209,12 @@ CacheEntry::CacheEntry(const nsACString& aStorageID,
|
|||
, mStorageID(aStorageID)
|
||||
, mUseDisk(aUseDisk)
|
||||
, mSkipSizeCheck(aSkipSizeCheck)
|
||||
, mIsDoomed(false)
|
||||
, mSecurityInfoLoaded(false)
|
||||
, mPreventCallbacks(false)
|
||||
, mHasData(false)
|
||||
, mPinned(aPin)
|
||||
, mPinningKnown(false)
|
||||
, mIsDoomed(false)
|
||||
, mState(NOTLOADED)
|
||||
, mRegistration(NEVERREGISTERED)
|
||||
, mWriter(nullptr)
|
||||
|
@ -350,16 +387,18 @@ bool CacheEntry::Load(bool aTruncate, bool aPriority)
|
|||
if (NS_SUCCEEDED(CacheIndex::HasEntry(fileKey, &status))) {
|
||||
switch (status) {
|
||||
case CacheIndex::DOES_NOT_EXIST:
|
||||
LOG((" entry doesn't exist according information from the index, truncating"));
|
||||
// Doesn't apply to memory-only entries, Load() is called only once for them
|
||||
// and never again for their session lifetime.
|
||||
if (!aTruncate && mUseDisk) {
|
||||
LOG((" entry doesn't exist according information from the index, truncating"));
|
||||
reportMiss = true;
|
||||
aTruncate = true;
|
||||
}
|
||||
aTruncate = true;
|
||||
break;
|
||||
case CacheIndex::EXISTS:
|
||||
case CacheIndex::DO_NOT_KNOW:
|
||||
if (!mUseDisk) {
|
||||
LOG((" entry open as memory-only, but there is (status=%d) a file, dooming it", status));
|
||||
LOG((" entry open as memory-only, but there is a file, status=%d, dooming it", status));
|
||||
CacheFileIOManager::DoomFileByKey(fileKey, nullptr);
|
||||
}
|
||||
break;
|
||||
|
@ -376,6 +415,7 @@ bool CacheEntry::Load(bool aTruncate, bool aPriority)
|
|||
// mLoadStart will be used to calculate telemetry of life-time of this entry.
|
||||
// Low resulution is then enough.
|
||||
mLoadStart = TimeStamp::NowLoRes();
|
||||
mPinningKnown = true;
|
||||
} else {
|
||||
mLoadStart = TimeStamp::Now();
|
||||
}
|
||||
|
@ -395,6 +435,7 @@ bool CacheEntry::Load(bool aTruncate, bool aPriority)
|
|||
!mUseDisk,
|
||||
mSkipSizeCheck,
|
||||
aPriority,
|
||||
mPinned,
|
||||
directLoad ? nullptr : this);
|
||||
}
|
||||
|
||||
|
@ -432,9 +473,10 @@ NS_IMETHODIMP CacheEntry::OnFileReady(nsresult aResult, bool aIsNew)
|
|||
}
|
||||
|
||||
// OnFileReady, that is the only code that can transit from LOADING
|
||||
// to any follow-on state, can only be invoked ones on an entry,
|
||||
// thus no need to lock. Until this moment there is no consumer that
|
||||
// could manipulate the entry state.
|
||||
// to any follow-on state and can only be invoked ones on an entry.
|
||||
// Until this moment there is no consumer that could manipulate
|
||||
// the entry state.
|
||||
|
||||
mozilla::MutexAutoLock lock(mLock);
|
||||
|
||||
MOZ_ASSERT(mState == LOADING);
|
||||
|
@ -445,6 +487,10 @@ NS_IMETHODIMP CacheEntry::OnFileReady(nsresult aResult, bool aIsNew)
|
|||
|
||||
mFileStatus = aResult;
|
||||
|
||||
mPinned = mFile->IsPinned();;
|
||||
mPinningKnown = true;
|
||||
LOG((" pinning=%d", mPinned));
|
||||
|
||||
if (mState == READY) {
|
||||
mHasData = true;
|
||||
|
||||
|
@ -456,6 +502,7 @@ NS_IMETHODIMP CacheEntry::OnFileReady(nsresult aResult, bool aIsNew)
|
|||
}
|
||||
|
||||
InvokeCallbacks();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -483,6 +530,13 @@ already_AddRefed<CacheEntryHandle> CacheEntry::ReopenTruncated(bool aMemoryOnly,
|
|||
RefPtr<CacheEntryHandle> handle;
|
||||
RefPtr<CacheEntry> newEntry;
|
||||
{
|
||||
if (mPinned) {
|
||||
MOZ_ASSERT(mUseDisk);
|
||||
// We want to pin even no-store entries (the case we recreate a disk entry as
|
||||
// a memory-only entry.)
|
||||
aMemoryOnly = false;
|
||||
}
|
||||
|
||||
mozilla::MutexAutoUnlock unlock(mLock);
|
||||
|
||||
// The following call dooms this entry (calls DoomAlreadyRemoved on us)
|
||||
|
@ -490,6 +544,7 @@ already_AddRefed<CacheEntryHandle> CacheEntry::ReopenTruncated(bool aMemoryOnly,
|
|||
GetStorageID(), GetURI(), GetEnhanceID(),
|
||||
mUseDisk && !aMemoryOnly,
|
||||
mSkipSizeCheck,
|
||||
mPinned,
|
||||
true, // always create
|
||||
true, // truncate existing (this one)
|
||||
getter_AddRefs(handle));
|
||||
|
@ -577,6 +632,8 @@ bool CacheEntry::InvokeCallbacks(bool aReadOnly)
|
|||
{
|
||||
mLock.AssertCurrentThreadOwns();
|
||||
|
||||
RefPtr<CacheEntryHandle> recreatedHandle;
|
||||
|
||||
uint32_t i = 0;
|
||||
while (i < mCallbacks.Length()) {
|
||||
if (mPreventCallbacks) {
|
||||
|
@ -589,6 +646,18 @@ bool CacheEntry::InvokeCallbacks(bool aReadOnly)
|
|||
return false;
|
||||
}
|
||||
|
||||
bool recreate;
|
||||
if (mCallbacks[i].DeferDoom(&recreate)) {
|
||||
mCallbacks.RemoveElementAt(i);
|
||||
if (!recreate) {
|
||||
continue;
|
||||
}
|
||||
|
||||
LOG((" defer doom marker callback hit positive, recreating"));
|
||||
recreatedHandle = ReopenTruncated(!mUseDisk, nullptr);
|
||||
break;
|
||||
}
|
||||
|
||||
if (mCallbacks[i].mReadOnly != aReadOnly) {
|
||||
// Callback is not r/w or r/o, go to another one in line
|
||||
++i;
|
||||
|
@ -625,6 +694,12 @@ bool CacheEntry::InvokeCallbacks(bool aReadOnly)
|
|||
}
|
||||
}
|
||||
|
||||
if (recreatedHandle) {
|
||||
// Must be released outside of the lock, enters InvokeCallback on the new entry
|
||||
mozilla::MutexAutoUnlock unlock(mLock);
|
||||
recreatedHandle = nullptr;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -991,6 +1066,13 @@ NS_IMETHODIMP CacheEntry::GetIsForcedValid(bool *aIsForcedValid)
|
|||
{
|
||||
NS_ENSURE_ARG(aIsForcedValid);
|
||||
|
||||
MOZ_ASSERT(mState > LOADING);
|
||||
|
||||
if (mPinned) {
|
||||
*aIsForcedValid = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsAutoCString key;
|
||||
|
||||
nsresult rv = HashingKeyWithStorage(key);
|
||||
|
@ -1442,6 +1524,28 @@ void CacheEntry::SetRegistered(bool aRegistered)
|
|||
}
|
||||
}
|
||||
|
||||
bool CacheEntry::DeferOrBypassRemovalOnPinStatus(bool aPinned)
|
||||
{
|
||||
LOG(("CacheEntry::DeferOrBypassRemovalOnPinStatus [this=%p]", this));
|
||||
|
||||
mozilla::MutexAutoLock lock(mLock);
|
||||
|
||||
if (mPinningKnown) {
|
||||
LOG((" pinned=%d, caller=%d", mPinned, aPinned));
|
||||
// Bypass when the pin status of this entry doesn't match the pin status
|
||||
// caller wants to remove
|
||||
return mPinned != aPinned;
|
||||
}
|
||||
|
||||
LOG((" pinning unknown, caller=%d", aPinned));
|
||||
// Oterwise, remember to doom after the status is determined for any
|
||||
// callback opening the entry after this point...
|
||||
Callback c(this, aPinned);
|
||||
RememberCallback(c);
|
||||
// ...and always bypass
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CacheEntry::Purge(uint32_t aWhat)
|
||||
{
|
||||
LOG(("CacheEntry::Purge [this=%p, what=%d]", this, aWhat));
|
||||
|
@ -1523,6 +1627,10 @@ void CacheEntry::DoomAlreadyRemoved()
|
|||
|
||||
mIsDoomed = true;
|
||||
|
||||
// Pretend pinning is know. This entry is now doomed for good, so don't
|
||||
// bother with defering doom because of unknown pinning state any more.
|
||||
mPinningKnown = true;
|
||||
|
||||
// This schedules dooming of the file, dooming is ensured to happen
|
||||
// sooner than demand to open the same file made after this point
|
||||
// so that we don't get this file for any newer opened entry(s).
|
||||
|
|
|
@ -55,7 +55,7 @@ public:
|
|||
NS_DECL_NSIRUNNABLE
|
||||
|
||||
CacheEntry(const nsACString& aStorageID, nsIURI* aURI, const nsACString& aEnhanceID,
|
||||
bool aUseDisk, bool aSkipSizeCheck);
|
||||
bool aUseDisk, bool aSkipSizeCheck, bool aPin);
|
||||
|
||||
void AsyncOpen(nsICacheEntryOpenCallback* aCallback, uint32_t aFlags);
|
||||
|
||||
|
@ -92,6 +92,7 @@ public:
|
|||
PURGE_WHOLE,
|
||||
};
|
||||
|
||||
bool DeferOrBypassRemovalOnPinStatus(bool aPinned);
|
||||
bool Purge(uint32_t aWhat);
|
||||
void PurgeAndDoom();
|
||||
void DoomAlreadyRemoved();
|
||||
|
@ -136,6 +137,9 @@ private:
|
|||
Callback(CacheEntry* aEntry,
|
||||
nsICacheEntryOpenCallback *aCallback,
|
||||
bool aReadOnly, bool aCheckOnAnyThread, bool aSecret);
|
||||
// Special constructor for Callback objects added to the chain
|
||||
// just to ensure proper defer dooming (recreation) of this entry.
|
||||
Callback(CacheEntry* aEntry, bool aDoomWhenFoundInPinStatus);
|
||||
Callback(Callback const &aThat);
|
||||
~Callback();
|
||||
|
||||
|
@ -143,6 +147,10 @@ private:
|
|||
// mainly during recreation.
|
||||
void ExchangeEntry(CacheEntry* aEntry);
|
||||
|
||||
// Returns true when an entry is about to be "defer" doomed and this is
|
||||
// a "defer" callback.
|
||||
bool DeferDoom(bool *aDoom) const;
|
||||
|
||||
// We are raising reference count here to take into account the pending
|
||||
// callback (that virtually holds a ref to this entry before it gets
|
||||
// it's pointer).
|
||||
|
@ -156,6 +164,13 @@ private:
|
|||
bool mNotWanted : 1;
|
||||
bool mSecret : 1;
|
||||
|
||||
// These are set only for the defer-doomer Callback instance inserted
|
||||
// to the callback chain. When any of these is set and also any of
|
||||
// the corressponding flags on the entry is set, this callback will
|
||||
// indicate (via DeferDoom()) the entry have to be recreated/doomed.
|
||||
bool mDoomWhenFoundPinned : 1;
|
||||
bool mDoomWhenFoundNonPinned : 1;
|
||||
|
||||
nsresult OnCheckThread(bool *aOnCheckThread) const;
|
||||
nsresult OnAvailThread(bool *aOnAvailThread) const;
|
||||
};
|
||||
|
@ -274,14 +289,9 @@ private:
|
|||
nsCString mStorageID;
|
||||
|
||||
// Whether it's allowed to persist the data to disk
|
||||
bool const mUseDisk;
|
||||
|
||||
bool const mUseDisk : 1;
|
||||
// Whether it should skip max size check.
|
||||
bool const mSkipSizeCheck;
|
||||
|
||||
// Set when entry is doomed with AsyncDoom() or DoomAlreadyRemoved().
|
||||
// Left as a standalone flag to not bother with locking (there is no need).
|
||||
bool mIsDoomed;
|
||||
bool const mSkipSizeCheck : 1;
|
||||
|
||||
// Following flags are all synchronized with the cache entry lock.
|
||||
|
||||
|
@ -296,6 +306,15 @@ private:
|
|||
// false: after load and a new file, or dropped to back to false when a writer
|
||||
// fails to open an output stream.
|
||||
bool mHasData : 1;
|
||||
// The indication of pinning this entry was open with
|
||||
bool mPinned : 1;
|
||||
// Whether the pinning state of the entry is known (equals to the actual state
|
||||
// of the cache file)
|
||||
bool mPinningKnown : 1;
|
||||
|
||||
// Set when entry is doomed with AsyncDoom() or DoomAlreadyRemoved().
|
||||
// Left as a standalone flag to not bother with locking (there is no need).
|
||||
bool mIsDoomed;
|
||||
|
||||
static char const * StateString(uint32_t aState);
|
||||
|
||||
|
|
|
@ -102,7 +102,7 @@ protected:
|
|||
nsCOMPtr<CacheFileChunkListener> mCallback;
|
||||
nsresult mRV;
|
||||
uint32_t mChunkIdx;
|
||||
RefPtr<CacheFileChunk> mChunk;
|
||||
RefPtr<CacheFileChunk> mChunk;
|
||||
};
|
||||
|
||||
|
||||
|
@ -185,6 +185,7 @@ CacheFile::CacheFile()
|
|||
, mMemoryOnly(false)
|
||||
, mSkipSizeCheck(false)
|
||||
, mOpenAsMemoryOnly(false)
|
||||
, mPinned(false)
|
||||
, mPriority(false)
|
||||
, mDataAccessed(false)
|
||||
, mDataIsDirty(false)
|
||||
|
@ -215,17 +216,21 @@ CacheFile::Init(const nsACString &aKey,
|
|||
bool aMemoryOnly,
|
||||
bool aSkipSizeCheck,
|
||||
bool aPriority,
|
||||
bool aPinned,
|
||||
CacheFileListener *aCallback)
|
||||
{
|
||||
MOZ_ASSERT(!mListener);
|
||||
MOZ_ASSERT(!mHandle);
|
||||
|
||||
MOZ_ASSERT(!(aMemoryOnly && aPinned));
|
||||
|
||||
nsresult rv;
|
||||
|
||||
mKey = aKey;
|
||||
mOpenAsMemoryOnly = mMemoryOnly = aMemoryOnly;
|
||||
mSkipSizeCheck = aSkipSizeCheck;
|
||||
mPriority = aPriority;
|
||||
mPinned = aPinned;
|
||||
|
||||
// Some consumers (at least nsHTTPCompressConv) assume that Read() can read
|
||||
// such amount of data that was announced by Available().
|
||||
|
@ -244,7 +249,7 @@ CacheFile::Init(const nsACString &aKey,
|
|||
if (mMemoryOnly) {
|
||||
MOZ_ASSERT(!aCallback);
|
||||
|
||||
mMetadata = new CacheFileMetadata(mOpenAsMemoryOnly, mKey);
|
||||
mMetadata = new CacheFileMetadata(mOpenAsMemoryOnly, false, mKey);
|
||||
mReady = true;
|
||||
mDataSize = mMetadata->Offset();
|
||||
return NS_OK;
|
||||
|
@ -256,7 +261,7 @@ CacheFile::Init(const nsACString &aKey,
|
|||
flags = CacheFileIOManager::CREATE_NEW;
|
||||
|
||||
// make sure we can use this entry immediately
|
||||
mMetadata = new CacheFileMetadata(mOpenAsMemoryOnly, mKey);
|
||||
mMetadata = new CacheFileMetadata(mOpenAsMemoryOnly, mPinned, mKey);
|
||||
mReady = true;
|
||||
mDataSize = mMetadata->Offset();
|
||||
} else {
|
||||
|
@ -267,6 +272,10 @@ CacheFile::Init(const nsACString &aKey,
|
|||
flags |= CacheFileIOManager::PRIORITY;
|
||||
}
|
||||
|
||||
if (mPinned) {
|
||||
flags |= CacheFileIOManager::PINNED;
|
||||
}
|
||||
|
||||
mOpeningFile = true;
|
||||
mListener = aCallback;
|
||||
rv = CacheFileIOManager::OpenFile(mKey, flags, this);
|
||||
|
@ -274,6 +283,12 @@ CacheFile::Init(const nsACString &aKey,
|
|||
mListener = nullptr;
|
||||
mOpeningFile = false;
|
||||
|
||||
if (mPinned) {
|
||||
LOG(("CacheFile::Init() - CacheFileIOManager::OpenFile() failed "
|
||||
"but we want to pin, fail the file opening. [this=%p]", this));
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
if (aCreateNew) {
|
||||
NS_WARNING("Forcing memory-only entry since OpenFile failed");
|
||||
LOG(("CacheFile::Init() - CacheFileIOManager::OpenFile() failed "
|
||||
|
@ -289,7 +304,7 @@ CacheFile::Init(const nsACString &aKey,
|
|||
"initializing entry as memory-only. [this=%p]", this));
|
||||
|
||||
mMemoryOnly = true;
|
||||
mMetadata = new CacheFileMetadata(mOpenAsMemoryOnly, mKey);
|
||||
mMetadata = new CacheFileMetadata(mOpenAsMemoryOnly, mPinned, mKey);
|
||||
mReady = true;
|
||||
mDataSize = mMetadata->Offset();
|
||||
|
||||
|
@ -482,7 +497,8 @@ CacheFile::OnFileOpened(CacheFileHandle *aHandle, nsresult aResult)
|
|||
autoDoom.mAlreadyDoomed = true;
|
||||
return NS_OK;
|
||||
}
|
||||
else if (NS_FAILED(aResult)) {
|
||||
|
||||
if (NS_FAILED(aResult)) {
|
||||
if (mMetadata) {
|
||||
// This entry was initialized as createNew, just switch to memory-only
|
||||
// mode.
|
||||
|
@ -494,7 +510,8 @@ CacheFile::OnFileOpened(CacheFileHandle *aHandle, nsresult aResult)
|
|||
mMemoryOnly = true;
|
||||
return NS_OK;
|
||||
}
|
||||
else if (aResult == NS_ERROR_FILE_INVALID_PATH) {
|
||||
|
||||
if (aResult == NS_ERROR_FILE_INVALID_PATH) {
|
||||
// CacheFileIOManager doesn't have mCacheDirectory, switch to
|
||||
// memory-only mode.
|
||||
NS_WARNING("Forcing memory-only entry since CacheFileIOManager doesn't "
|
||||
|
@ -504,22 +521,20 @@ CacheFile::OnFileOpened(CacheFileHandle *aHandle, nsresult aResult)
|
|||
this));
|
||||
|
||||
mMemoryOnly = true;
|
||||
mMetadata = new CacheFileMetadata(mOpenAsMemoryOnly, mKey);
|
||||
mMetadata = new CacheFileMetadata(mOpenAsMemoryOnly, mPinned, mKey);
|
||||
mReady = true;
|
||||
mDataSize = mMetadata->Offset();
|
||||
|
||||
isNew = true;
|
||||
retval = NS_OK;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// CacheFileIOManager::OpenFile() failed for another reason.
|
||||
isNew = false;
|
||||
retval = aResult;
|
||||
}
|
||||
|
||||
mListener.swap(listener);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
mHandle = aHandle;
|
||||
if (NS_FAILED(mStatus)) {
|
||||
CacheFileIOManager::DoomFile(mHandle, nullptr);
|
||||
|
@ -583,6 +598,7 @@ CacheFile::OnMetadataRead(nsresult aResult)
|
|||
|
||||
bool isNew = false;
|
||||
if (NS_SUCCEEDED(aResult)) {
|
||||
mPinned = mMetadata->Pinned();
|
||||
mReady = true;
|
||||
mDataSize = mMetadata->Offset();
|
||||
if (mDataSize == 0 && mMetadata->ElementsSize() == 0) {
|
||||
|
@ -1970,7 +1986,8 @@ CacheFile::InitIndexEntry()
|
|||
rv = CacheFileIOManager::InitIndexEntry(mHandle,
|
||||
mMetadata->OriginAttributes().mAppId,
|
||||
mMetadata->IsAnonymous(),
|
||||
mMetadata->OriginAttributes().mInBrowser);
|
||||
mMetadata->OriginAttributes().mInBrowser,
|
||||
mPinned);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
uint32_t expTime;
|
||||
|
|
|
@ -58,6 +58,7 @@ public:
|
|||
bool aMemoryOnly,
|
||||
bool aSkipSizeCheck,
|
||||
bool aPriority,
|
||||
bool aPinned,
|
||||
CacheFileListener *aCallback);
|
||||
|
||||
NS_IMETHOD OnChunkRead(nsresult aResult, CacheFileChunk *aChunk) override;
|
||||
|
@ -103,6 +104,7 @@ public:
|
|||
bool DataSize(int64_t* aSize);
|
||||
void Key(nsACString& aKey) { aKey = mKey; }
|
||||
bool IsDoomed();
|
||||
bool IsPinned() const { return mPinned; }
|
||||
bool IsWriteInProgress();
|
||||
|
||||
// Memory reporting
|
||||
|
@ -196,6 +198,7 @@ private:
|
|||
bool mMemoryOnly;
|
||||
bool mSkipSizeCheck;
|
||||
bool mOpenAsMemoryOnly;
|
||||
bool mPinned;
|
||||
bool mPriority;
|
||||
bool mDataAccessed;
|
||||
bool mDataIsDirty;
|
||||
|
@ -206,8 +209,8 @@ private:
|
|||
int64_t mDataSize;
|
||||
nsCString mKey;
|
||||
|
||||
RefPtr<CacheFileHandle> mHandle;
|
||||
RefPtr<CacheFileMetadata> mMetadata;
|
||||
RefPtr<CacheFileHandle> mHandle;
|
||||
RefPtr<CacheFileMetadata> mMetadata;
|
||||
nsCOMPtr<CacheFileListener> mListener;
|
||||
nsCOMPtr<CacheFileIOListener> mDoomAfterOpenListener;
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ public:
|
|||
|
||||
protected:
|
||||
nsCOMPtr<CacheFileChunkListener> mCallback;
|
||||
RefPtr<CacheFileChunk> mChunk;
|
||||
RefPtr<CacheFileChunk> mChunk;
|
||||
};
|
||||
|
||||
bool
|
||||
|
|
|
@ -150,7 +150,7 @@ private:
|
|||
uint32_t mRWBufSize;
|
||||
CacheHash::Hash16_t mReadHash;
|
||||
|
||||
RefPtr<CacheFile> mFile; // is null if chunk is cached to
|
||||
RefPtr<CacheFile> mFile; // is null if chunk is cached to
|
||||
// prevent reference cycles
|
||||
nsCOMPtr<CacheFileChunkListener> mListener;
|
||||
nsTArray<ChunkListenerItem *> mUpdateListeners;
|
||||
|
|
|
@ -81,32 +81,49 @@ CacheFileContextEvictor::ContextsCount()
|
|||
}
|
||||
|
||||
nsresult
|
||||
CacheFileContextEvictor::AddContext(nsILoadContextInfo *aLoadContextInfo)
|
||||
CacheFileContextEvictor::AddContext(nsILoadContextInfo *aLoadContextInfo,
|
||||
bool aPinned)
|
||||
{
|
||||
LOG(("CacheFileContextEvictor::AddContext() [this=%p, loadContextInfo=%p]",
|
||||
this, aLoadContextInfo));
|
||||
LOG(("CacheFileContextEvictor::AddContext() [this=%p, loadContextInfo=%p, pinned=%d]",
|
||||
this, aLoadContextInfo, aPinned));
|
||||
|
||||
nsresult rv;
|
||||
|
||||
MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
|
||||
|
||||
CacheFileContextEvictorEntry *entry = nullptr;
|
||||
for (uint32_t i = 0; i < mEntries.Length(); ++i) {
|
||||
if (mEntries[i]->mInfo->Equals(aLoadContextInfo)) {
|
||||
entry = mEntries[i];
|
||||
break;
|
||||
if (aLoadContextInfo) {
|
||||
for (uint32_t i = 0; i < mEntries.Length(); ++i) {
|
||||
if (mEntries[i]->mInfo &&
|
||||
mEntries[i]->mInfo->Equals(aLoadContextInfo) &&
|
||||
mEntries[i]->mPinned == aPinned) {
|
||||
entry = mEntries[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Not providing load context info means we want to delete everything,
|
||||
// so let's not bother with any currently running context cleanups
|
||||
// for the same pinning state.
|
||||
for (uint32_t i = mEntries.Length(); i > 0;) {
|
||||
--i;
|
||||
if (mEntries[i]->mInfo && mEntries[i]->mPinned == aPinned) {
|
||||
RemoveEvictInfoFromDisk(mEntries[i]->mInfo, mEntries[i]->mPinned);
|
||||
mEntries.RemoveElementAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!entry) {
|
||||
entry = new CacheFileContextEvictorEntry();
|
||||
entry->mInfo = aLoadContextInfo;
|
||||
entry->mPinned = aPinned;
|
||||
mEntries.AppendElement(entry);
|
||||
}
|
||||
|
||||
entry->mTimeStamp = PR_Now() / PR_USEC_PER_MSEC;
|
||||
|
||||
PersistEvictionInfoToDisk(aLoadContextInfo);
|
||||
PersistEvictionInfoToDisk(aLoadContextInfo, aPinned);
|
||||
|
||||
if (mIndexIsUpToDate) {
|
||||
// Already existing context could be added again, in this case the iterator
|
||||
|
@ -180,58 +197,62 @@ CacheFileContextEvictor::CacheIndexStateChanged()
|
|||
|
||||
nsresult
|
||||
CacheFileContextEvictor::WasEvicted(const nsACString &aKey, nsIFile *aFile,
|
||||
bool *_retval)
|
||||
bool *aEvictedAsPinned, bool *aEvictedAsNonPinned)
|
||||
{
|
||||
LOG(("CacheFileContextEvictor::WasEvicted() [key=%s]",
|
||||
PromiseFlatCString(aKey).get()));
|
||||
|
||||
nsresult rv;
|
||||
|
||||
*aEvictedAsPinned = false;
|
||||
*aEvictedAsNonPinned = false;
|
||||
|
||||
MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
|
||||
|
||||
nsCOMPtr<nsILoadContextInfo> info = CacheFileUtils::ParseKey(aKey);
|
||||
MOZ_ASSERT(info);
|
||||
if (!info) {
|
||||
LOG(("CacheFileContextEvictor::WasEvicted() - Cannot parse key!"));
|
||||
*_retval = false;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
CacheFileContextEvictorEntry *entry = nullptr;
|
||||
for (uint32_t i = 0; i < mEntries.Length(); ++i) {
|
||||
if (info->Equals(mEntries[i]->mInfo)) {
|
||||
entry = mEntries[i];
|
||||
break;
|
||||
CacheFileContextEvictorEntry *entry = mEntries[i];
|
||||
|
||||
if (entry->mInfo && !info->Equals(entry->mInfo)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
PRTime lastModifiedTime;
|
||||
rv = aFile->GetLastModifiedTime(&lastModifiedTime);
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG(("CacheFileContextEvictor::WasEvicted() - Cannot get last modified time"
|
||||
", returning false."));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (lastModifiedTime > entry->mTimeStamp) {
|
||||
// File has been modified since context eviction.
|
||||
continue;
|
||||
}
|
||||
|
||||
LOG(("CacheFileContextEvictor::WasEvicted() - evicted [pinning=%d, "
|
||||
"mTimeStamp=%lld, lastModifiedTime=%lld]",
|
||||
entry->mPinned, entry->mTimeStamp, lastModifiedTime));
|
||||
|
||||
if (entry->mPinned) {
|
||||
*aEvictedAsPinned = true;
|
||||
} else {
|
||||
*aEvictedAsNonPinned = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!entry) {
|
||||
LOG(("CacheFileContextEvictor::WasEvicted() - Didn't find equal context, "
|
||||
"returning false."));
|
||||
*_retval = false;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
PRTime lastModifiedTime;
|
||||
rv = aFile->GetLastModifiedTime(&lastModifiedTime);
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG(("CacheFileContextEvictor::WasEvicted() - Cannot get last modified time"
|
||||
", returning false."));
|
||||
*_retval = false;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
*_retval = !(lastModifiedTime > entry->mTimeStamp);
|
||||
LOG(("CacheFileContextEvictor::WasEvicted() - returning %s. [mTimeStamp=%lld,"
|
||||
" lastModifiedTime=%lld]", *_retval ? "true" : "false",
|
||||
mEntries[0]->mTimeStamp, lastModifiedTime));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
CacheFileContextEvictor::PersistEvictionInfoToDisk(
|
||||
nsILoadContextInfo *aLoadContextInfo)
|
||||
nsILoadContextInfo *aLoadContextInfo, bool aPinned)
|
||||
{
|
||||
LOG(("CacheFileContextEvictor::PersistEvictionInfoToDisk() [this=%p, "
|
||||
"loadContextInfo=%p]", this, aLoadContextInfo));
|
||||
|
@ -241,7 +262,7 @@ CacheFileContextEvictor::PersistEvictionInfoToDisk(
|
|||
MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
|
||||
|
||||
nsCOMPtr<nsIFile> file;
|
||||
rv = GetContextFile(aLoadContextInfo, getter_AddRefs(file));
|
||||
rv = GetContextFile(aLoadContextInfo, aPinned, getter_AddRefs(file));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
@ -268,7 +289,7 @@ CacheFileContextEvictor::PersistEvictionInfoToDisk(
|
|||
|
||||
nsresult
|
||||
CacheFileContextEvictor::RemoveEvictInfoFromDisk(
|
||||
nsILoadContextInfo *aLoadContextInfo)
|
||||
nsILoadContextInfo *aLoadContextInfo, bool aPinned)
|
||||
{
|
||||
LOG(("CacheFileContextEvictor::RemoveEvictInfoFromDisk() [this=%p, "
|
||||
"loadContextInfo=%p]", this, aLoadContextInfo));
|
||||
|
@ -278,7 +299,7 @@ CacheFileContextEvictor::RemoveEvictInfoFromDisk(
|
|||
MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
|
||||
|
||||
nsCOMPtr<nsIFile> file;
|
||||
rv = GetContextFile(aLoadContextInfo, getter_AddRefs(file));
|
||||
rv = GetContextFile(aLoadContextInfo, aPinned, getter_AddRefs(file));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
@ -363,16 +384,26 @@ CacheFileContextEvictor::LoadEvictInfoFromDisk()
|
|||
continue;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsILoadContextInfo> info = CacheFileUtils::ParseKey(decoded);
|
||||
|
||||
if (!info) {
|
||||
LOG(("CacheFileContextEvictor::LoadEvictInfoFromDisk() - Cannot parse "
|
||||
"context key, removing file. [contextKey=%s, file=%s]",
|
||||
decoded.get(), leaf.get()));
|
||||
file->Remove(false);
|
||||
continue;
|
||||
bool pinned = decoded[0] == '\t';
|
||||
if (pinned) {
|
||||
decoded = Substring(decoded, 1);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsILoadContextInfo> info;
|
||||
if (!NS_LITERAL_CSTRING("*").Equals(decoded)) {
|
||||
// "*" is indication of 'delete all', info left null will pass
|
||||
// to CacheFileContextEvictor::AddContext and clear all the cache data.
|
||||
info = CacheFileUtils::ParseKey(decoded);
|
||||
if (!info) {
|
||||
LOG(("CacheFileContextEvictor::LoadEvictInfoFromDisk() - Cannot parse "
|
||||
"context key, removing file. [contextKey=%s, file=%s]",
|
||||
decoded.get(), leaf.get()));
|
||||
file->Remove(false);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PRTime lastModifiedTime;
|
||||
rv = file->GetLastModifiedTime(&lastModifiedTime);
|
||||
if (NS_FAILED(rv)) {
|
||||
|
@ -381,6 +412,7 @@ CacheFileContextEvictor::LoadEvictInfoFromDisk()
|
|||
|
||||
CacheFileContextEvictorEntry *entry = new CacheFileContextEvictorEntry();
|
||||
entry->mInfo = info;
|
||||
entry->mPinned = pinned;
|
||||
entry->mTimeStamp = lastModifiedTime;
|
||||
mEntries.AppendElement(entry);
|
||||
}
|
||||
|
@ -390,6 +422,7 @@ CacheFileContextEvictor::LoadEvictInfoFromDisk()
|
|||
|
||||
nsresult
|
||||
CacheFileContextEvictor::GetContextFile(nsILoadContextInfo *aLoadContextInfo,
|
||||
bool aPinned,
|
||||
nsIFile **_retval)
|
||||
{
|
||||
nsresult rv;
|
||||
|
@ -398,7 +431,16 @@ CacheFileContextEvictor::GetContextFile(nsILoadContextInfo *aLoadContextInfo,
|
|||
leafName.AssignLiteral(CONTEXT_EVICTION_PREFIX);
|
||||
|
||||
nsAutoCString keyPrefix;
|
||||
CacheFileUtils::AppendKeyPrefix(aLoadContextInfo, keyPrefix);
|
||||
if (aPinned) {
|
||||
// Mark pinned context files with a tab char at the start.
|
||||
// Tab is chosen because it can never be used as a context key tag.
|
||||
keyPrefix.Append('\t');
|
||||
}
|
||||
if (aLoadContextInfo) {
|
||||
CacheFileUtils::AppendKeyPrefix(aLoadContextInfo, keyPrefix);
|
||||
} else {
|
||||
keyPrefix.Append('*');
|
||||
}
|
||||
|
||||
nsAutoCString data64;
|
||||
rv = Base64Encode(keyPrefix, data64);
|
||||
|
@ -530,7 +572,7 @@ CacheFileContextEvictor::EvictEntries()
|
|||
LOG(("CacheFileContextEvictor::EvictEntries() - No more entries left in "
|
||||
"iterator. [iterator=%p, info=%p]", mEntries[0]->mIterator.get(),
|
||||
mEntries[0]->mInfo.get()));
|
||||
RemoveEvictInfoFromDisk(mEntries[0]->mInfo);
|
||||
RemoveEvictInfoFromDisk(mEntries[0]->mInfo, mEntries[0]->mPinned);
|
||||
mEntries.RemoveElementAt(0);
|
||||
continue;
|
||||
} else if (NS_FAILED(rv)) {
|
||||
|
@ -557,6 +599,20 @@ CacheFileContextEvictor::EvictEntries()
|
|||
continue;
|
||||
}
|
||||
|
||||
CacheIndex::EntryStatus status;
|
||||
bool pinned;
|
||||
rv = CacheIndex::HasEntry(hash, &status, &pinned);
|
||||
// This must never fail, since eviction (this code) happens only when the index
|
||||
// is up-to-date and thus the informatin is known.
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
||||
|
||||
if (pinned != mEntries[0]->mPinned) {
|
||||
LOG(("CacheFileContextEvictor::EvictEntries() - Skipping entry since pinning "
|
||||
"doesn't match [evicting pinned=%d, entry pinned=%d]",
|
||||
mEntries[0]->mPinned, pinned));
|
||||
continue;
|
||||
}
|
||||
|
||||
nsAutoCString leafName;
|
||||
CacheFileIOManager::HashToStr(&hash, leafName);
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ class CacheIndexIterator;
|
|||
struct CacheFileContextEvictorEntry
|
||||
{
|
||||
nsCOMPtr<nsILoadContextInfo> mInfo;
|
||||
bool mPinned;
|
||||
PRTime mTimeStamp; // in milliseconds
|
||||
RefPtr<CacheIndexIterator> mIterator;
|
||||
};
|
||||
|
@ -40,7 +41,7 @@ public:
|
|||
// Returns number of contexts that are being evicted.
|
||||
uint32_t ContextsCount();
|
||||
// Start evicting given context.
|
||||
nsresult AddContext(nsILoadContextInfo *aLoadContextInfo);
|
||||
nsresult AddContext(nsILoadContextInfo *aLoadContextInfo, bool aPinned);
|
||||
// CacheFileIOManager calls this method when CacheIndex's state changes. We
|
||||
// check whether the index is up to date and start or stop evicting according
|
||||
// to index's state.
|
||||
|
@ -50,21 +51,22 @@ public:
|
|||
// info to the given key and the last modified time of the entry file is
|
||||
// earlier than the time stamp of the time when the context was added to the
|
||||
// evictor.
|
||||
nsresult WasEvicted(const nsACString &aKey, nsIFile *aFile, bool *_retval);
|
||||
nsresult WasEvicted(const nsACString &aKey, nsIFile *aFile,
|
||||
bool *aEvictedAsPinned, bool *aEvictedAsNonPinned);
|
||||
|
||||
private:
|
||||
// Writes information about eviction of the given context to the disk. This is
|
||||
// done for every context added to the evictor to be able to recover eviction
|
||||
// after a shutdown or crash. When the context file is found after startup, we
|
||||
// restore mTimeStamp from the last modified time of the file.
|
||||
nsresult PersistEvictionInfoToDisk(nsILoadContextInfo *aLoadContextInfo);
|
||||
nsresult PersistEvictionInfoToDisk(nsILoadContextInfo *aLoadContextInfo, bool aPinned);
|
||||
// Once we are done with eviction for the given context, the eviction info is
|
||||
// removed from the disk.
|
||||
nsresult RemoveEvictInfoFromDisk(nsILoadContextInfo *aLoadContextInfo);
|
||||
nsresult RemoveEvictInfoFromDisk(nsILoadContextInfo *aLoadContextInfo, bool aPinned);
|
||||
// Tries to load all contexts from the disk. This method is called just once
|
||||
// after startup.
|
||||
nsresult LoadEvictInfoFromDisk();
|
||||
nsresult GetContextFile(nsILoadContextInfo *aLoadContextInfo,
|
||||
nsresult GetContextFile(nsILoadContextInfo *aLoadContextInfo, bool aPinned,
|
||||
nsIFile **_retval);
|
||||
|
||||
void CreateIterators();
|
||||
|
|
|
@ -108,13 +108,16 @@ NS_INTERFACE_MAP_BEGIN(CacheFileHandle)
|
|||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END_THREADSAFE
|
||||
|
||||
CacheFileHandle::CacheFileHandle(const SHA1Sum::Hash *aHash, bool aPriority)
|
||||
CacheFileHandle::CacheFileHandle(const SHA1Sum::Hash *aHash, bool aPriority, PinningStatus aPinning)
|
||||
: mHash(aHash)
|
||||
, mPriority(aPriority)
|
||||
, mClosed(false)
|
||||
, mSpecialFile(false)
|
||||
, mInvalid(false)
|
||||
, mFileExists(false)
|
||||
, mPinning(aPinning)
|
||||
, mDoomWhenFoundPinned(false)
|
||||
, mDoomWhenFoundNonPinned(false)
|
||||
, mFileSize(-1)
|
||||
, mFD(nullptr)
|
||||
{
|
||||
|
@ -127,13 +130,16 @@ CacheFileHandle::CacheFileHandle(const SHA1Sum::Hash *aHash, bool aPriority)
|
|||
, this, LOGSHA1(aHash)));
|
||||
}
|
||||
|
||||
CacheFileHandle::CacheFileHandle(const nsACString &aKey, bool aPriority)
|
||||
CacheFileHandle::CacheFileHandle(const nsACString &aKey, bool aPriority, PinningStatus aPinning)
|
||||
: mHash(nullptr)
|
||||
, mPriority(aPriority)
|
||||
, mClosed(false)
|
||||
, mSpecialFile(true)
|
||||
, mInvalid(false)
|
||||
, mFileExists(false)
|
||||
, mPinning(aPinning)
|
||||
, mDoomWhenFoundPinned(false)
|
||||
, mDoomWhenFoundNonPinned(false)
|
||||
, mFileSize(-1)
|
||||
, mFD(nullptr)
|
||||
, mKey(aKey)
|
||||
|
@ -199,6 +205,32 @@ CacheFileHandle::FileSizeInK() const
|
|||
return size;
|
||||
}
|
||||
|
||||
bool
|
||||
CacheFileHandle::SetPinned(bool aPinned)
|
||||
{
|
||||
LOG(("CacheFileHandle::SetPinned [this=%p, pinned=%d]", this, aPinned));
|
||||
|
||||
MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
|
||||
|
||||
mPinning = aPinned
|
||||
? PinningStatus::PINNED
|
||||
: PinningStatus::NON_PINNED;
|
||||
|
||||
if ((MOZ_UNLIKELY(mDoomWhenFoundPinned) && aPinned) ||
|
||||
(MOZ_UNLIKELY(mDoomWhenFoundNonPinned) && !aPinned)) {
|
||||
|
||||
LOG((" dooming, when: pinned=%d, non-pinned=%d, found: pinned=%d",
|
||||
bool(mDoomWhenFoundPinned), bool(mDoomWhenFoundNonPinned), aPinned));
|
||||
|
||||
mDoomWhenFoundPinned = false;
|
||||
mDoomWhenFoundNonPinned = false;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Memory reporting
|
||||
|
||||
size_t
|
||||
|
@ -360,7 +392,7 @@ CacheFileHandles::GetHandle(const SHA1Sum::Hash *aHash,
|
|||
|
||||
nsresult
|
||||
CacheFileHandles::NewHandle(const SHA1Sum::Hash *aHash,
|
||||
bool aPriority,
|
||||
bool aPriority, CacheFileHandle::PinningStatus aPinning,
|
||||
CacheFileHandle **_retval)
|
||||
{
|
||||
MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
|
||||
|
@ -381,7 +413,7 @@ CacheFileHandles::NewHandle(const SHA1Sum::Hash *aHash,
|
|||
entry->AssertHandlesState();
|
||||
#endif
|
||||
|
||||
RefPtr<CacheFileHandle> handle = new CacheFileHandle(entry->Hash(), aPriority);
|
||||
RefPtr<CacheFileHandle> handle = new CacheFileHandle(entry->Hash(), aPriority, aPinning);
|
||||
entry->AddHandle(handle);
|
||||
|
||||
LOG(("CacheFileHandles::NewHandle() hash=%08x%08x%08x%08x%08x "
|
||||
|
@ -576,8 +608,8 @@ public:
|
|||
}
|
||||
}
|
||||
}
|
||||
mCallback->OnFileOpened(mHandle, rv);
|
||||
|
||||
mCallback->OnFileOpened(mHandle, rv);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -585,8 +617,8 @@ protected:
|
|||
SHA1Sum::Hash mHash;
|
||||
uint32_t mFlags;
|
||||
nsCOMPtr<CacheFileIOListener> mCallback;
|
||||
RefPtr<CacheFileIOManager> mIOMan;
|
||||
RefPtr<CacheFileHandle> mHandle;
|
||||
RefPtr<CacheFileIOManager> mIOMan;
|
||||
RefPtr<CacheFileHandle> mHandle;
|
||||
nsCString mKey;
|
||||
};
|
||||
|
||||
|
@ -626,7 +658,7 @@ public:
|
|||
}
|
||||
|
||||
protected:
|
||||
RefPtr<CacheFileHandle> mHandle;
|
||||
RefPtr<CacheFileHandle> mHandle;
|
||||
int64_t mOffset;
|
||||
char *mBuf;
|
||||
int32_t mCount;
|
||||
|
@ -685,7 +717,7 @@ public:
|
|||
}
|
||||
|
||||
protected:
|
||||
RefPtr<CacheFileHandle> mHandle;
|
||||
RefPtr<CacheFileHandle> mHandle;
|
||||
int64_t mOffset;
|
||||
const char *mBuf;
|
||||
int32_t mCount;
|
||||
|
@ -729,9 +761,9 @@ public:
|
|||
}
|
||||
|
||||
protected:
|
||||
nsCOMPtr<CacheFileIOListener> mCallback;
|
||||
nsCOMPtr<nsIEventTarget> mTarget;
|
||||
RefPtr<CacheFileHandle> mHandle;
|
||||
nsCOMPtr<CacheFileIOListener> mCallback;
|
||||
nsCOMPtr<nsIEventTarget> mTarget;
|
||||
RefPtr<CacheFileHandle> mHandle;
|
||||
};
|
||||
|
||||
class DoomFileByKeyEvent : public nsRunnable {
|
||||
|
@ -777,7 +809,7 @@ public:
|
|||
protected:
|
||||
SHA1Sum::Hash mHash;
|
||||
nsCOMPtr<CacheFileIOListener> mCallback;
|
||||
RefPtr<CacheFileIOManager> mIOMan;
|
||||
RefPtr<CacheFileIOManager> mIOMan;
|
||||
};
|
||||
|
||||
class ReleaseNSPRHandleEvent : public nsRunnable {
|
||||
|
@ -805,7 +837,7 @@ public:
|
|||
}
|
||||
|
||||
protected:
|
||||
RefPtr<CacheFileHandle> mHandle;
|
||||
RefPtr<CacheFileHandle> mHandle;
|
||||
};
|
||||
|
||||
class TruncateSeekSetEOFEvent : public nsRunnable {
|
||||
|
@ -846,7 +878,7 @@ public:
|
|||
}
|
||||
|
||||
protected:
|
||||
RefPtr<CacheFileHandle> mHandle;
|
||||
RefPtr<CacheFileHandle> mHandle;
|
||||
int64_t mTruncatePos;
|
||||
int64_t mEOFPos;
|
||||
nsCOMPtr<CacheFileIOListener> mCallback;
|
||||
|
@ -889,7 +921,7 @@ public:
|
|||
}
|
||||
|
||||
protected:
|
||||
RefPtr<CacheFileHandle> mHandle;
|
||||
RefPtr<CacheFileHandle> mHandle;
|
||||
nsCString mNewName;
|
||||
nsCOMPtr<CacheFileIOListener> mCallback;
|
||||
};
|
||||
|
@ -897,11 +929,12 @@ protected:
|
|||
class InitIndexEntryEvent : public nsRunnable {
|
||||
public:
|
||||
InitIndexEntryEvent(CacheFileHandle *aHandle, uint32_t aAppId,
|
||||
bool aAnonymous, bool aInBrowser)
|
||||
bool aAnonymous, bool aInBrowser, bool aPinning)
|
||||
: mHandle(aHandle)
|
||||
, mAppId(aAppId)
|
||||
, mAnonymous(aAnonymous)
|
||||
, mInBrowser(aInBrowser)
|
||||
, mPinning(aPinning)
|
||||
{
|
||||
MOZ_COUNT_CTOR(InitIndexEntryEvent);
|
||||
}
|
||||
|
@ -919,7 +952,7 @@ public:
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
CacheIndex::InitEntry(mHandle->Hash(), mAppId, mAnonymous, mInBrowser);
|
||||
CacheIndex::InitEntry(mHandle->Hash(), mAppId, mAnonymous, mInBrowser, mPinning);
|
||||
|
||||
// We cannot set the filesize before we init the entry. If we're opening
|
||||
// an existing entry file, frecency and expiration time will be set after
|
||||
|
@ -936,6 +969,7 @@ protected:
|
|||
uint32_t mAppId;
|
||||
bool mAnonymous;
|
||||
bool mInBrowser;
|
||||
bool mPinning;
|
||||
};
|
||||
|
||||
class UpdateIndexEntryEvent : public nsRunnable {
|
||||
|
@ -1523,6 +1557,10 @@ CacheFileIOManager::OpenFileInternal(const SHA1Sum::Hash *aHash,
|
|||
if (NS_FAILED(rv)) return rv;
|
||||
}
|
||||
|
||||
CacheFileHandle::PinningStatus pinning = aFlags & PINNED
|
||||
? CacheFileHandle::PinningStatus::PINNED
|
||||
: CacheFileHandle::PinningStatus::NON_PINNED;
|
||||
|
||||
nsCOMPtr<nsIFile> file;
|
||||
rv = GetFile(aHash, getter_AddRefs(file));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
@ -1537,7 +1575,7 @@ CacheFileIOManager::OpenFileInternal(const SHA1Sum::Hash *aHash,
|
|||
handle = nullptr;
|
||||
}
|
||||
|
||||
rv = mHandles.NewHandle(aHash, aFlags & PRIORITY, getter_AddRefs(handle));
|
||||
rv = mHandles.NewHandle(aHash, aFlags & PRIORITY, pinning, getter_AddRefs(handle));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
bool exists;
|
||||
|
@ -1567,7 +1605,7 @@ CacheFileIOManager::OpenFileInternal(const SHA1Sum::Hash *aHash,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
bool exists;
|
||||
bool exists, evictedAsPinned = false, evictedAsNonPinned = false;
|
||||
rv = file->Exists(&exists);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
|
@ -1575,15 +1613,7 @@ CacheFileIOManager::OpenFileInternal(const SHA1Sum::Hash *aHash,
|
|||
if (mContextEvictor->ContextsCount() == 0) {
|
||||
mContextEvictor = nullptr;
|
||||
} else {
|
||||
bool wasEvicted = false;
|
||||
mContextEvictor->WasEvicted(aKey, file, &wasEvicted);
|
||||
if (wasEvicted) {
|
||||
LOG(("CacheFileIOManager::OpenFileInternal() - Removing file since the "
|
||||
"entry was evicted by EvictByContext()"));
|
||||
exists = false;
|
||||
file->Remove(false);
|
||||
CacheIndex::RemoveEntry(aHash);
|
||||
}
|
||||
mContextEvictor->WasEvicted(aKey, file, &evictedAsPinned, &evictedAsNonPinned);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1591,10 +1621,29 @@ CacheFileIOManager::OpenFileInternal(const SHA1Sum::Hash *aHash,
|
|||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
rv = mHandles.NewHandle(aHash, aFlags & PRIORITY, getter_AddRefs(handle));
|
||||
if (exists) {
|
||||
// For existing files we determine the pinning status later, after the metadata gets parsed.
|
||||
pinning = CacheFileHandle::PinningStatus::UNKNOWN;
|
||||
}
|
||||
|
||||
rv = mHandles.NewHandle(aHash, aFlags & PRIORITY, pinning, getter_AddRefs(handle));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (exists) {
|
||||
// If this file has been found evicted through the context file evictor above for
|
||||
// any of pinned or non-pinned state, these calls ensure we doom the handle ASAP
|
||||
// we know the real pinning state after metadta has been parsed. DoomFileInternal
|
||||
// on the |handle| doesn't doom right now, since the pinning state is unknown
|
||||
// and we pass down a pinning restriction.
|
||||
if (evictedAsPinned) {
|
||||
rv = DoomFileInternal(handle, DOOM_WHEN_PINNED);
|
||||
MOZ_ASSERT(!handle->IsDoomed() && NS_SUCCEEDED(rv));
|
||||
}
|
||||
if (evictedAsNonPinned) {
|
||||
rv = DoomFileInternal(handle, DOOM_WHEN_NON_PINNED);
|
||||
MOZ_ASSERT(!handle->IsDoomed() && NS_SUCCEEDED(rv));
|
||||
}
|
||||
|
||||
rv = file->GetFileSize(&handle->mFileSize);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
|
@ -1652,7 +1701,7 @@ CacheFileIOManager::OpenSpecialFileInternal(const nsACString &aKey,
|
|||
handle = nullptr;
|
||||
}
|
||||
|
||||
handle = new CacheFileHandle(aKey, aFlags & PRIORITY);
|
||||
handle = new CacheFileHandle(aKey, aFlags & PRIORITY, CacheFileHandle::PinningStatus::NON_PINNED);
|
||||
mSpecialHandles.AppendElement(handle);
|
||||
|
||||
bool exists;
|
||||
|
@ -1687,7 +1736,7 @@ CacheFileIOManager::OpenSpecialFileInternal(const nsACString &aKey,
|
|||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
handle = new CacheFileHandle(aKey, aFlags & PRIORITY);
|
||||
handle = new CacheFileHandle(aKey, aFlags & PRIORITY, CacheFileHandle::PinningStatus::NON_PINNED);
|
||||
mSpecialHandles.AppendElement(handle);
|
||||
|
||||
if (exists) {
|
||||
|
@ -1984,17 +2033,52 @@ CacheFileIOManager::DoomFile(CacheFileHandle *aHandle,
|
|||
}
|
||||
|
||||
nsresult
|
||||
CacheFileIOManager::DoomFileInternal(CacheFileHandle *aHandle)
|
||||
CacheFileIOManager::DoomFileInternal(CacheFileHandle *aHandle,
|
||||
PinningDoomRestriction aPinningDoomRestriction)
|
||||
{
|
||||
LOG(("CacheFileIOManager::DoomFileInternal() [handle=%p]", aHandle));
|
||||
aHandle->Log();
|
||||
|
||||
MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
|
||||
|
||||
nsresult rv;
|
||||
|
||||
if (aHandle->IsDoomed()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (aPinningDoomRestriction > NO_RESTRICTION) {
|
||||
switch (aHandle->mPinning) {
|
||||
case CacheFileHandle::PinningStatus::NON_PINNED:
|
||||
if (MOZ_LIKELY(aPinningDoomRestriction != DOOM_WHEN_NON_PINNED)) {
|
||||
LOG((" not dooming, it's a non-pinned handle"));
|
||||
return NS_OK;
|
||||
}
|
||||
// Doom now
|
||||
break;
|
||||
|
||||
case CacheFileHandle::PinningStatus::PINNED:
|
||||
if (MOZ_UNLIKELY(aPinningDoomRestriction != DOOM_WHEN_PINNED)) {
|
||||
LOG((" not dooming, it's a pinned handle"));
|
||||
return NS_OK;
|
||||
}
|
||||
// Doom now
|
||||
break;
|
||||
|
||||
case CacheFileHandle::PinningStatus::UNKNOWN:
|
||||
if (MOZ_LIKELY(aPinningDoomRestriction == DOOM_WHEN_NON_PINNED)) {
|
||||
LOG((" doom when non-pinned set"));
|
||||
aHandle->mDoomWhenFoundNonPinned = true;
|
||||
} else if (MOZ_UNLIKELY(aPinningDoomRestriction == DOOM_WHEN_PINNED)) {
|
||||
LOG((" doom when pinned set"));
|
||||
aHandle->mDoomWhenFoundPinned = true;
|
||||
}
|
||||
|
||||
LOG((" pinning status not known, deferring doom decision"));
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
if (aHandle->mFileExists) {
|
||||
// we need to move the current file to the doomed directory
|
||||
if (aHandle->mFD) {
|
||||
|
@ -2784,7 +2868,7 @@ CacheFileIOManager::EvictAllInternal()
|
|||
|
||||
// static
|
||||
nsresult
|
||||
CacheFileIOManager::EvictByContext(nsILoadContextInfo *aLoadContextInfo)
|
||||
CacheFileIOManager::EvictByContext(nsILoadContextInfo *aLoadContextInfo, bool aPinned)
|
||||
{
|
||||
LOG(("CacheFileIOManager::EvictByContext() [loadContextInfo=%p]",
|
||||
aLoadContextInfo));
|
||||
|
@ -2797,8 +2881,8 @@ CacheFileIOManager::EvictByContext(nsILoadContextInfo *aLoadContextInfo)
|
|||
}
|
||||
|
||||
nsCOMPtr<nsIRunnable> ev;
|
||||
ev = NS_NewRunnableMethodWithArg<nsCOMPtr<nsILoadContextInfo> >
|
||||
(ioMan, &CacheFileIOManager::EvictByContextInternal, aLoadContextInfo);
|
||||
ev = NS_NewRunnableMethodWithArgs<nsCOMPtr<nsILoadContextInfo>, bool>
|
||||
(ioMan, &CacheFileIOManager::EvictByContextInternal, aLoadContextInfo, aPinned);
|
||||
|
||||
rv = ioMan->mIOThread->DispatchAfterPendingOpens(ev);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
|
@ -2809,24 +2893,35 @@ CacheFileIOManager::EvictByContext(nsILoadContextInfo *aLoadContextInfo)
|
|||
}
|
||||
|
||||
nsresult
|
||||
CacheFileIOManager::EvictByContextInternal(nsILoadContextInfo *aLoadContextInfo)
|
||||
CacheFileIOManager::EvictByContextInternal(nsILoadContextInfo *aLoadContextInfo, bool aPinned)
|
||||
{
|
||||
nsAutoCString suffix;
|
||||
aLoadContextInfo->OriginAttributesPtr()->CreateSuffix(suffix);
|
||||
LOG(("CacheFileIOManager::EvictByContextInternal() [loadContextInfo=%p, "
|
||||
"anonymous=%u, suffix=%s]", aLoadContextInfo, aLoadContextInfo->IsAnonymous(),
|
||||
suffix.get()));
|
||||
LOG(("CacheFileIOManager::EvictByContextInternal() [loadContextInfo=%p, pinned=%d]",
|
||||
aLoadContextInfo, aPinned));
|
||||
|
||||
nsresult rv;
|
||||
|
||||
MOZ_ASSERT(mIOThread->IsCurrentThread());
|
||||
if (aLoadContextInfo) {
|
||||
nsAutoCString suffix;
|
||||
aLoadContextInfo->OriginAttributesPtr()->CreateSuffix(suffix);
|
||||
LOG((" anonymous=%u, suffix=%s]", aLoadContextInfo->IsAnonymous(), suffix.get()));
|
||||
|
||||
MOZ_ASSERT(!aLoadContextInfo->IsPrivate());
|
||||
if (aLoadContextInfo->IsPrivate()) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
MOZ_ASSERT(mIOThread->IsCurrentThread());
|
||||
|
||||
MOZ_ASSERT(!aLoadContextInfo->IsPrivate());
|
||||
if (aLoadContextInfo->IsPrivate()) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
}
|
||||
|
||||
if (!mCacheDirectory) {
|
||||
// This is a kind of hack. Somebody called EvictAll() without a profile.
|
||||
// This happens in xpcshell tests that use cache without profile. We need
|
||||
// to notify observers in this case since the tests are waiting for it.
|
||||
// Also notify for aPinned == true, those are interested as well.
|
||||
if (!aLoadContextInfo) {
|
||||
RefPtr<EvictionNotifierRunnable> r = new EvictionNotifierRunnable();
|
||||
NS_DispatchToMainThread(r);
|
||||
}
|
||||
return NS_ERROR_FILE_INVALID_PATH;
|
||||
}
|
||||
|
||||
|
@ -2846,24 +2941,38 @@ CacheFileIOManager::EvictByContextInternal(nsILoadContextInfo *aLoadContextInfo)
|
|||
mHandles.GetActiveHandles(&handles);
|
||||
|
||||
for (uint32_t i = 0; i < handles.Length(); ++i) {
|
||||
bool equals;
|
||||
rv = CacheFileUtils::KeyMatchesLoadContextInfo(handles[i]->Key(),
|
||||
aLoadContextInfo,
|
||||
&equals);
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG(("CacheFileIOManager::EvictByContextInternal() - Cannot parse key in "
|
||||
"handle! [handle=%p, key=%s]", handles[i].get(),
|
||||
handles[i]->Key().get()));
|
||||
MOZ_CRASH("Unexpected error!");
|
||||
}
|
||||
CacheFileHandle* handle = handles[i];
|
||||
|
||||
if (equals) {
|
||||
rv = DoomFileInternal(handles[i]);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
LOG(("CacheFileIOManager::EvictByContextInternal() - Cannot doom handle"
|
||||
" [handle=%p]", handles[i].get()));
|
||||
if (aLoadContextInfo) {
|
||||
bool equals;
|
||||
rv = CacheFileUtils::KeyMatchesLoadContextInfo(handle->Key(),
|
||||
aLoadContextInfo,
|
||||
&equals);
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG(("CacheFileIOManager::EvictByContextInternal() - Cannot parse key in "
|
||||
"handle! [handle=%p, key=%s]", handle, handle->Key().get()));
|
||||
MOZ_CRASH("Unexpected error!");
|
||||
}
|
||||
|
||||
if (!equals) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// handle will be doomed only when pinning status is known and equal or
|
||||
// doom decision will be deferred until pinning status is determined.
|
||||
rv = DoomFileInternal(handle, aPinned
|
||||
? CacheFileIOManager::DOOM_WHEN_PINNED
|
||||
: CacheFileIOManager::DOOM_WHEN_NON_PINNED);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
LOG(("CacheFileIOManager::EvictByContextInternal() - Cannot doom handle"
|
||||
" [handle=%p]", handle));
|
||||
}
|
||||
}
|
||||
|
||||
if (!aLoadContextInfo) {
|
||||
RefPtr<EvictionNotifierRunnable> r = new EvictionNotifierRunnable();
|
||||
NS_DispatchToMainThread(r);
|
||||
}
|
||||
|
||||
if (!mContextEvictor) {
|
||||
|
@ -2871,7 +2980,7 @@ CacheFileIOManager::EvictByContextInternal(nsILoadContextInfo *aLoadContextInfo)
|
|||
mContextEvictor->Init(mCacheDirectory);
|
||||
}
|
||||
|
||||
mContextEvictor->AddContext(aLoadContextInfo);
|
||||
mContextEvictor->AddContext(aLoadContextInfo, aPinned);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -3243,10 +3352,11 @@ nsresult
|
|||
CacheFileIOManager::InitIndexEntry(CacheFileHandle *aHandle,
|
||||
uint32_t aAppId,
|
||||
bool aAnonymous,
|
||||
bool aInBrowser)
|
||||
bool aInBrowser,
|
||||
bool aPinning)
|
||||
{
|
||||
LOG(("CacheFileIOManager::InitIndexEntry() [handle=%p, appId=%u, anonymous=%d"
|
||||
", inBrowser=%d]", aHandle, aAppId, aAnonymous, aInBrowser));
|
||||
", inBrowser=%d, pinned=%d]", aHandle, aAppId, aAnonymous, aInBrowser, aPinning));
|
||||
|
||||
nsresult rv;
|
||||
RefPtr<CacheFileIOManager> ioMan = gInstance;
|
||||
|
@ -3260,7 +3370,7 @@ CacheFileIOManager::InitIndexEntry(CacheFileHandle *aHandle,
|
|||
}
|
||||
|
||||
RefPtr<InitIndexEntryEvent> ev =
|
||||
new InitIndexEntryEvent(aHandle, aAppId, aAnonymous, aInBrowser);
|
||||
new InitIndexEntryEvent(aHandle, aAppId, aAnonymous, aInBrowser, aPinning);
|
||||
rv = ioMan->mIOThread->Dispatch(ev, CacheIOThread::WRITE);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
|
|
|
@ -29,6 +29,8 @@ namespace mozilla {
|
|||
namespace net {
|
||||
|
||||
class CacheFile;
|
||||
class CacheFileIOListener;
|
||||
|
||||
#ifdef DEBUG_HANDLES
|
||||
class CacheFileHandlesEntry;
|
||||
#endif
|
||||
|
@ -41,12 +43,17 @@ class CacheFileHandlesEntry;
|
|||
class CacheFileHandle : public nsISupports
|
||||
{
|
||||
public:
|
||||
enum class PinningStatus : uint32_t {
|
||||
UNKNOWN,
|
||||
NON_PINNED,
|
||||
PINNED
|
||||
};
|
||||
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
bool DispatchRelease();
|
||||
|
||||
CacheFileHandle(const SHA1Sum::Hash *aHash, bool aPriority);
|
||||
CacheFileHandle(const nsACString &aKey, bool aPriority);
|
||||
CacheFileHandle(const CacheFileHandle &aOther);
|
||||
CacheFileHandle(const SHA1Sum::Hash *aHash, bool aPriority, PinningStatus aPinning);
|
||||
CacheFileHandle(const nsACString &aKey, bool aPriority, PinningStatus aPinning);
|
||||
void Log();
|
||||
bool IsDoomed() const { return mIsDoomed; }
|
||||
const SHA1Sum::Hash *Hash() const { return mHash; }
|
||||
|
@ -58,6 +65,9 @@ public:
|
|||
bool IsSpecialFile() const { return mSpecialFile; }
|
||||
nsCString & Key() { return mKey; }
|
||||
|
||||
// Returns false when this handle has been doomed based on the pinning state update.
|
||||
bool SetPinned(bool aPinned);
|
||||
|
||||
// Memory reporting
|
||||
size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
|
||||
size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
|
||||
|
@ -79,6 +89,22 @@ private:
|
|||
// but it can be still deleted by OS/user
|
||||
// and then a subsequent OpenNSPRFileDesc()
|
||||
// will fail.
|
||||
|
||||
// For existing files this is always pre-set to UNKNOWN. The status is udpated accordingly
|
||||
// after the matadata has been parsed.
|
||||
// For new files the flag is set according to which storage kind is opening
|
||||
// the cache entry and remains so for the handle's lifetime.
|
||||
// The status can only change from UNKNOWN (if set so initially) to one of PINNED or NON_PINNED
|
||||
// and it stays unchanged afterwards.
|
||||
// This status is only accessed on the IO thread.
|
||||
PinningStatus mPinning;
|
||||
// Both initially false. Can be raised to true only when this handle is to be doomed
|
||||
// during the period when the pinning status is unknown. After the pinning status
|
||||
// determination we check these flags and possibly doom.
|
||||
// These flags are only accessed on the IO thread.
|
||||
bool mDoomWhenFoundPinned : 1;
|
||||
bool mDoomWhenFoundNonPinned : 1;
|
||||
|
||||
nsCOMPtr<nsIFile> mFile;
|
||||
int64_t mFileSize;
|
||||
PRFileDesc *mFD; // if null then the file doesn't exists on the disk
|
||||
|
@ -91,7 +117,8 @@ public:
|
|||
~CacheFileHandles();
|
||||
|
||||
nsresult GetHandle(const SHA1Sum::Hash *aHash, CacheFileHandle **_retval);
|
||||
nsresult NewHandle(const SHA1Sum::Hash *aHash, bool aPriority, CacheFileHandle **_retval);
|
||||
nsresult NewHandle(const SHA1Sum::Hash *aHash, bool aPriority,
|
||||
CacheFileHandle::PinningStatus aPinning, CacheFileHandle **_retval);
|
||||
void RemoveHandle(CacheFileHandle *aHandlle);
|
||||
void GetAllHandles(nsTArray<RefPtr<CacheFileHandle> > *_retval);
|
||||
void GetActiveHandles(nsTArray<RefPtr<CacheFileHandle> > *_retval);
|
||||
|
@ -212,11 +239,12 @@ public:
|
|||
NS_DECL_NSITIMERCALLBACK
|
||||
|
||||
enum {
|
||||
OPEN = 0U,
|
||||
CREATE = 1U,
|
||||
CREATE_NEW = 2U,
|
||||
PRIORITY = 4U,
|
||||
SPECIAL_FILE = 8U
|
||||
OPEN = 0U,
|
||||
CREATE = 1U,
|
||||
CREATE_NEW = 2U,
|
||||
PRIORITY = 4U,
|
||||
SPECIAL_FILE = 8U,
|
||||
PINNED = 16U
|
||||
};
|
||||
|
||||
CacheFileIOManager();
|
||||
|
@ -247,6 +275,19 @@ public:
|
|||
static nsresult Write(CacheFileHandle *aHandle, int64_t aOffset,
|
||||
const char *aBuf, int32_t aCount, bool aValidate,
|
||||
bool aTruncate, CacheFileIOListener *aCallback);
|
||||
// PinningDoomRestriction:
|
||||
// NO_RESTRICTION
|
||||
// no restriction is checked, the file is simply always doomed
|
||||
// DOOM_WHEN_(NON)_PINNED, we branch based on the pinning status of the handle:
|
||||
// UNKNOWN: the handle is marked to be doomed when later found (non)pinned
|
||||
// PINNED/NON_PINNED: doom only when the restriction matches the pin status
|
||||
// and the handle has not yet been required to doom during the UNKNOWN
|
||||
// period
|
||||
enum PinningDoomRestriction {
|
||||
NO_RESTRICTION,
|
||||
DOOM_WHEN_NON_PINNED,
|
||||
DOOM_WHEN_PINNED
|
||||
};
|
||||
static nsresult DoomFile(CacheFileHandle *aHandle,
|
||||
CacheFileIOListener *aCallback);
|
||||
static nsresult DoomFileByKey(const nsACString &aKey,
|
||||
|
@ -260,12 +301,14 @@ public:
|
|||
CacheFileIOListener *aCallback);
|
||||
static nsresult EvictIfOverLimit();
|
||||
static nsresult EvictAll();
|
||||
static nsresult EvictByContext(nsILoadContextInfo *aLoadContextInfo);
|
||||
static nsresult EvictByContext(nsILoadContextInfo *aLoadContextInfo,
|
||||
bool aPinning);
|
||||
|
||||
static nsresult InitIndexEntry(CacheFileHandle *aHandle,
|
||||
uint32_t aAppId,
|
||||
bool aAnonymous,
|
||||
bool aInBrowser);
|
||||
bool aInBrowser,
|
||||
bool aPinning);
|
||||
static nsresult UpdateIndexEntry(CacheFileHandle *aHandle,
|
||||
const uint32_t *aFrecency,
|
||||
const uint32_t *aExpirationTime);
|
||||
|
@ -329,7 +372,8 @@ private:
|
|||
nsresult WriteInternal(CacheFileHandle *aHandle, int64_t aOffset,
|
||||
const char *aBuf, int32_t aCount, bool aValidate,
|
||||
bool aTruncate);
|
||||
nsresult DoomFileInternal(CacheFileHandle *aHandle);
|
||||
nsresult DoomFileInternal(CacheFileHandle *aHandle,
|
||||
PinningDoomRestriction aPinningStatusRestriction = NO_RESTRICTION);
|
||||
nsresult DoomFileByKeyInternal(const SHA1Sum::Hash *aHash);
|
||||
nsresult ReleaseNSPRHandleInternal(CacheFileHandle *aHandle);
|
||||
nsresult TruncateSeekSetEOFInternal(CacheFileHandle *aHandle,
|
||||
|
@ -339,7 +383,8 @@ private:
|
|||
nsresult EvictIfOverLimitInternal();
|
||||
nsresult OverLimitEvictionInternal();
|
||||
nsresult EvictAllInternal();
|
||||
nsresult EvictByContextInternal(nsILoadContextInfo *aLoadContextInfo);
|
||||
nsresult EvictByContextInternal(nsILoadContextInfo *aLoadContextInfo,
|
||||
bool aPinning);
|
||||
|
||||
nsresult TrashDirectory(nsIFile *aFile);
|
||||
static void OnTrashTimer(nsITimer *aTimer, void *aClosure);
|
||||
|
@ -383,7 +428,7 @@ private:
|
|||
static CacheFileIOManager *gInstance;
|
||||
TimeStamp mStartTime;
|
||||
bool mShuttingDown;
|
||||
RefPtr<CacheIOThread> mIOThread;
|
||||
RefPtr<CacheIOThread> mIOThread;
|
||||
nsCOMPtr<nsIFile> mCacheDirectory;
|
||||
#if defined(MOZ_WIDGET_ANDROID)
|
||||
// On Android we add the active profile directory name between the path
|
||||
|
@ -396,7 +441,7 @@ private:
|
|||
CacheFileHandles mHandles;
|
||||
nsTArray<CacheFileHandle *> mHandlesByLastUsed;
|
||||
nsTArray<CacheFileHandle *> mSpecialHandles;
|
||||
nsTArray<RefPtr<CacheFile> > mScheduledMetadataWrites;
|
||||
nsTArray<RefPtr<CacheFile> > mScheduledMetadataWrites;
|
||||
nsCOMPtr<nsITimer> mMetadataWritesTimer;
|
||||
bool mOverLimitEvicting;
|
||||
bool mRemovingTrashDirs;
|
||||
|
@ -404,7 +449,7 @@ private:
|
|||
nsCOMPtr<nsIFile> mTrashDir;
|
||||
nsCOMPtr<nsIDirectoryEnumerator> mTrashDirEnumerator;
|
||||
nsTArray<nsCString> mFailedTrashDirs;
|
||||
RefPtr<CacheFileContextEvictor> mContextEvictor;
|
||||
RefPtr<CacheFileContextEvictor> mContextEvictor;
|
||||
TimeStamp mLastSmartSizeTime;
|
||||
};
|
||||
|
||||
|
|
|
@ -53,7 +53,7 @@ private:
|
|||
void NotifyListener();
|
||||
void MaybeNotifyListener();
|
||||
|
||||
RefPtr<CacheFile> mFile;
|
||||
RefPtr<CacheFile> mFile;
|
||||
RefPtr<CacheFileChunk> mChunk;
|
||||
int64_t mPos;
|
||||
bool mClosed;
|
||||
|
|
|
@ -35,8 +35,6 @@ namespace net {
|
|||
// Max size of elements in bytes.
|
||||
#define kMaxElementsSize 64*1024
|
||||
|
||||
#define kCacheEntryVersion 1
|
||||
|
||||
#define NOW_SECONDS() (uint32_t(PR_Now() / PR_USEC_PER_SEC))
|
||||
|
||||
NS_IMPL_ISUPPORTS(CacheFileMetadata, CacheFileIOListener)
|
||||
|
@ -71,7 +69,7 @@ CacheFileMetadata::CacheFileMetadata(CacheFileHandle *aHandle, const nsACString
|
|||
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
||||
}
|
||||
|
||||
CacheFileMetadata::CacheFileMetadata(bool aMemoryOnly, const nsACString &aKey)
|
||||
CacheFileMetadata::CacheFileMetadata(bool aMemoryOnly, bool aPinned, const nsACString &aKey)
|
||||
: CacheMemoryConsumer(aMemoryOnly ? MEMORY_ONLY : NORMAL)
|
||||
, mHandle(nullptr)
|
||||
, mHashArray(nullptr)
|
||||
|
@ -93,6 +91,9 @@ CacheFileMetadata::CacheFileMetadata(bool aMemoryOnly, const nsACString &aKey)
|
|||
MOZ_COUNT_CTOR(CacheFileMetadata);
|
||||
memset(&mMetaHdr, 0, sizeof(CacheFileMetadataHeader));
|
||||
mMetaHdr.mVersion = kCacheEntryVersion;
|
||||
if (aPinned) {
|
||||
AddFlags(kCacheEntryIsPinned);
|
||||
}
|
||||
mMetaHdr.mExpirationTime = nsICacheEntry::NO_EXPIRATION_TIME;
|
||||
mKey = aKey;
|
||||
mMetaHdr.mKeySize = mKey.Length();
|
||||
|
@ -534,6 +535,29 @@ CacheFileMetadata::SetHash(uint32_t aIndex, CacheHash::Hash16_t aHash)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
CacheFileMetadata::AddFlags(uint32_t aFlags)
|
||||
{
|
||||
MarkDirty(false);
|
||||
mMetaHdr.mFlags |= aFlags;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
CacheFileMetadata::RemoveFlags(uint32_t aFlags)
|
||||
{
|
||||
MarkDirty(false);
|
||||
mMetaHdr.mFlags &= ~aFlags;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
CacheFileMetadata::GetFlags(uint32_t *_retval)
|
||||
{
|
||||
*_retval = mMetaHdr.mFlags;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
CacheFileMetadata::SetExpirationTime(uint32_t aExpirationTime)
|
||||
{
|
||||
|
@ -814,11 +838,16 @@ CacheFileMetadata::InitEmptyMetadata()
|
|||
mMetaHdr.mExpirationTime = nsICacheEntry::NO_EXPIRATION_TIME;
|
||||
mMetaHdr.mKeySize = mKey.Length();
|
||||
|
||||
// Deliberately not touching the "kCacheEntryIsPinned" flag.
|
||||
|
||||
DoMemoryReport(MemoryUsage());
|
||||
|
||||
// We're creating a new entry. If there is any old data truncate it.
|
||||
if (mHandle && mHandle->FileExists() && mHandle->FileSize()) {
|
||||
CacheFileIOManager::TruncateSeekSetEOF(mHandle, 0, 0, nullptr);
|
||||
if (mHandle) {
|
||||
mHandle->SetPinned(Pinned());
|
||||
if (mHandle->FileExists() && mHandle->FileSize()) {
|
||||
CacheFileIOManager::TruncateSeekSetEOF(mHandle, 0, 0, nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -853,12 +882,19 @@ CacheFileMetadata::ParseMetadata(uint32_t aMetaOffset, uint32_t aBufOffset,
|
|||
|
||||
mMetaHdr.ReadFromBuf(mBuf + hdrOffset);
|
||||
|
||||
if (mMetaHdr.mVersion != kCacheEntryVersion) {
|
||||
if (mMetaHdr.mVersion == 1) {
|
||||
// Backward compatibility before we've added flags to the header
|
||||
keyOffset -= sizeof(uint32_t);
|
||||
} else if (mMetaHdr.mVersion != kCacheEntryVersion) {
|
||||
LOG(("CacheFileMetadata::ParseMetadata() - Not a version we understand to. "
|
||||
"[version=0x%x, this=%p]", mMetaHdr.mVersion, this));
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
// Update the version stored in the header to make writes
|
||||
// store the header in the current version form.
|
||||
mMetaHdr.mVersion = kCacheEntryVersion;
|
||||
|
||||
uint32_t elementsOffset = mMetaHdr.mKeySize + keyOffset + 1;
|
||||
|
||||
if (elementsOffset > metaposOffset) {
|
||||
|
@ -917,6 +953,14 @@ CacheFileMetadata::ParseMetadata(uint32_t aMetaOffset, uint32_t aBufOffset,
|
|||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
if (mHandle) {
|
||||
if (!mHandle->SetPinned(Pinned())) {
|
||||
LOG(("CacheFileMetadata::ParseMetadata() - handle was doomed for this "
|
||||
"pinning state, truncate the file [this=%p, pinned=%d]", this, Pinned()));
|
||||
return NS_ERROR_FILE_CORRUPTED;
|
||||
}
|
||||
}
|
||||
|
||||
mHashArraySize = hashesLen;
|
||||
mHashCount = hashCount;
|
||||
if (mHashArraySize) {
|
||||
|
|
|
@ -19,6 +19,12 @@ class nsICacheEntryMetaDataVisitor;
|
|||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
// Flags stored in CacheFileMetadataHeader.mFlags
|
||||
|
||||
// Whether an entry is a pinned entry (created with
|
||||
// nsICacheStorageService.pinningCacheStorage.)
|
||||
static const uint32_t kCacheEntryIsPinned = 1 << 0;
|
||||
|
||||
// By multiplying with the current half-life we convert the frecency
|
||||
// to time independent of half-life value. The range fits 32bits.
|
||||
// When decay time changes on next run of the browser, we convert
|
||||
|
@ -30,6 +36,9 @@ namespace net {
|
|||
((double)(aInt) / (double)CacheObserver::HalfLifeSeconds())
|
||||
|
||||
|
||||
#define kCacheEntryVersion 2
|
||||
|
||||
|
||||
#pragma pack(push)
|
||||
#pragma pack(1)
|
||||
|
||||
|
@ -42,19 +51,22 @@ public:
|
|||
uint32_t mFrecency;
|
||||
uint32_t mExpirationTime;
|
||||
uint32_t mKeySize;
|
||||
uint32_t mFlags;
|
||||
|
||||
void WriteToBuf(void *aBuf)
|
||||
{
|
||||
EnsureCorrectClassSize();
|
||||
|
||||
uint8_t* ptr = static_cast<uint8_t*>(aBuf);
|
||||
MOZ_ASSERT(mVersion == kCacheEntryVersion);
|
||||
NetworkEndian::writeUint32(ptr, mVersion); ptr += sizeof(uint32_t);
|
||||
NetworkEndian::writeUint32(ptr, mFetchCount); ptr += sizeof(uint32_t);
|
||||
NetworkEndian::writeUint32(ptr, mLastFetched); ptr += sizeof(uint32_t);
|
||||
NetworkEndian::writeUint32(ptr, mLastModified); ptr += sizeof(uint32_t);
|
||||
NetworkEndian::writeUint32(ptr, mFrecency); ptr += sizeof(uint32_t);
|
||||
NetworkEndian::writeUint32(ptr, mExpirationTime); ptr += sizeof(uint32_t);
|
||||
NetworkEndian::writeUint32(ptr, mKeySize);
|
||||
NetworkEndian::writeUint32(ptr, mKeySize); ptr += sizeof(uint32_t);
|
||||
NetworkEndian::writeUint32(ptr, mFlags);
|
||||
}
|
||||
|
||||
void ReadFromBuf(const void *aBuf)
|
||||
|
@ -68,14 +80,19 @@ public:
|
|||
mLastModified = BigEndian::readUint32(ptr); ptr += sizeof(uint32_t);
|
||||
mFrecency = BigEndian::readUint32(ptr); ptr += sizeof(uint32_t);
|
||||
mExpirationTime = BigEndian::readUint32(ptr); ptr += sizeof(uint32_t);
|
||||
mKeySize = BigEndian::readUint32(ptr);
|
||||
mKeySize = BigEndian::readUint32(ptr); ptr += sizeof(uint32_t);
|
||||
if (mVersion >= kCacheEntryVersion) {
|
||||
mFlags = BigEndian::readUint32(ptr);
|
||||
} else {
|
||||
mFlags = 0;
|
||||
}
|
||||
}
|
||||
|
||||
inline void EnsureCorrectClassSize()
|
||||
{
|
||||
static_assert((sizeof(mVersion) + sizeof(mFetchCount) +
|
||||
sizeof(mLastFetched) + sizeof(mLastModified) + sizeof(mFrecency) +
|
||||
sizeof(mExpirationTime) + sizeof(mKeySize)) ==
|
||||
sizeof(mExpirationTime) + sizeof(mKeySize)) + sizeof(mFlags) ==
|
||||
sizeof(CacheFileMetadataHeader),
|
||||
"Unexpected sizeof(CacheFileMetadataHeader)!");
|
||||
}
|
||||
|
@ -114,6 +131,7 @@ public:
|
|||
CacheFileMetadata(CacheFileHandle *aHandle,
|
||||
const nsACString &aKey);
|
||||
CacheFileMetadata(bool aMemoryOnly,
|
||||
bool aPinned,
|
||||
const nsACString &aKey);
|
||||
CacheFileMetadata();
|
||||
|
||||
|
@ -127,8 +145,9 @@ public:
|
|||
CacheFileMetadataListener *aListener);
|
||||
nsresult SyncReadMetadata(nsIFile *aFile);
|
||||
|
||||
bool IsAnonymous() { return mAnonymous; }
|
||||
bool IsAnonymous() const { return mAnonymous; }
|
||||
mozilla::OriginAttributes const & OriginAttributes() const { return mOriginAttributes; }
|
||||
bool Pinned() const { return !!(mMetaHdr.mFlags & kCacheEntryIsPinned); }
|
||||
|
||||
const char * GetElement(const char *aKey);
|
||||
nsresult SetElement(const char *aKey, const char *aValue);
|
||||
|
@ -137,6 +156,9 @@ public:
|
|||
CacheHash::Hash16_t GetHash(uint32_t aIndex);
|
||||
nsresult SetHash(uint32_t aIndex, CacheHash::Hash16_t aHash);
|
||||
|
||||
nsresult AddFlags(uint32_t aFlags);
|
||||
nsresult RemoveFlags(uint32_t aFlags);
|
||||
nsresult GetFlags(uint32_t *_retval);
|
||||
nsresult SetExpirationTime(uint32_t aExpirationTime);
|
||||
nsresult GetExpirationTime(uint32_t *_retval);
|
||||
nsresult SetFrecency(uint32_t aFrecency);
|
||||
|
@ -175,7 +197,7 @@ private:
|
|||
nsresult EnsureBuffer(uint32_t aSize);
|
||||
nsresult ParseKey(const nsACString &aKey);
|
||||
|
||||
RefPtr<CacheFileHandle> mHandle;
|
||||
RefPtr<CacheFileHandle> mHandle;
|
||||
nsCString mKey;
|
||||
CacheHash::Hash16_t *mHashArray;
|
||||
uint32_t mHashArraySize;
|
||||
|
|
|
@ -50,7 +50,7 @@ private:
|
|||
void FillHole();
|
||||
void NotifyListener();
|
||||
|
||||
RefPtr<CacheFile> mFile;
|
||||
RefPtr<CacheFile> mFile;
|
||||
RefPtr<CacheFileChunk> mChunk;
|
||||
RefPtr<CacheOutputCloseListener> mCloseListener;
|
||||
int64_t mPos;
|
||||
|
|
|
@ -705,11 +705,12 @@ nsresult
|
|||
CacheIndex::InitEntry(const SHA1Sum::Hash *aHash,
|
||||
uint32_t aAppId,
|
||||
bool aAnonymous,
|
||||
bool aInBrowser)
|
||||
bool aInBrowser,
|
||||
bool aPinned)
|
||||
{
|
||||
LOG(("CacheIndex::InitEntry() [hash=%08x%08x%08x%08x%08x, appId=%u, "
|
||||
"anonymous=%d, inBrowser=%d]", LOGSHA1(aHash), aAppId, aAnonymous,
|
||||
aInBrowser));
|
||||
"anonymous=%d, inBrowser=%d, pinned=%d]", LOGSHA1(aHash), aAppId,
|
||||
aAnonymous, aInBrowser, aPinned));
|
||||
|
||||
RefPtr<CacheIndex> index = gInstance;
|
||||
|
||||
|
@ -799,10 +800,10 @@ CacheIndex::InitEntry(const SHA1Sum::Hash *aHash,
|
|||
}
|
||||
|
||||
if (updated) {
|
||||
updated->Init(aAppId, aAnonymous, aInBrowser);
|
||||
updated->Init(aAppId, aAnonymous, aInBrowser, aPinned);
|
||||
updated->MarkDirty();
|
||||
} else {
|
||||
entry->Init(aAppId, aAnonymous, aInBrowser);
|
||||
entry->Init(aAppId, aAnonymous, aInBrowser, aPinned);
|
||||
entry->MarkDirty();
|
||||
}
|
||||
}
|
||||
|
@ -1108,10 +1109,22 @@ CacheIndex::RemoveAll()
|
|||
|
||||
// static
|
||||
nsresult
|
||||
CacheIndex::HasEntry(const nsACString &aKey, EntryStatus *_retval)
|
||||
CacheIndex::HasEntry(const nsACString &aKey, EntryStatus *_retval, bool *_pinned)
|
||||
{
|
||||
LOG(("CacheIndex::HasEntry() [key=%s]", PromiseFlatCString(aKey).get()));
|
||||
|
||||
SHA1Sum sum;
|
||||
SHA1Sum::Hash hash;
|
||||
sum.update(aKey.BeginReading(), aKey.Length());
|
||||
sum.finish(hash);
|
||||
|
||||
return HasEntry(hash, _retval, _pinned);
|
||||
}
|
||||
|
||||
// static
|
||||
nsresult
|
||||
CacheIndex::HasEntry(const SHA1Sum::Hash &hash, EntryStatus *_retval, bool *_pinned)
|
||||
{
|
||||
RefPtr<CacheIndex> index = gInstance;
|
||||
|
||||
if (!index) {
|
||||
|
@ -1124,10 +1137,9 @@ CacheIndex::HasEntry(const nsACString &aKey, EntryStatus *_retval)
|
|||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
SHA1Sum sum;
|
||||
SHA1Sum::Hash hash;
|
||||
sum.update(aKey.BeginReading(), aKey.Length());
|
||||
sum.finish(hash);
|
||||
if (_pinned) {
|
||||
*_pinned = false;
|
||||
}
|
||||
|
||||
const CacheIndexEntry *entry = nullptr;
|
||||
|
||||
|
@ -1163,6 +1175,9 @@ CacheIndex::HasEntry(const nsACString &aKey, EntryStatus *_retval)
|
|||
}
|
||||
} else {
|
||||
*_retval = EXISTS;
|
||||
if (_pinned && entry->IsPinned()) {
|
||||
*_pinned = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1193,7 +1208,7 @@ CacheIndex::GetEntryForEviction(bool aIgnoreEmptyEntries, SHA1Sum::Hash *aHash,
|
|||
bool foundEntry = false;
|
||||
uint32_t i;
|
||||
|
||||
// find first non-forced valid entry with the lowest frecency
|
||||
// find first non-forced valid and unpinned entry with the lowest frecency
|
||||
index->mFrecencyArray.Sort(FrecencyComparator());
|
||||
for (i = 0; i < index->mFrecencyArray.Length(); ++i) {
|
||||
memcpy(&hash, &index->mFrecencyArray[i]->mHash, sizeof(SHA1Sum::Hash));
|
||||
|
@ -1202,6 +1217,10 @@ CacheIndex::GetEntryForEviction(bool aIgnoreEmptyEntries, SHA1Sum::Hash *aHash,
|
|||
continue;
|
||||
}
|
||||
|
||||
if (CacheIndexEntry::IsPinned(index->mFrecencyArray[i])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (aIgnoreEmptyEntries &&
|
||||
!CacheIndexEntry::GetFileSize(index->mFrecencyArray[i])) {
|
||||
continue;
|
||||
|
@ -2575,7 +2594,8 @@ CacheIndex::InitEntryFromDiskData(CacheIndexEntry *aEntry,
|
|||
// Bug 1201042 - will pass OriginAttributes directly.
|
||||
aEntry->Init(aMetaData->OriginAttributes().mAppId,
|
||||
aMetaData->IsAnonymous(),
|
||||
aMetaData->OriginAttributes().mInBrowser);
|
||||
aMetaData->OriginAttributes().mInBrowser,
|
||||
aMetaData->Pinned());
|
||||
|
||||
uint32_t expirationTime;
|
||||
aMetaData->GetExpirationTime(&expirationTime);
|
||||
|
|
|
@ -148,7 +148,7 @@ public:
|
|||
mRec->mFlags = 0;
|
||||
}
|
||||
|
||||
void Init(uint32_t aAppId, bool aAnonymous, bool aInBrowser)
|
||||
void Init(uint32_t aAppId, bool aAnonymous, bool aInBrowser, bool aPinned)
|
||||
{
|
||||
MOZ_ASSERT(mRec->mFrecency == 0);
|
||||
MOZ_ASSERT(mRec->mExpirationTime == nsICacheEntry::NO_EXPIRATION_TIME);
|
||||
|
@ -164,6 +164,9 @@ public:
|
|||
if (aInBrowser) {
|
||||
mRec->mFlags |= kInBrowserMask;
|
||||
}
|
||||
if (aPinned) {
|
||||
mRec->mFlags |= kPinnedMask;
|
||||
}
|
||||
}
|
||||
|
||||
const SHA1Sum::Hash * Hash() const { return &mRec->mHash; }
|
||||
|
@ -184,6 +187,8 @@ public:
|
|||
bool IsFresh() const { return !!(mRec->mFlags & kFreshMask); }
|
||||
void MarkFresh() { mRec->mFlags |= kFreshMask; }
|
||||
|
||||
bool IsPinned() const { return !!(mRec->mFlags & kPinnedMask); }
|
||||
|
||||
void SetFrecency(uint32_t aFrecency) { mRec->mFrecency = aFrecency; }
|
||||
uint32_t GetFrecency() const { return mRec->mFrecency; }
|
||||
|
||||
|
@ -210,6 +215,10 @@ public:
|
|||
{
|
||||
return aRec->mFlags & kFileSizeMask;
|
||||
}
|
||||
static uint32_t IsPinned(CacheIndexRecord *aRec)
|
||||
{
|
||||
return aRec->mFlags & kPinnedMask;
|
||||
}
|
||||
bool IsFileEmpty() const { return GetFileSize() == 0; }
|
||||
|
||||
void WriteToBuf(void *aBuf)
|
||||
|
@ -301,7 +310,10 @@ private:
|
|||
// this entry during update or build process.
|
||||
static const uint32_t kFreshMask = 0x04000000;
|
||||
|
||||
static const uint32_t kReservedMask = 0x03000000;
|
||||
// Indicates a pinned entry.
|
||||
static const uint32_t kPinnedMask = 0x02000000;
|
||||
|
||||
static const uint32_t kReservedMask = 0x01000000;
|
||||
|
||||
// FileSize in kilobytes
|
||||
static const uint32_t kFileSizeMask = 0x00FFFFFF;
|
||||
|
@ -610,7 +622,8 @@ public:
|
|||
static nsresult InitEntry(const SHA1Sum::Hash *aHash,
|
||||
uint32_t aAppId,
|
||||
bool aAnonymous,
|
||||
bool aInBrowser);
|
||||
bool aInBrowser,
|
||||
bool aPinned);
|
||||
|
||||
// Remove entry from index. The entry should be present in index.
|
||||
static nsresult RemoveEntry(const SHA1Sum::Hash *aHash);
|
||||
|
@ -635,12 +648,16 @@ public:
|
|||
|
||||
// Returns status of the entry in index for the given key. It can be called
|
||||
// on any thread.
|
||||
static nsresult HasEntry(const nsACString &aKey, EntryStatus *_retval);
|
||||
// If _pinned is non-null, it's filled with pinning status of the entry.
|
||||
static nsresult HasEntry(const nsACString &aKey, EntryStatus *_retval,
|
||||
bool *_pinned = nullptr);
|
||||
static nsresult HasEntry(const SHA1Sum::Hash &hash, EntryStatus *_retval,
|
||||
bool *_pinned = nullptr);
|
||||
|
||||
// Returns a hash of the least important entry that should be evicted if the
|
||||
// cache size is over limit and also returns a total number of all entries in
|
||||
// the index minus the number of forced valid entries that we encounter
|
||||
// when searching (see below)
|
||||
// the index minus the number of forced valid entries and unpinned entries
|
||||
// that we encounter when searching (see below)
|
||||
static nsresult GetEntryForEviction(bool aIgnoreEmptyEntries, SHA1Sum::Hash *aHash, uint32_t *aCnt);
|
||||
|
||||
// Checks if a cache entry is currently forced valid. Used to prevent an entry
|
||||
|
@ -969,7 +986,7 @@ private:
|
|||
char *mRWBuf;
|
||||
uint32_t mRWBufSize;
|
||||
uint32_t mRWBufPos;
|
||||
RefPtr<CacheHash> mRWHash;
|
||||
RefPtr<CacheHash> mRWHash;
|
||||
|
||||
// Reading of journal succeeded if true.
|
||||
bool mJournalReadSuccessfully;
|
||||
|
@ -981,9 +998,9 @@ private:
|
|||
// Used to check the existence of the file during reading process.
|
||||
RefPtr<CacheFileHandle> mTmpHandle;
|
||||
|
||||
RefPtr<FileOpenHelper> mIndexFileOpener;
|
||||
RefPtr<FileOpenHelper> mJournalFileOpener;
|
||||
RefPtr<FileOpenHelper> mTmpFileOpener;
|
||||
RefPtr<FileOpenHelper> mIndexFileOpener;
|
||||
RefPtr<FileOpenHelper> mJournalFileOpener;
|
||||
RefPtr<FileOpenHelper> mTmpFileOpener;
|
||||
|
||||
// Directory enumerator used when building and updating index.
|
||||
nsCOMPtr<nsIDirectoryEnumerator> mDirEnumerator;
|
||||
|
|
|
@ -49,7 +49,7 @@ protected:
|
|||
CacheIndexRecord *aNewRecord);
|
||||
|
||||
nsresult mStatus;
|
||||
RefPtr<CacheIndex> mIndex;
|
||||
RefPtr<CacheIndex> mIndex;
|
||||
nsTArray<CacheIndexRecord *> mRecords;
|
||||
bool mAddNew;
|
||||
};
|
||||
|
|
|
@ -26,11 +26,13 @@ NS_IMPL_ISUPPORTS(CacheStorage, nsICacheStorage)
|
|||
CacheStorage::CacheStorage(nsILoadContextInfo* aInfo,
|
||||
bool aAllowDisk,
|
||||
bool aLookupAppCache,
|
||||
bool aSkipSizeCheck)
|
||||
bool aSkipSizeCheck,
|
||||
bool aPinning)
|
||||
: mLoadContextInfo(GetLoadContextInfo(aInfo))
|
||||
, mWriteToDisk(aAllowDisk)
|
||||
, mLookupAppCache(aLookupAppCache)
|
||||
, mSkipSizeCheck(aSkipSizeCheck)
|
||||
, mPinning(aPinning)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -53,7 +53,8 @@ public:
|
|||
CacheStorage(nsILoadContextInfo* aInfo,
|
||||
bool aAllowDisk,
|
||||
bool aLookupAppCache,
|
||||
bool aSkipSizeCheck);
|
||||
bool aSkipSizeCheck,
|
||||
bool aPinning);
|
||||
|
||||
protected:
|
||||
virtual ~CacheStorage();
|
||||
|
@ -64,12 +65,14 @@ protected:
|
|||
bool mWriteToDisk : 1;
|
||||
bool mLookupAppCache : 1;
|
||||
bool mSkipSizeCheck: 1;
|
||||
bool mPinning : 1;
|
||||
|
||||
public:
|
||||
nsILoadContextInfo* LoadInfo() const { return mLoadContextInfo; }
|
||||
bool WriteToDisk() const { return mWriteToDisk && !mLoadContextInfo->IsPrivate(); }
|
||||
bool LookupAppCache() const { return mLookupAppCache; }
|
||||
bool SkipSizeCheck() const { return mSkipSizeCheck; }
|
||||
bool Pinning() const { return mPinning; }
|
||||
};
|
||||
|
||||
} // namespace net
|
||||
|
|
|
@ -103,7 +103,8 @@ CacheStorageService::MemoryPool::Limit() const
|
|||
NS_IMPL_ISUPPORTS(CacheStorageService,
|
||||
nsICacheStorageService,
|
||||
nsIMemoryReporter,
|
||||
nsITimerCallback)
|
||||
nsITimerCallback,
|
||||
nsICacheTesting)
|
||||
|
||||
CacheStorageService* CacheStorageService::sSelf = nullptr;
|
||||
|
||||
|
@ -536,7 +537,7 @@ void CacheStorageService::DropPrivateBrowsingEntries()
|
|||
sGlobalEntryTables->EnumerateRead(&CollectPrivateContexts, &keys);
|
||||
|
||||
for (uint32_t i = 0; i < keys.Length(); ++i)
|
||||
DoomStorageEntries(keys[i], nullptr, true, nullptr);
|
||||
DoomStorageEntries(keys[i], nullptr, true, false, nullptr);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
@ -681,7 +682,7 @@ NS_IMETHODIMP CacheStorageService::MemoryCacheStorage(nsILoadContextInfo *aLoadC
|
|||
|
||||
nsCOMPtr<nsICacheStorage> storage;
|
||||
if (CacheObserver::UseNewCache()) {
|
||||
storage = new CacheStorage(aLoadContextInfo, false, false, false);
|
||||
storage = new CacheStorage(aLoadContextInfo, false, false, false, false);
|
||||
}
|
||||
else {
|
||||
storage = new _OldStorage(aLoadContextInfo, false, false, false, nullptr);
|
||||
|
@ -706,7 +707,7 @@ NS_IMETHODIMP CacheStorageService::DiskCacheStorage(nsILoadContextInfo *aLoadCon
|
|||
|
||||
nsCOMPtr<nsICacheStorage> storage;
|
||||
if (CacheObserver::UseNewCache()) {
|
||||
storage = new CacheStorage(aLoadContextInfo, useDisk, aLookupAppCache, false);
|
||||
storage = new CacheStorage(aLoadContextInfo, useDisk, aLookupAppCache, false /* size limit */, false /* don't pin */);
|
||||
}
|
||||
else {
|
||||
storage = new _OldStorage(aLoadContextInfo, useDisk, aLookupAppCache, false, nullptr);
|
||||
|
@ -716,6 +717,27 @@ NS_IMETHODIMP CacheStorageService::DiskCacheStorage(nsILoadContextInfo *aLoadCon
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP CacheStorageService::PinningCacheStorage(nsILoadContextInfo *aLoadContextInfo,
|
||||
nsICacheStorage * *_retval)
|
||||
{
|
||||
NS_ENSURE_ARG(aLoadContextInfo);
|
||||
NS_ENSURE_ARG(_retval);
|
||||
|
||||
if (!CacheObserver::UseNewCache()) {
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
// When disk cache is disabled don't pretend we cache.
|
||||
if (!CacheObserver::UseDiskCache()) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsICacheStorage> storage = new CacheStorage(
|
||||
aLoadContextInfo, true /* use disk */, false /* no appcache */, true /* ignore size checks */, true /* pin */);
|
||||
storage.forget(_retval);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP CacheStorageService::AppCacheStorage(nsILoadContextInfo *aLoadContextInfo,
|
||||
nsIApplicationCache *aApplicationCache,
|
||||
nsICacheStorage * *_retval)
|
||||
|
@ -745,7 +767,7 @@ NS_IMETHODIMP CacheStorageService::SynthesizedCacheStorage(nsILoadContextInfo *a
|
|||
|
||||
nsCOMPtr<nsICacheStorage> storage;
|
||||
if (CacheObserver::UseNewCache()) {
|
||||
storage = new CacheStorage(aLoadContextInfo, false, false, true /* skip size checks for synthesized cache */);
|
||||
storage = new CacheStorage(aLoadContextInfo, false, false, true /* skip size checks for synthesized cache */, false /* no pinning */);
|
||||
}
|
||||
else {
|
||||
storage = new _OldStorage(aLoadContextInfo, false, false, false, nullptr);
|
||||
|
@ -768,12 +790,15 @@ NS_IMETHODIMP CacheStorageService::Clear()
|
|||
nsTArray<nsCString> keys;
|
||||
sGlobalEntryTables->EnumerateRead(&CollectContexts, &keys);
|
||||
|
||||
for (uint32_t i = 0; i < keys.Length(); ++i)
|
||||
DoomStorageEntries(keys[i], nullptr, true, nullptr);
|
||||
}
|
||||
for (uint32_t i = 0; i < keys.Length(); ++i) {
|
||||
DoomStorageEntries(keys[i], nullptr, true, false, nullptr);
|
||||
}
|
||||
|
||||
rv = CacheFileIOManager::EvictAll();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
// Passing null as a load info means to evict all contexts.
|
||||
// EvictByContext() respects the entry pinning. EvictAll() does not.
|
||||
rv = CacheFileIOManager::EvictByContext(nullptr, false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
} else {
|
||||
nsCOMPtr<nsICacheService> serv =
|
||||
do_GetService(NS_CACHESERVICE_CONTRACTID, &rv);
|
||||
|
@ -813,6 +838,29 @@ NS_IMETHODIMP CacheStorageService::PurgeFromMemory(uint32_t aWhat)
|
|||
return Dispatch(event);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP CacheStorageService::PurgeFromMemoryRunnable::Run()
|
||||
{
|
||||
if (NS_IsMainThread()) {
|
||||
nsCOMPtr<nsIObserverService> observerService =
|
||||
mozilla::services::GetObserverService();
|
||||
if (observerService) {
|
||||
observerService->NotifyObservers(nullptr, "cacheservice:purge-memory-pools", nullptr);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (mService) {
|
||||
// TODO not all flags apply to both pools
|
||||
mService->Pool(true).PurgeAll(mWhat);
|
||||
mService->Pool(false).PurgeAll(mWhat);
|
||||
mService = nullptr;
|
||||
}
|
||||
|
||||
NS_DispatchToMainThread(this);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP CacheStorageService::AsyncGetDiskConsumption(
|
||||
nsICacheStorageConsumptionObserver* aObserver)
|
||||
{
|
||||
|
@ -924,7 +972,7 @@ CacheStorageService::UnregisterEntry(CacheEntry* aEntry)
|
|||
|
||||
static bool
|
||||
AddExactEntry(CacheEntryTable* aEntries,
|
||||
nsCString const& aKey,
|
||||
nsACString const& aKey,
|
||||
CacheEntry* aEntry,
|
||||
bool aOverwrite)
|
||||
{
|
||||
|
@ -942,7 +990,7 @@ AddExactEntry(CacheEntryTable* aEntries,
|
|||
|
||||
static bool
|
||||
RemoveExactEntry(CacheEntryTable* aEntries,
|
||||
nsCString const& aKey,
|
||||
nsACString const& aKey,
|
||||
CacheEntry* aEntry,
|
||||
bool aOverwrite)
|
||||
{
|
||||
|
@ -1357,7 +1405,9 @@ CacheStorageService::AddStorageEntry(CacheStorage const* aStorage,
|
|||
CacheFileUtils::AppendKeyPrefix(aStorage->LoadInfo(), contextKey);
|
||||
|
||||
return AddStorageEntry(contextKey, aURI, aIdExtension,
|
||||
aStorage->WriteToDisk(), aStorage->SkipSizeCheck(),
|
||||
aStorage->WriteToDisk(),
|
||||
aStorage->SkipSizeCheck(),
|
||||
aStorage->Pinning(),
|
||||
aCreateIfNotExist, aReplace,
|
||||
aResult);
|
||||
}
|
||||
|
@ -1368,6 +1418,7 @@ CacheStorageService::AddStorageEntry(nsCSubstring const& aContextKey,
|
|||
const nsACString & aIdExtension,
|
||||
bool aWriteToDisk,
|
||||
bool aSkipSizeCheck,
|
||||
bool aPin,
|
||||
bool aCreateIfNotExist,
|
||||
bool aReplace,
|
||||
CacheEntryHandle** aResult)
|
||||
|
@ -1402,12 +1453,8 @@ CacheStorageService::AddStorageEntry(nsCSubstring const& aContextKey,
|
|||
bool entryExists = entries->Get(entryKey, getter_AddRefs(entry));
|
||||
|
||||
if (entryExists && !aReplace) {
|
||||
// check whether the file is already doomed or we want to turn this entry
|
||||
// to a memory-only.
|
||||
if (MOZ_UNLIKELY(entry->IsFileDoomed())) {
|
||||
LOG((" file already doomed, replacing the entry"));
|
||||
aReplace = true;
|
||||
} else if (MOZ_UNLIKELY(!aWriteToDisk) && MOZ_LIKELY(entry->IsUsingDisk())) {
|
||||
// check whether we want to turn this entry to a memory-only.
|
||||
if (MOZ_UNLIKELY(!aWriteToDisk) && MOZ_LIKELY(entry->IsUsingDisk())) {
|
||||
LOG((" entry is persistnet but we want mem-only, replacing it"));
|
||||
aReplace = true;
|
||||
}
|
||||
|
@ -1430,7 +1477,7 @@ CacheStorageService::AddStorageEntry(nsCSubstring const& aContextKey,
|
|||
// Ensure entry for the particular URL, if not read/only
|
||||
if (!entryExists && (aCreateIfNotExist || aReplace)) {
|
||||
// Entry is not in the hashtable or has just been truncated...
|
||||
entry = new CacheEntry(aContextKey, aURI, aIdExtension, aWriteToDisk, aSkipSizeCheck);
|
||||
entry = new CacheEntry(aContextKey, aURI, aIdExtension, aWriteToDisk, aSkipSizeCheck, aPin);
|
||||
entries->Put(entryKey, entry);
|
||||
LOG((" new entry %p for %s", entry.get(), entryKey.get()));
|
||||
}
|
||||
|
@ -1668,15 +1715,19 @@ CacheStorageService::DoomStorageEntries(CacheStorage const* aStorage,
|
|||
mozilla::MutexAutoLock lock(mLock);
|
||||
|
||||
return DoomStorageEntries(contextKey, aStorage->LoadInfo(),
|
||||
aStorage->WriteToDisk(), aCallback);
|
||||
aStorage->WriteToDisk(), aStorage->Pinning(),
|
||||
aCallback);
|
||||
}
|
||||
|
||||
nsresult
|
||||
CacheStorageService::DoomStorageEntries(nsCSubstring const& aContextKey,
|
||||
nsILoadContextInfo* aContext,
|
||||
bool aDiskStorage,
|
||||
bool aPinned,
|
||||
nsICacheEntryDoomCallback* aCallback)
|
||||
{
|
||||
LOG(("CacheStorageService::DoomStorageEntries [context=%s]", aContextKey.BeginReading()));
|
||||
|
||||
mLock.AssertCurrentThreadOwns();
|
||||
|
||||
NS_ENSURE_TRUE(!mShutdown, NS_ERROR_NOT_INITIALIZED);
|
||||
|
@ -1687,30 +1738,31 @@ CacheStorageService::DoomStorageEntries(nsCSubstring const& aContextKey,
|
|||
if (aDiskStorage) {
|
||||
LOG((" dooming disk+memory storage of %s", aContextKey.BeginReading()));
|
||||
|
||||
// Just remove all entries, CacheFileIOManager will take care of the files.
|
||||
sGlobalEntryTables->Remove(aContextKey);
|
||||
sGlobalEntryTables->Remove(memoryStorageID);
|
||||
// Walk one by one and remove entries according their pin status
|
||||
CacheEntryTable *diskEntries, *memoryEntries;
|
||||
if (sGlobalEntryTables->Get(aContextKey, &diskEntries)) {
|
||||
sGlobalEntryTables->Get(memoryStorageID, &memoryEntries);
|
||||
|
||||
for (auto iter = diskEntries->Iter(); !iter.Done(); iter.Next()) {
|
||||
auto entry = iter.Data();
|
||||
if (entry->DeferOrBypassRemovalOnPinStatus(aPinned)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (memoryEntries) {
|
||||
RemoveExactEntry(memoryEntries, iter.Key(), entry, false);
|
||||
}
|
||||
iter.Remove();
|
||||
}
|
||||
}
|
||||
|
||||
if (aContext && !aContext->IsPrivate()) {
|
||||
LOG((" dooming disk entries"));
|
||||
CacheFileIOManager::EvictByContext(aContext);
|
||||
CacheFileIOManager::EvictByContext(aContext, aPinned);
|
||||
}
|
||||
} else {
|
||||
LOG((" dooming memory-only storage of %s", aContextKey.BeginReading()));
|
||||
|
||||
class MemoryEntriesRemoval {
|
||||
public:
|
||||
static PLDHashOperator EvictEntry(const nsACString& aKey,
|
||||
CacheEntry* aEntry,
|
||||
void* aClosure)
|
||||
{
|
||||
CacheEntryTable* entries = static_cast<CacheEntryTable*>(aClosure);
|
||||
nsCString key(aKey);
|
||||
RemoveExactEntry(entries, key, aEntry, false);
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
};
|
||||
|
||||
// Remove the memory entries table from the global tables.
|
||||
// Since we store memory entries also in the disk entries table
|
||||
// we need to remove the memory entries from the disk table one
|
||||
|
@ -1718,10 +1770,14 @@ CacheStorageService::DoomStorageEntries(nsCSubstring const& aContextKey,
|
|||
nsAutoPtr<CacheEntryTable> memoryEntries;
|
||||
sGlobalEntryTables->RemoveAndForget(memoryStorageID, memoryEntries);
|
||||
|
||||
CacheEntryTable* entries;
|
||||
sGlobalEntryTables->Get(aContextKey, &entries);
|
||||
if (memoryEntries && entries)
|
||||
memoryEntries->EnumerateRead(&MemoryEntriesRemoval::EvictEntry, entries);
|
||||
CacheEntryTable* diskEntries;
|
||||
sGlobalEntryTables->Get(aContextKey, &diskEntries);
|
||||
if (memoryEntries && diskEntries) {
|
||||
for (auto iter = memoryEntries->Iter(); !iter.Done(); iter.Next()) {
|
||||
auto entry = iter.Data();
|
||||
RemoveExactEntry(diskEntries, iter.Key(), entry, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// An artificial callback. This is a candidate for removal tho. In the new
|
||||
|
@ -1798,9 +1854,6 @@ CacheStorageService::CacheFileDoomed(nsILoadContextInfo* aLoadContextInfo,
|
|||
if (!entry->IsFileDoomed())
|
||||
return;
|
||||
|
||||
if (entry->IsReferenced())
|
||||
return;
|
||||
|
||||
// Need to remove under the lock to avoid possible race leading
|
||||
// to duplication of the entry per its key.
|
||||
RemoveExactEntry(entries, entryKey, entry, false);
|
||||
|
@ -2110,5 +2163,46 @@ CacheStorageService::CollectReports(nsIMemoryReporterCallback* aHandleReport,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
// nsICacheTesting
|
||||
|
||||
NS_IMETHODIMP
|
||||
CacheStorageService::IOThreadSuspender::Run()
|
||||
{
|
||||
MonitorAutoLock mon(mMon);
|
||||
mon.Wait();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
CacheStorageService::IOThreadSuspender::Notify()
|
||||
{
|
||||
MonitorAutoLock mon(mMon);
|
||||
mon.Notify();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
CacheStorageService::SuspendCacheIOThread(uint32_t aLevel)
|
||||
{
|
||||
RefPtr<CacheIOThread> thread = CacheFileIOManager::IOThread();
|
||||
if (!thread) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!mActiveIOSuspender);
|
||||
mActiveIOSuspender = new IOThreadSuspender();
|
||||
return thread->Dispatch(mActiveIOSuspender, aLevel);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
CacheStorageService::ResumeCacheIOThread()
|
||||
{
|
||||
MOZ_ASSERT(mActiveIOSuspender);
|
||||
|
||||
RefPtr<IOThreadSuspender> suspender;
|
||||
suspender.swap(mActiveIOSuspender);
|
||||
suspender->Notify();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -7,8 +7,9 @@
|
|||
|
||||
#include "nsICacheStorageService.h"
|
||||
#include "nsIMemoryReporter.h"
|
||||
|
||||
#include "nsITimer.h"
|
||||
#include "nsICacheTesting.h"
|
||||
|
||||
#include "nsClassHashtable.h"
|
||||
#include "nsDataHashtable.h"
|
||||
#include "nsString.h"
|
||||
|
@ -65,12 +66,14 @@ protected:
|
|||
class CacheStorageService final : public nsICacheStorageService
|
||||
, public nsIMemoryReporter
|
||||
, public nsITimerCallback
|
||||
, public nsICacheTesting
|
||||
{
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSICACHESTORAGESERVICE
|
||||
NS_DECL_NSIMEMORYREPORTER
|
||||
NS_DECL_NSITIMERCALLBACK
|
||||
NS_DECL_NSICACHETESTING
|
||||
|
||||
CacheStorageService();
|
||||
|
||||
|
@ -273,12 +276,14 @@ private:
|
|||
nsresult DoomStorageEntries(nsCSubstring const& aContextKey,
|
||||
nsILoadContextInfo* aContext,
|
||||
bool aDiskStorage,
|
||||
bool aPin,
|
||||
nsICacheEntryDoomCallback* aCallback);
|
||||
nsresult AddStorageEntry(nsCSubstring const& aContextKey,
|
||||
nsIURI* aURI,
|
||||
const nsACString & aIdExtension,
|
||||
bool aWriteToDisk,
|
||||
bool aSkipSizeCheck,
|
||||
bool aPin,
|
||||
bool aCreateIfNotExist,
|
||||
bool aReplace,
|
||||
CacheEntryHandle** aResult);
|
||||
|
@ -344,13 +349,7 @@ private:
|
|||
private:
|
||||
virtual ~PurgeFromMemoryRunnable() { }
|
||||
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
// TODO not all flags apply to both pools
|
||||
mService->Pool(true).PurgeAll(mWhat);
|
||||
mService->Pool(false).PurgeAll(mWhat);
|
||||
return NS_OK;
|
||||
}
|
||||
NS_IMETHOD Run() override;
|
||||
|
||||
RefPtr<CacheStorageService> mService;
|
||||
uint32_t mWhat;
|
||||
|
@ -361,6 +360,21 @@ private:
|
|||
// and also would be complicated to report since reporting happens on the main
|
||||
// thread but this table is manipulated on the management thread.
|
||||
nsDataHashtable<nsCStringHashKey, mozilla::TimeStamp> mPurgeTimeStamps;
|
||||
|
||||
// nsICacheTesting
|
||||
class IOThreadSuspender : public nsRunnable
|
||||
{
|
||||
public:
|
||||
IOThreadSuspender() : mMon("IOThreadSuspender") { }
|
||||
void Notify();
|
||||
private:
|
||||
virtual ~IOThreadSuspender() { }
|
||||
NS_IMETHOD Run() override;
|
||||
|
||||
Monitor mMon;
|
||||
};
|
||||
|
||||
RefPtr<IOThreadSuspender> mActiveIOSuspender;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
|
|
|
@ -11,6 +11,7 @@ XPIDL_SOURCES += [
|
|||
'nsICacheStorage.idl',
|
||||
'nsICacheStorageService.idl',
|
||||
'nsICacheStorageVisitor.idl',
|
||||
'nsICacheTesting.idl',
|
||||
]
|
||||
|
||||
XPIDL_MODULE = 'necko_cache2'
|
||||
|
|
|
@ -13,7 +13,7 @@ interface nsICacheStorageConsumptionObserver;
|
|||
/**
|
||||
* Provides access to particual cache storages of the network URI cache.
|
||||
*/
|
||||
[scriptable, uuid(9c9dc1d6-533e-4716-9ad8-11e08c3763b3)]
|
||||
[scriptable, uuid(ae29c44b-fbc3-4552-afaf-0a157ce771e7)]
|
||||
interface nsICacheStorageService : nsISupports
|
||||
{
|
||||
/**
|
||||
|
@ -42,6 +42,13 @@ interface nsICacheStorageService : nsISupports
|
|||
nsICacheStorage diskCacheStorage(in nsILoadContextInfo aLoadContextInfo,
|
||||
in bool aLookupAppCache);
|
||||
|
||||
/**
|
||||
* Get storage where entries will be written to disk and marked as pinned.
|
||||
* These pinned entries are immune to over limit eviction and call of clear()
|
||||
* on this service.
|
||||
*/
|
||||
nsICacheStorage pinningCacheStorage(in nsILoadContextInfo aLoadContextInfo);
|
||||
|
||||
/**
|
||||
* Get storage for a specified application cache obtained using some different
|
||||
* mechanism.
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
/**
|
||||
* This is an internal interface used only for testing purposes.
|
||||
*
|
||||
* THIS IS NOT AN API TO BE USED BY EXTENSIONS! ONLY USED BY MOZILLA TESTS.
|
||||
*/
|
||||
[scriptable, builtinclass, uuid(4e8ba935-92e1-4a74-944b-b1a2f02a7480)]
|
||||
interface nsICacheTesting : nsISupports
|
||||
{
|
||||
void suspendCacheIOThread(in uint32_t aLevel);
|
||||
void resumeCacheIOThread();
|
||||
};
|
|
@ -262,6 +262,7 @@ nsHttpChannel::nsHttpChannel()
|
|||
, mConcurentCacheAccess(0)
|
||||
, mIsPartialRequest(0)
|
||||
, mHasAutoRedirectVetoNotifier(0)
|
||||
, mPinCacheContent(0)
|
||||
, mIsPackagedAppResource(0)
|
||||
, mIsCorsPreflightDone(0)
|
||||
, mPushedStream(nullptr)
|
||||
|
@ -2984,6 +2985,10 @@ nsHttpChannel::OpenCacheEntry(bool isHttps)
|
|||
rv = cacheStorageService->MemoryCacheStorage(info, // ? choose app cache as well...
|
||||
getter_AddRefs(cacheStorage));
|
||||
}
|
||||
else if (mPinCacheContent) {
|
||||
rv = cacheStorageService->PinningCacheStorage(info,
|
||||
getter_AddRefs(cacheStorage));
|
||||
}
|
||||
else {
|
||||
rv = cacheStorageService->DiskCacheStorage(info,
|
||||
!mPostID && (mChooseApplicationCache || (mLoadFlags & LOAD_CHECK_OFFLINE_CACHE)),
|
||||
|
@ -6434,6 +6439,26 @@ nsHttpChannel::SetCacheOnlyMetadata(bool aOnlyMetadata)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsHttpChannel::GetPin(bool *aPin)
|
||||
{
|
||||
NS_ENSURE_ARG(aPin);
|
||||
*aPin = mPinCacheContent;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsHttpChannel::SetPin(bool aPin)
|
||||
{
|
||||
LOG(("nsHttpChannel::SetPin [this=%p pin=%d]\n",
|
||||
this, aPin));
|
||||
|
||||
ENSURE_CALLED_BEFORE_CONNECT();
|
||||
|
||||
mPinCacheContent = aPin;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsHttpChannel::nsIResumableChannel
|
||||
//-----------------------------------------------------------------------------
|
||||
|
|
|
@ -497,6 +497,9 @@ private:
|
|||
uint32_t mIsPartialRequest : 1;
|
||||
// true iff there is AutoRedirectVetoNotifier on the stack
|
||||
uint32_t mHasAutoRedirectVetoNotifier : 1;
|
||||
// consumers set this to true to use cache pinning, this has effect
|
||||
// only when the channel is in an app context (load context has an appid)
|
||||
uint32_t mPinCacheContent : 1;
|
||||
// Whether fetching the content is meant to be handled by the
|
||||
// packaged app service, which behaves like a caching layer.
|
||||
// Upon successfully fetching the package, the resource will be placed in
|
||||
|
|
|
@ -50,6 +50,7 @@ function getCacheStorage(where, lci, appcache)
|
|||
case "disk": return svc.diskCacheStorage(lci, false);
|
||||
case "memory": return svc.memoryCacheStorage(lci);
|
||||
case "appcache": return svc.appCacheStorage(lci, appcache);
|
||||
case "pin": return svc.pinningCacheStorage(lci);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -49,6 +49,8 @@ const DONTFILL = 1 << 13;
|
|||
const DONTSETVALID = 1 << 14;
|
||||
// Notify before checking the data, useful for proper callback ordering checks
|
||||
const NOTIFYBEFOREREAD = 1 << 15;
|
||||
// It's allowed to not get an existing entry (result of opening is undetermined)
|
||||
const MAYBE_NEW = 1 << 16;
|
||||
|
||||
var log_c2 = true;
|
||||
function LOG_C2(o, m)
|
||||
|
@ -150,6 +152,10 @@ OpenCallback.prototype =
|
|||
},
|
||||
onCacheEntryAvailable: function(entry, isnew, appCache, status)
|
||||
{
|
||||
if ((this.behavior & MAYBE_NEW) && isnew) {
|
||||
this.behavior |= NEW;
|
||||
}
|
||||
|
||||
LOG_C2(this, "onCacheEntryAvailable, " + this.behavior);
|
||||
do_check_true(!this.onAvailPassed);
|
||||
this.onAvailPassed = true;
|
||||
|
@ -207,9 +213,13 @@ OpenCallback.prototype =
|
|||
}
|
||||
do_execute_soon(function() { // emulate more network latency
|
||||
if (self.behavior & DOOMED) {
|
||||
LOG_C2(self, "checking doom state");
|
||||
try {
|
||||
var os = entry.openOutputStream(0);
|
||||
do_check_true(false);
|
||||
// Unfortunately, in the undetermined state we cannot even check whether the entry
|
||||
// is actually doomed or not.
|
||||
os.close();
|
||||
do_check_true(!!(self.behavior & MAYBE_NEW));
|
||||
} catch (ex) {
|
||||
do_check_true(true);
|
||||
}
|
||||
|
@ -258,9 +268,9 @@ OpenCallback.prototype =
|
|||
{
|
||||
LOG_C2(this, "selfCheck");
|
||||
|
||||
do_check_true(this.onCheckPassed);
|
||||
do_check_true(this.onCheckPassed || (this.behavior & MAYBE_NEW));
|
||||
do_check_true(this.onAvailPassed);
|
||||
do_check_true(this.onDataCheckPassed);
|
||||
do_check_true(this.onDataCheckPassed || (this.behavior & MAYBE_NEW));
|
||||
},
|
||||
throwAndNotify: function(entry)
|
||||
{
|
||||
|
@ -386,6 +396,10 @@ MultipleCallbacks.prototype =
|
|||
else
|
||||
this.goon();
|
||||
}
|
||||
},
|
||||
add: function()
|
||||
{
|
||||
++this.pending;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
function run_test()
|
||||
{
|
||||
do_get_profile();
|
||||
|
||||
// Open for write, write
|
||||
asyncOpenCacheEntry("http://a/", "pin", Ci.nsICacheStorage.OPEN_TRUNCATE, LoadContextInfo.default,
|
||||
new OpenCallback(NEW|WAITFORWRITE, "a1m", "a1d", function(entry) {
|
||||
// Open for read and check
|
||||
asyncOpenCacheEntry("http://a/", "disk", Ci.nsICacheStorage.OPEN_NORMALLY, LoadContextInfo.default,
|
||||
new OpenCallback(NORMAL, "a1m", "a1d", function(entry) {
|
||||
|
||||
// Now clear the whole cache
|
||||
get_cache_service().clear();
|
||||
|
||||
// The pinned entry should be intact
|
||||
asyncOpenCacheEntry("http://a/", "disk", Ci.nsICacheStorage.OPEN_NORMALLY, LoadContextInfo.default,
|
||||
new OpenCallback(NORMAL, "a1m", "a1d", function(entry) {
|
||||
finish_cache2_test();
|
||||
})
|
||||
);
|
||||
|
||||
})
|
||||
);
|
||||
})
|
||||
);
|
||||
|
||||
do_test_pending();
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
function run_test()
|
||||
{
|
||||
do_get_profile();
|
||||
var lci = LoadContextInfo.default;
|
||||
|
||||
// Open a pinned entry for write, write
|
||||
asyncOpenCacheEntry("http://a/", "pin", Ci.nsICacheStorage.OPEN_TRUNCATE, lci,
|
||||
new OpenCallback(NEW|WAITFORWRITE, "a1m", "a1d", function(entry) {
|
||||
|
||||
// Now clear the disk storage, that should leave the pinned entry in the cache
|
||||
var diskStorage = getCacheStorage("disk", lci);
|
||||
diskStorage.asyncEvictStorage(null);
|
||||
|
||||
// Open for read and check, it should still be there
|
||||
asyncOpenCacheEntry("http://a/", "disk", Ci.nsICacheStorage.OPEN_NORMALLY, lci,
|
||||
new OpenCallback(NORMAL, "a1m", "a1d", function(entry) {
|
||||
|
||||
// Now clear the pinning storage, entry should be gone
|
||||
var pinningStorage = getCacheStorage("pin", lci);
|
||||
pinningStorage.asyncEvictStorage(null);
|
||||
|
||||
asyncOpenCacheEntry("http://a/", "disk", Ci.nsICacheStorage.OPEN_NORMALLY, lci,
|
||||
new OpenCallback(NEW, "", "", function(entry) {
|
||||
finish_cache2_test();
|
||||
})
|
||||
);
|
||||
|
||||
})
|
||||
);
|
||||
})
|
||||
);
|
||||
|
||||
do_test_pending();
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
/*
|
||||
|
||||
This is a complex test checking the internal "deferred doom" functionality in both CacheEntry and CacheFileHandle.
|
||||
|
||||
- We create a batch of 10 non-pinned and 10 pinned entries, write something to them.
|
||||
- Then we purge them from memory, so they have to reload from disk.
|
||||
- After that the IO thread is suspended not to process events on the READ (3) level. This forces opening operation and eviction
|
||||
sync operations happen before we know actual pinning status of already cached entries.
|
||||
- We async-open the same batch of the 10+10 entries again, all should open as existing with the expected, previously stored
|
||||
content
|
||||
- After all these entries are made to open, we clear the cache. This does some synchronous operations on the entries
|
||||
being open and also on the handles being in an already open state (but before the entry metadata has started to be read.)
|
||||
Expected is to leave the pinned entries only.
|
||||
- Now, we resume the IO thread, so it start reading. One could say this is a hack, but this can very well happen in reality
|
||||
on slow disk or when a large number of entries is about to be open at once. Suspending the IO thread is just doing this
|
||||
simulation is a fully deterministic way and actually very easily and elegantly.
|
||||
- After the resume we want to open all those 10+10 entries once again (no purgin involved this time.). It is expected
|
||||
to open all the pinning entries intact and loose all the non-pinned entries (get them as new and empty again.)
|
||||
|
||||
*/
|
||||
|
||||
const kENTRYCOUNT = 10;
|
||||
|
||||
function log_(msg) { if (true) dump(">>>>>>>>>>>>> " + msg + "\n"); }
|
||||
|
||||
function run_test()
|
||||
{
|
||||
do_get_profile();
|
||||
var lci = LoadContextInfo.default;
|
||||
var testingInterface = get_cache_service().QueryInterface(Ci.nsICacheTesting);
|
||||
do_check_true(testingInterface);
|
||||
|
||||
var mc = new MultipleCallbacks(1, function() {
|
||||
// (2)
|
||||
|
||||
mc = new MultipleCallbacks(1, finish_cache2_test);
|
||||
// Release all references to cache entries so that they can be purged
|
||||
// Calling gc() four times is needed to force it to actually release
|
||||
// entries that are obviously unreferenced. Yeah, I know, this is wacky...
|
||||
gc();
|
||||
gc();
|
||||
do_execute_soon(() => {
|
||||
gc();
|
||||
gc();
|
||||
log_("purging");
|
||||
|
||||
// Invokes cacheservice:purge-memory-pools when done.
|
||||
get_cache_service().purgeFromMemory(Ci.nsICacheStorageService.PURGE_EVERYTHING); // goes to (3)
|
||||
});
|
||||
}, true);
|
||||
|
||||
// (1), here we start
|
||||
|
||||
var i;
|
||||
for (i = 0; i < kENTRYCOUNT; ++i) {
|
||||
log_("first set of opens");
|
||||
|
||||
// Callbacks 1-20
|
||||
mc.add();
|
||||
asyncOpenCacheEntry("http://pinned" + i + "/", "pin", Ci.nsICacheStorage.OPEN_TRUNCATE, lci,
|
||||
new OpenCallback(NEW|WAITFORWRITE, "m" + i, "p" + i, function(entry) { mc.fired(); }));
|
||||
|
||||
mc.add();
|
||||
asyncOpenCacheEntry("http://common" + i + "/", "disk", Ci.nsICacheStorage.OPEN_TRUNCATE, lci,
|
||||
new OpenCallback(NEW|WAITFORWRITE, "m" + i, "d" + i, function(entry) { mc.fired(); }));
|
||||
}
|
||||
|
||||
mc.fired(); // Goes to (2)
|
||||
|
||||
var os = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
|
||||
os.addObserver({
|
||||
observe: function(subject, topic, data)
|
||||
{
|
||||
// (3)
|
||||
|
||||
log_("after purge, second set of opens");
|
||||
// Prevent the I/O thread from reading the data. We first want to schedule clear of the cache.
|
||||
// This deterministically emulates a slow hard drive.
|
||||
testingInterface.suspendCacheIOThread(3);
|
||||
|
||||
// All entries should load
|
||||
// Callbacks 21-40
|
||||
for (i = 0; i < kENTRYCOUNT; ++i) {
|
||||
mc.add();
|
||||
asyncOpenCacheEntry("http://pinned" + i + "/", "disk", Ci.nsICacheStorage.OPEN_NORMALLY, lci,
|
||||
new OpenCallback(NORMAL, "m" + i, "p" + i, function(entry) { mc.fired(); }));
|
||||
|
||||
// Unfortunately we cannot ensure that entries existing in the cache will be delivered to the consumer
|
||||
// when soon after are evicted by some cache API call. It's better to not ensure getting an entry
|
||||
// than allowing to get an entry that was just evicted from the cache. Entries may be delievered
|
||||
// as new, but are already doomed. Output stream cannot be openned, or the file handle is already
|
||||
// writing to a doomed file.
|
||||
//
|
||||
// The API now just ensures that entries removed by any of the cache eviction APIs are never more
|
||||
// available to consumers.
|
||||
mc.add();
|
||||
asyncOpenCacheEntry("http://common" + i + "/", "disk", Ci.nsICacheStorage.OPEN_NORMALLY, lci,
|
||||
new OpenCallback(MAYBE_NEW|DOOMED, "m" + i, "d" + i, function(entry) { mc.fired(); }));
|
||||
}
|
||||
|
||||
log_("clearing");
|
||||
// Now clear everything except pinned, all entries are in state of reading
|
||||
get_cache_service().clear();
|
||||
log_("cleared");
|
||||
|
||||
// Resume reading the cache data, only now the pinning status on entries will be discovered,
|
||||
// the deferred dooming code will trigger.
|
||||
testingInterface.resumeCacheIOThread();
|
||||
|
||||
log_("third set of opens");
|
||||
// Now open again. Pinned entries should be there, disk entries should be the renewed entries.
|
||||
// Callbacks 41-60
|
||||
for (i = 0; i < kENTRYCOUNT; ++i) {
|
||||
mc.add();
|
||||
asyncOpenCacheEntry("http://pinned" + i + "/", "disk", Ci.nsICacheStorage.OPEN_NORMALLY, lci,
|
||||
new OpenCallback(NORMAL, "m" + i, "p" + i, function(entry) { mc.fired(); }));
|
||||
|
||||
mc.add();
|
||||
asyncOpenCacheEntry("http://common" + i + "/", "disk", Ci.nsICacheStorage.OPEN_NORMALLY, lci,
|
||||
new OpenCallback(NEW, "m2" + i, "d2" + i, function(entry) { mc.fired(); }));
|
||||
}
|
||||
|
||||
mc.fired(); // Finishes this test
|
||||
}
|
||||
}, "cacheservice:purge-memory-pools", false);
|
||||
|
||||
|
||||
do_test_pending();
|
||||
}
|
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
|
||||
This test exercises the CacheFileContextEvictor::WasEvicted API and code using it.
|
||||
|
||||
- We store 10+10 (pinned and non-pinned) entries to the cache, wait for them being written.
|
||||
- Then we purge the memory pools.
|
||||
- Now the IO thread is suspended on the EVICT (8) level to prevent actual deletion of the files.
|
||||
- Index is disabled.
|
||||
- We do clear() of the cache, this creates the "ce_*" file and posts to the EVICT level
|
||||
the eviction loop mechanics.
|
||||
- We open again those 10+10 entries previously stored.
|
||||
- IO is resumed
|
||||
- We expect to get all the pinned and
|
||||
loose all the non-pinned (common) entries.
|
||||
|
||||
*/
|
||||
|
||||
const kENTRYCOUNT = 10;
|
||||
|
||||
function log_(msg) { if (true) dump(">>>>>>>>>>>>> " + msg + "\n"); }
|
||||
|
||||
function run_test()
|
||||
{
|
||||
do_get_profile();
|
||||
var lci = LoadContextInfo.default;
|
||||
var testingInterface = get_cache_service().QueryInterface(Ci.nsICacheTesting);
|
||||
do_check_true(testingInterface);
|
||||
|
||||
var mc = new MultipleCallbacks(1, function() {
|
||||
// (2)
|
||||
|
||||
mc = new MultipleCallbacks(1, finish_cache2_test);
|
||||
// Release all references to cache entries so that they can be purged
|
||||
// Calling gc() four times is needed to force it to actually release
|
||||
// entries that are obviously unreferenced. Yeah, I know, this is wacky...
|
||||
gc();
|
||||
gc();
|
||||
do_execute_soon(() => {
|
||||
gc();
|
||||
gc();
|
||||
log_("purging");
|
||||
|
||||
// Invokes cacheservice:purge-memory-pools when done.
|
||||
get_cache_service().purgeFromMemory(Ci.nsICacheStorageService.PURGE_EVERYTHING); // goes to (3)
|
||||
});
|
||||
}, true);
|
||||
|
||||
// (1), here we start
|
||||
|
||||
var i;
|
||||
for (i = 0; i < kENTRYCOUNT; ++i) {
|
||||
log_("first set of opens");
|
||||
|
||||
// Callbacks 1-20
|
||||
mc.add();
|
||||
asyncOpenCacheEntry("http://pinned" + i + "/", "pin", Ci.nsICacheStorage.OPEN_TRUNCATE, lci,
|
||||
new OpenCallback(NEW|WAITFORWRITE, "m" + i, "p" + i, function(entry) { mc.fired(); }));
|
||||
|
||||
mc.add();
|
||||
asyncOpenCacheEntry("http://common" + i + "/", "disk", Ci.nsICacheStorage.OPEN_TRUNCATE, lci,
|
||||
new OpenCallback(NEW|WAITFORWRITE, "m" + i, "d" + i, function(entry) { mc.fired(); }));
|
||||
}
|
||||
|
||||
mc.fired(); // Goes to (2)
|
||||
|
||||
var os = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
|
||||
os.addObserver({
|
||||
observe: function(subject, topic, data)
|
||||
{
|
||||
// (3)
|
||||
|
||||
log_("after purge");
|
||||
// Prevent the I/O thread from evicting physically the data. We first want to re-open the entries.
|
||||
// This deterministically emulates a slow hard drive.
|
||||
testingInterface.suspendCacheIOThread(8);
|
||||
|
||||
log_("clearing");
|
||||
// Now clear everything except pinned. Stores the "ce_*" file and schedules background eviction.
|
||||
get_cache_service().clear();
|
||||
log_("cleared");
|
||||
|
||||
log_("second set of opens");
|
||||
// Now open again. Pinned entries should be there, disk entries should be the renewed entries.
|
||||
// Callbacks 21-40
|
||||
for (i = 0; i < kENTRYCOUNT; ++i) {
|
||||
mc.add();
|
||||
asyncOpenCacheEntry("http://pinned" + i + "/", "disk", Ci.nsICacheStorage.OPEN_NORMALLY, lci,
|
||||
new OpenCallback(NORMAL, "m" + i, "p" + i, function(entry) { mc.fired(); }));
|
||||
|
||||
mc.add();
|
||||
asyncOpenCacheEntry("http://common" + i + "/", "disk", Ci.nsICacheStorage.OPEN_NORMALLY, lci,
|
||||
new OpenCallback(NEW, "m2" + i, "d2" + i, function(entry) { mc.fired(); }));
|
||||
}
|
||||
|
||||
// Resume IO, this will just pop-off the CacheFileContextEvictor::EvictEntries() because of
|
||||
// an early check on CacheIOThread::YieldAndRerun() in that method.
|
||||
// CacheFileIOManager::OpenFileInternal should now run and CacheFileContextEvictor::WasEvicted
|
||||
// should be checked on.
|
||||
testingInterface.resumeCacheIOThread();
|
||||
|
||||
mc.fired(); // Finishes this test
|
||||
}
|
||||
}, "cacheservice:purge-memory-pools", false);
|
||||
|
||||
|
||||
do_test_pending();
|
||||
}
|
|
@ -99,7 +99,8 @@ function run_test() {
|
|||
|
||||
function doneFirstLoad(req, buffer, expected) {
|
||||
// Load it again, make sure it hits the cache
|
||||
var chan = makeChan(URL, 0, false);
|
||||
var nc = req.notificationCallbacks.getInterface(Ci.nsILoadContext);
|
||||
var chan = makeChan(URL, nc.appId, nc.isInBrowserElement);
|
||||
chan.asyncOpen(new ChannelListener(doneSecondLoad, expected), null);
|
||||
}
|
||||
|
||||
|
|
|
@ -70,8 +70,12 @@ skip-if = true
|
|||
[test_cache2-28a-OPEN_SECRETLY.js]
|
||||
# This test will be fixed in bug 1067931
|
||||
skip-if = true
|
||||
[test_cache2-28-concurrent_read_resumable_entry_size_zero.js]
|
||||
[test_cache2-29-concurrent_read_non-resumable_entry_size_zero.js]
|
||||
[test_cache2-29a-concurrent_read_resumable_entry_size_zero.js]
|
||||
[test_cache2-29b-concurrent_read_non-resumable_entry_size_zero.js]
|
||||
[test_cache2-30a-entry-pinning.js]
|
||||
[test_cache2-30b-pinning-storage-clear.js]
|
||||
[test_cache2-30c-pinning-deferred-doom.js]
|
||||
[test_cache2-30d-pinning-WasEvicted-API.js]
|
||||
[test_partial_response_entry_size_smart_shrink.js]
|
||||
[test_304_responses.js]
|
||||
[test_421.js]
|
||||
|
|
Загрузка…
Ссылка в новой задаче